diff --git a/.gitignore b/.gitignore index b5b56cf..68aad34 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ node_modules -public-min +build *.log .DS_Store diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..9b57ab3 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,43 @@ +module.exports = function(grunt) { + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + + // =watch + watch: { + react: { + files: ['app/**/*.js'], + tasks: ['react'], + options: { interrupt: true} + }, + browserify: { + files: ['build/**/*.js'], + tasks: ['browserify'], + options: { interrupt: true} + } + }, + + react: { + files: { + expand: true, + cwd: './app', + src: ['**/*.js'], + dest: './build', + ext: '.js' + } + }, + + browserify: { + all: { + src: 'build/main.js', + dest: 'public/js/cssarrowplease.js' + } + } + }); + + grunt.registerTask('build', ['react', 'browserify']); + grunt.registerTask('default', 'build'); + + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-browserify'); + grunt.loadNpmTasks('grunt-react'); +}; diff --git a/Makefile b/Makefile deleted file mode 100644 index 5f4d512..0000000 --- a/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -deps := $(shell find public/ -type f -name "*.*") - -public-min: public/index.html $(deps) - node_modules/assetgraph-builder/bin/buildProduction \ - --root public \ - --outroot public-min \ - $< - -PHONY: clean - -clean: - rm -rf public-min diff --git a/app/app.js b/app/app.js new file mode 100644 index 0000000..0cce538 --- /dev/null +++ b/app/app.js @@ -0,0 +1,18 @@ +/** @jsx React.DOM */ + +var Page = require('./components/page'); + +var App = function (props) { + this.container = props.container; +}; + +App.prototype = { + run: function () { + React.renderComponent( + , + this.container + ); + } +}; + +module.exports = App; diff --git a/app/arrow_generator.js b/app/arrow_generator.js new file mode 100644 index 0000000..9e5cec4 --- /dev/null +++ b/app/arrow_generator.js @@ -0,0 +1,252 @@ +/** +@class Arrow +@constructor +**/ +var Arrow = function () { + this._createAttrs(); +}; + +Arrow.prototype = { + /** + returns the opposite of the position + so 'top' becomes 'bottom' and 'left' becomes 'right' + @method invertedPosition + @returns {String} + **/ + invertedPosition: function () { + var pos = this.get('position'); + + if ( pos === 'top' ) return 'bottom'; + else if ( pos === 'bottom') return 'top'; + else if ( pos === 'left' ) return 'right'; + else if ( pos === 'right' ) return 'left'; + }, + + /** + returns an rgb color from an hex color + + @method hexToRGB + @returns {Array} + **/ + hexToRGB: function (h) { + if ( typeof h !== 'string' || !h.match(/^#([0-9A-F]{3}$)|([0-9A-F]{6}$)/i) ) return [0, 0, 0]; + else if ( h.match(/^(#[0-9a-f]{3})$/i) ) h = '#' + h[1] + h[1] + h[2] + h[2] + h[3] + h[3]; + var rgb = [], + i = 1; + + for(; i < 6; i+=2) { + rgb.push(parseInt(h.substring(i, i + 2), 16)); + } + return rgb; + }, + + /** + generates the base css + + @method _baseCSS + @returns {String} css + @protected + **/ + _baseCSS: function () { + var pos = this.get('position'), + iPos = this.invertedPosition(), + color = this.get('color'), + borderSize = this.get('borderSize'), + borderColor = this.get('borderColor'), + hasBorder = borderSize > 0, + css = '.with-arrow {\n'; + + css += '\tposition: relative;\n'; + css += '\tbackground: ' + color + ';\n'; + + if (hasBorder) css += '\tborder: ' + borderSize + 'px solid ' + borderColor + ';\n'; + + css += '}\n'; + css += '.with-arrow:after'; + + if (hasBorder) css += ', .with-arrow:before {\n'; + else css += ' {\n'; + + css += '\t' + iPos +': 100%;\n'; + + if (pos === 'top' || pos === 'bottom') { + css += '\tleft: 50%;\n'; + } + else { + css += '\ttop: 50%;\n'; + } + + css += '\tborder: solid transparent;\n'; + css += '\tcontent: " ";\n'; + css += '\theight: 0;\n'; + css += '\twidth: 0;\n'; + css += '\tposition: absolute;\n'; + css += '\tpointer-events: none;\n'; + + if(hasBorder) css += '}\n'; + + return css; + }, + + /** + @method _arrowCSS + @description generates arrow css + @param {String} color the color of the arrow + @param {Integer} size the size of the arrow + @param {String} layer :after or :before (defaults to :after) + @returns {String} css + @protected + **/ + _arrowCSS: function (color, size, layer) { + var pos = this.get('position'), + iPos = this.invertedPosition(), + rgbColor = this.hexToRGB(color), + borderSize = this.get('borderSize'), + css = ""; + + layer = layer || 'after'; + + if(borderSize > 0) css += '.with-arrow:' + layer + ' {\n'; + + css += '\tborder-color: rgba(' + rgbColor.join(', ') + ', 0);\n'; + css += '\tborder-' + iPos + '-color: ' + color + ';\n'; + css += '\tborder-width: ' + size + 'px;\n'; + + if (pos === 'top' || pos === 'bottom') { + css += '\tmargin-left: -' + size + 'px;\n'; + } + else { + css += '\tmargin-top: -' + size + 'px;\n'; + } + + css += '}'; + + return css; + }, + + /** + @method _baseArrowCSS + @description generates the base arrow + @returns {String} css + @protected + **/ + _baseArrowCSS: function () { + return this._arrowCSS( + this.get('color'), + this.get('size'), + 'after' + ); + }, + + /** + @method _arrowBorderCSS + @description generates the border arrow + @returns {String} css + @protected + **/ + _arrowBorderCSS: function () { + var css = '', + borderSize = this.get('borderSize'); + + if (borderSize > 0) { + css = this._arrowCSS( + this.get('borderColor'), + this.get('size') + Math.round(borderSize * 1.41421356), // cos(PI/4) * 2 + 'before' + ); + } + + return css; + }, + + /** + @method toCSS + @description returns a CSS representation of the arrow + @returns {String} css + **/ + toCSS: function () { + + var css = [ + this._baseCSS(), + this._baseArrowCSS(), + this._arrowBorderCSS() + ]; + + return css.join(css[2] ? '\n':''); + }, + + /** + @method _createAttrs + @description creates attributes from the ATTR constant + @protected + **/ + _createAttrs: function () { + var ATTRS = Arrow.ATTRS, + attributes = {}; + + for (var attr in ATTRS) { + if (ATTRS.hasOwnProperty(attr)) { + attributes[attr] = ATTRS[attr]; + } + }; + + this._attributes = attributes; + }, + + /** + @method getAttrs + @description returns all the attributes + @returns {Object} all the model attributes + **/ + getAttrs: function () { + return this._attributes; + }, + + /** + @method get + @description returns the provided attribute + @param {String} attr the attribute to return + @returns {?} the attribute + **/ + get: function (attr) { + return this._attributes[attr]; + }, + + /** + @method set + @description updates the provided attribute + @param {String} attr the attribute to update + @param {?} val the value to update with + **/ + set: function (attr, val) { + if (!(attr in this._attributes)) return; + + this._attributes[attr] = val; + }, + + setAttrs: function (attrs) { + for (var attr in attrs) { + if (attrs.hasOwnProperty(attr)) { + this.set(attr, attrs[attr]); + } + } + } + +}; + +Arrow.ATTRS = { + position: 'top', + size: 16, + color: '#88b7d5', + borderSize: 3, + borderColor:'#c2e1f5' +}; + +// TODO: refactor!! +var arrowGenerator = function (config) { + var arrow = new Arrow(); + arrow.setAttrs(config); + return arrow.toCSS(); +}; + +module.exports = arrowGenerator; diff --git a/app/components/arrow_box.js b/app/components/arrow_box.js new file mode 100644 index 0000000..976ef5f --- /dev/null +++ b/app/components/arrow_box.js @@ -0,0 +1,15 @@ +/** @jsx React.DOM */ + +var ArrowBox = React.createClass({ + render: function () { + return ( +
+ Generate tooltips with a
+ CSS triangle in any direction.
+ Configure below +
+ ); + } +}); + +module.exports = ArrowBox; diff --git a/app/components/config.js b/app/components/config.js new file mode 100644 index 0000000..dd70708 --- /dev/null +++ b/app/components/config.js @@ -0,0 +1,43 @@ +/** @jsx React.DOM */ + +var ConfigLabels = require('./config_labels'), + ConfigForm = require('./config_form'), + GeneratedCode = require('./generated_code'), + arrowGenerator = require('../arrow_generator'); + +var Config = React.createClass({ + + /** + @method getInitialState + @return {Object} + **/ + getInitialState: function () { + return { + generatedCode: arrowGenerator(this.props.config) + }; + }, + + /** + @method render + @return {ReactComponent} + **/ + render: function() { + return ( +
+ + + +
+ ); + }, + + /** + @method handleConfigChange + @param {Object} config + **/ + handleConfigChange: function (config) { + this.setState({ generatedCode: arrowGenerator(config) }); + } +}); + +module.exports = Config; diff --git a/app/components/config_form.js b/app/components/config_form.js new file mode 100644 index 0000000..fcf0c68 --- /dev/null +++ b/app/components/config_form.js @@ -0,0 +1,78 @@ +/** @jsx React.DOM */ + +var ConfigForm = React.createClass({ + + propTypes: { + onConfigChange: React.PropTypes.func.isRequired + }, + + /** + @method getInitialState + @return {Object} + **/ + getInitialState: function () { + return this.props.config; + }, + + /** + @method render + @return {ReactComponent} + **/ + render: function () { + var change = function (propName) { + return this.handleChange.bind(this, propName); + }.bind(this); + + var isPositionChecked = function (pos) { + return this.state.position === pos; + }.bind(this); + + return ( +
+
    +
  1. + +
  2. +
  3. + +
  4. +
  5. + +
  6. +
  7. + +
  8. +
  9. + +
  10. +
  11. + +
  12. +
  13. + +
  14. +
  15. + +
  16. +
+
+ ); + }, + + handleChange: function (propName, ev) { + var nextState = {}, + val = ev.target.value; + + if (val && propName.indexOf('ize') !== -1) { + val = parseInt(val, 10); + } + + nextState[propName] = val; + + this.setState(nextState, function () { + this.props.onConfigChange(this.state); + }.bind(this)); + } +}); + +module.exports = ConfigForm; diff --git a/app/components/config_labels.js b/app/components/config_labels.js new file mode 100644 index 0000000..b3aa2df --- /dev/null +++ b/app/components/config_labels.js @@ -0,0 +1,21 @@ +/** @jsx React.DOM */ + +var ConfigLabels = React.createClass({ + render: function() { + return ( + + ); + } +}); +module.exports = ConfigLabels; diff --git a/app/components/generated_code.js b/app/components/generated_code.js new file mode 100644 index 0000000..129b190 --- /dev/null +++ b/app/components/generated_code.js @@ -0,0 +1,29 @@ +/** @jsx React.DOM */ + +var GeneratedCode = React.createClass({ + + propTypes: { + code: React.PropTypes.string.isRequired + }, + + render: function() { + this.updateStyles(); + + return ( +
+        {this.props.code}
+      
+ ); + }, + + componentWillMount: function () { + this.styleContainer = document.createElement('style'); + document.getElementsByTagName('body')[0].appendChild(this.styleContainer); + }, + + updateStyles: function () { + this.styleContainer.innerText = this.props.code; + } + +}); +module.exports = GeneratedCode; diff --git a/app/components/header.js b/app/components/header.js new file mode 100644 index 0000000..60b887e --- /dev/null +++ b/app/components/header.js @@ -0,0 +1,27 @@ +/** @jsx React.DOM */ + +var ArrowBox = require('./arrow_box'); + +var Header = React.createClass({ + render: function() { + return ( +
+

+ I would love a + CSS Arrow Please + By Simon Højberg +

+ + + +

+
+
Config
Code
+
+

+
+ ) + } +}); + +module.exports = Header; diff --git a/app/components/page.js b/app/components/page.js new file mode 100644 index 0000000..f9e8c5c --- /dev/null +++ b/app/components/page.js @@ -0,0 +1,35 @@ +/** @jsx React.DOM */ + +var Header = require('./header'), + Config = require('./config'); + +var Page = React.createClass({ + render: function () { + + // default arrow + var config = { + position: 'top', + size: 16, + color: '#FFFFFF', + borderSize: 3, + borderColor: '#42474E' + }; + + return ( +
+
+
+ + + +
+
+ ); + } +}); + +module.exports = Page; diff --git a/app/main.js b/app/main.js new file mode 100644 index 0000000..bb6d84b --- /dev/null +++ b/app/main.js @@ -0,0 +1,8 @@ +/** @jsx React.DOM */ + +var Page = require('./components/page'); + +React.renderComponent( + , + document.querySelector('.app') +); diff --git a/app/models/arrow.js b/app/models/arrow.js new file mode 100644 index 0000000..1d02753 --- /dev/null +++ b/app/models/arrow.js @@ -0,0 +1,267 @@ +/** +@class Arrow +@constructor +**/ +var Arrow = function () { + // jquerify 'this' + this._$self = $(this); + this._createAttrs(); +}; + +Arrow.prototype = { + /** + returns the opposite of the position + so 'top' becomes 'bottom' and 'left' becomes 'right' + @method invertedPosition + @returns {String} + **/ + invertedPosition: function () { + var pos = this.get('position'); + + if ( pos === 'top' ) return 'bottom'; + else if ( pos === 'bottom') return 'top'; + else if ( pos === 'left' ) return 'right'; + else if ( pos === 'right' ) return 'left'; + }, + + /** + returns an rgb color from an hex color + + @method hexToRGB + @returns {Array} + **/ + hexToRGB: function (h) { + if ( typeof h !== 'string' || !h.match(/^#([0-9A-F]{3}$)|([0-9A-F]{6}$)/i) ) return [0, 0, 0]; + else if ( h.match(/^(#[0-9a-f]{3})$/i) ) h = '#' + h[1] + h[1] + h[2] + h[2] + h[3] + h[3]; + var rgb = [], + i = 1; + + for(; i < 6; i+=2) { + rgb.push(parseInt(h.substring(i, i + 2), 16)); + } + return rgb; + }, + + /** + generates the base css + + @method _baseCSS + @returns {String} css + @protected + **/ + _baseCSS: function () { + var pos = this.get('position'), + iPos = this.invertedPosition(), + color = this.get('color'), + borderWidth = this.get('borderWidth'), + borderColor = this.get('borderColor'), + hasBorder = borderWidth > 0, + css = '.arrow_box {\n'; + + css += '\tposition: relative;\n'; + css += '\tbackground: ' + color + ';\n'; + + if (hasBorder) css += '\tborder: ' + borderWidth + 'px solid ' + borderColor + ';\n'; + + css += '}\n'; + css += '.arrow_box:after'; + + if (hasBorder) css += ', .arrow_box:before {\n'; + else css += ' {\n'; + + css += '\t' + iPos +': 100%;\n'; + + if (pos === 'top' || pos === 'bottom') { + css += '\tleft: 50%;\n'; + } + else { + css += '\ttop: 50%;\n'; + } + + css += '\tborder: solid transparent;\n'; + css += '\tcontent: " ";\n'; + css += '\theight: 0;\n'; + css += '\twidth: 0;\n'; + css += '\tposition: absolute;\n'; + css += '\tpointer-events: none;\n'; + + if(hasBorder) css += '}\n'; + + return css; + }, + + /** + @method _arrowCSS + @description generates arrow css + @param {String} color the color of the arrow + @param {Integer} size the size of the arrow + @param {String} layer :after or :before (defaults to :after) + @returns {String} css + @protected + **/ + _arrowCSS: function (color, size, layer) { + var pos = this.get('position'), + iPos = this.invertedPosition(), + rgbColor = this.hexToRGB(color), + borderWidth = this.get('borderWidth'), + css = ""; + + layer = layer || 'after'; + + if(borderWidth > 0) css += '.arrow_box:' + layer + ' {\n'; + + css += '\tborder-color: rgba(' + rgbColor.join(', ') + ', 0);\n'; + css += '\tborder-' + iPos + '-color: ' + color + ';\n'; + css += '\tborder-width: ' + size + 'px;\n'; + + if (pos === 'top' || pos === 'bottom') { + css += '\tmargin-left: -' + size + 'px;\n'; + } + else { + css += '\tmargin-top: -' + size + 'px;\n'; + } + + css += '}'; + + return css; + }, + + /** + @method _baseArrowCSS + @description generates the base arrow + @returns {String} css + @protected + **/ + _baseArrowCSS: function () { + return this._arrowCSS( + this.get('color'), + this.get('size'), + 'after' + ); + }, + + /** + @method _arrowBorderCSS + @description generates the border arrow + @returns {String} css + @protected + **/ + _arrowBorderCSS: function () { + var css = '', + borderWidth = this.get('borderWidth'); + + if (borderWidth > 0) { + css = this._arrowCSS( + this.get('borderColor'), + this.get('size') + Math.round(borderWidth * 1.41421356), // cos(PI/4) * 2 + 'before' + ); + } + + return css; + }, + + /** + @method toCSS + @description returns a CSS representation of the arrow + @returns {String} css + **/ + toCSS: function () { + + var css = [ + this._baseCSS(), + this._baseArrowCSS(), + this._arrowBorderCSS() + ]; + + return css.join(css[2] ? '\n':''); + }, + + /** + @method _createAttrs + @description creates attributes from the ATTR constant + @protected + **/ + _createAttrs: function () { + var ATTRS = Arrow.ATTRS, + attributes = {}; + + $.each(ATTRS, function (attr, value) { + attributes[attr] = value; + }); + + this._attributes = attributes; + }, + + /** + @method getAttrs + @description returns all the attributes + @returns {Object} all the model attributes + **/ + getAttrs: function () { + return this._attributes; + }, + + /** + @method get + @description returns the provided attribute + @param {String} attr the attribute to return + @returns {?} the attribute + **/ + get: function (attr) { + return this._attributes[attr]; + }, + + /** + @method set + @description updates the provided attribute + @param {String} attr the attribute to update + @param {?} val the value to update with + **/ + set: function (attr, val) { + if (!(attr in this._attributes)) return; + + this._attributes[attr] = val; + this.fire('change'); + }, + + /** + @method on + @description adds event listeners + @note uses jQuery custom events under the hood + @param {String} evType the event type + @param {Function} callback the event handler + @param {Object} context the 'this' for the callback + **/ + on: function (evType, callback, context) { + var $self = this._$self; + + $self.on( + evType, + $.proxy(callback, context || this) + ); + }, + + /** + @method fire + @description trigger event + @note uses jQuery custom events under the hood + @param {String} evType the event type + **/ + fire: function (evType) { + var $self = this._$self; + + $self.trigger(evType); + } + +}; + +Arrow.ATTRS = { + position: 'top', + size: 30, + color: '#88b7d5', + borderWidth: 4, + borderColor: '#c2e1f5' +}; + +module.exports = Arrow; diff --git a/bin/server b/bin/server index f1ef1b5..ee3614f 100644 --- a/bin/server +++ b/bin/server @@ -1,18 +1,11 @@ #!/usr/bin/env node -var connect = require('connect'), +var connect = require('connect'), http = require('http'), - port = process.env.PORT || 3000, + port = process.env.PORT || 1337, static; -if (process.argv.indexOf('--development') !== -1) { - console.log('CSS Arrow Please in development on http://localhost:' + port); - static = connect.static('public'); -} -else { - static = connect.static('public-min', { - maxAge: 365 * 24 * 60 * 60 * 1000 - }); -} +console.log('CSS Arrow Please in development on http://localhost:' + port); +static = connect.static('public'); http.createServer( connect().use( static ) ).listen( port ); diff --git a/package.json b/package.json index a47cfab..9579649 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,7 @@ "type": "git", "url": "git://github.com/hojberg/cssarrowplease.git" }, - "keywords": [ - "nodeapps", - "helloworld" - ], + "keywords": [ "css", "arrow", "tooltip" ], "homepage": "http://cssarrowplease.com/", "scripts": { "start": "node ./bin/server" @@ -23,10 +20,15 @@ "connect": "~2.0.3" }, "devDependencies": { - "assetgraph-builder": ">=0.2.37" + "grunt": "latest", + "grunt-contrib-watch": "~0.6.0", + "react-tools": "latest", + "grunt-react": "latest", + "grunt-browserify": "latest" }, "engines": { - "node": ">=0.6" + "node": "0.8.x", + "npm": "1.1.x" }, "licenses": [ { diff --git a/public/css/app.css b/public/css/app.css index c137a21..f521927 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -1,61 +1,204 @@ -/* =BASE -====================================================== */ -html, body { background: url(../img/noisebg.png); } -body { font-size: 18px; font-family: 'Terminal Dosis', sans-serif; padding: 0; margin: 0; color: #fff; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4); } -a { color: #fff; color: rgba(255, 255, 255, 0.7); } -ul, ol, form { margin: 0; padding: 0; } -ul, ol { list-style-type: none; } -h1 { margin: 0; padding: 0; } -h2 { margin: 0; margin-bottom: 10px; padding: 0; font-weight: normal; font-size: 30px; color: #626569; color: rgba(0, 0, 0, 0.4); text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5); } -input { font-size: 14px; border: 1px solid #777; box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(255, 255, 255, 0.3); border-radius: 4px; padding: 3px; -webkit-background-clip: padding-box; } -input[type='radio'] { border: 0; } - -/* =LAYOUT -====================================================== */ -#content { width: 800px; margin: auto; padding: 100px; padding-bottom: 60px; - /* white radial gradient background */ - background: -moz-radial-gradient(center, ellipse cover, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 70%); - background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%,rgba(255,255,255,0.5)), color-stop(70%,rgba(255,255,255,0))); - background: -webkit-radial-gradient(center, ellipse cover, rgba(255,255,255,0.5) 0%,rgba(255,255,255,0) 70%); - background: -o-radial-gradient(center, ellipse cover, rgba(255,255,255,0.5) 0%,rgba(255,255,255,0) 70%); - background: -ms-radial-gradient(center, ellipse cover, rgba(255,255,255,0.5) 0%,rgba(255,255,255,0) 70%); - background: radial-gradient(center, ellipse cover, rgba(255,255,255,0.5) 0%,rgba(255,255,255,0) 70%); -} -#footer { padding-top: 10px; font-size: 13px; color: rgba(255, 255, 255, 0.7); text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); border-top: 1px solid #797f85; border-top-color: rgba(0, 0, 0, 0.15); box-shadow: inset 0 1px 1px 0 rgba(255, 255, 255, 0.2); vertical-align: top; text-align: center; } -#footer a { vertical-align: top; color: #fff; } -.clr:after { clear:both; content:"."; display: block; height:0; visibility: hidden; line-height:0; font-size:0; } - -.ir { border: 0; font: 0/0 a; text-shadow: none; color: transparent; background-color: transparent; } - -.preview_and_configuration { float: left; width: 395px; } - -/* =MODULES -====================================================== */ -/* preview */ -.arrow_box { padding: 40px; width: 280px; height: 100px; border-radius: 6px; } - -/* logo */ -.logo { color: #ddf8c6; text-align: center; font-size: 54px; line-height: 54px; font-weight: bold; text-transform: uppercase; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4); } - -/* configuration */ -.configuration { margin-top: 20px; } -.configuration .section { float: left; width: 195px; font-size: 20px } -.configuration .section.positions { width: 140px; margin-left: 14px } -.configuration .section li { margin-bottom: 5px; } -.configuration .section label { display: inline-block; width: 112px; } - -.configuration .size, -.configuration .border_width { width: 28px; text-align: right; } - -.configuration .color { width: 65px; text-align: center } - -.configuration .unit { font-size: 14px; color: rgba(0, 0, 0, 0.4); text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5); margin-left: 5px; } - -/* css_result */ -.css_result { position: relative; float: right; width: 402px; } -.css_result .code { white-space: pre; padding: 10px; display: block; width: 380px; font-size: 12px; font-family: 'Courier new'; font-weight: bold; background: #8c9196; background: rgba(0, 0, 0, 0.15); border-radius: 4px; color: #fff; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3); border: 1px solid #696d72; border-color: rgba(0, 0, 0, 0.2); box-shadow: 0 1px 1px 0 rgba(255, 255, 255, 0.3), inset 0 1px 5px rgba(0, 0, 0, 0.1); } -.css_result .copy_code { position: absolute; bottom: 5px; right: 10px; width: 14px; height: 22px; background: url(../img/clippy.png) no-repeat 0 4px; } - - -/* fork_me */ -.fork_me { position: absolute; top: 0; right: 0; display: block; width: 149px; height: 149px; background: url(../img/fork.png); } +body { + font-family: Arial, sans-serif; + background: #F0F0F0; + color: #BFBFBF; + padding: 0; + margin: 0; + font-size: 13px; +} + +pre { + tab-size: 2; +} + +h1, h2 { + margin: 0; +} + +h1 { + box-shadow: 0 4px 0 0 rgba(0, 0, 0, 0.1); + display: -webkit-flex; + display: flex; + -webkit-flex-direction: row; + flex-direction: row; + -webkit-justify-content: center; + justify-content: center; +} + +h2 { + font-size: 13px; + text-transform: uppercase; +} + +a { + text-decoration: none; + color: rgba(0, 0, 0, 0.4); + -webkit-transition: all 0.2s; + -moz-transition: all 0.2s; + transition: all 0.2s; +} + +a:hover { + color: #000; +} + +ol { + list-style-type: none; + margin: 0; + padding: 0; +} + +ol li { + height: 19px; + padding-top: 7px; + vertical-align: middle; +} +ol li.with-input { + padding-top: 0; + height: 26px; + margin-top: 3px; +} +ol li.for-input { + margin-top: 3px; +} + +input[type=text] { + border: 0; + padding: 2px 4px; + width: 69px; + color: #4D4D4D; + font-weight: bold; + font-size: 14px; + height: 22px; +} +input[type=text]:focus { + outline: none; + box-shadow: inset 0 0 0 2px #6CC3FF; +} + +input[type=radio] { + width: 18px; + position: relative; + font-size: 1px; +} +input[type=radio]:after { + background: #fff; + position: absolute; + top: -5px; + left: -2px; + border-radius: 9px; + width: 18px; + height: 18px; + content: ' '; +} +input[type=radio]:checked:after { + background: #6CC3FF; + box-shadow: inset 0 0 0 2px #568aaf; +} + +.content { + display: -webkit-flex; + display: flex; + -webkit-flex-direction: row; + flex-direction: row; + -webkit-justify-content: center; + justify-content: center; +} + +.main-header { + background: #535962; +} + +.main-header h1 { + padding: 15px 0; + background: #ff8786; + text-align: center; + text-transform: uppercase; +} + +.main-header h1 span { + font-size: 11px; + color: #cc6c6b; + line-height: 24px; + margin: 0 5px; +} + +.main-header h1 .logo { + font-size: 24px; + color: #fff; +} + +.main-header h1 a { + font-size: 11px; + line-height: 24px; + color: #995150; +} +.main-header h1 a:hover{ + color: #fff; +} + +.main-header h2 { + background: #42474e; + color: rgba(187, 216, 236, 0.44); +} + +.main-header h2 .config { + padding: 15px 10px; + width: 57px; + background: #393c41; + display: inline-block; +} +.main-header h2 .code { + padding: 15px 10px; + background: #303338; + display: inline-block; +} + +.config { + width: 77px; +} + +.code { + margin: 0; + width: 325px; + padding: 10px; + font-size: 12px; +} + +.main-content aside { + padding-top: 10px; + font-weight: bold; + text-transform: uppercase; +} + +.main-content .config-labels { + text-align: right; + margin-right: 20px; + width: 170px; +} + +.main-content .who { + margin-left: 20px; + width: 170px; + font-size: 11px; + line-height: 14px; +} +.main-content .who .twitter-share-button { + display: block; + margin-bottom: 10px; +} + +.main-content .config { + padding-top: 10px; + background: #d8d8d8; + height: 500px; +} + +.main-content .config li { + text-align: center; +} + +.main-content .code { + background: #c3c3c3; + color: #373737; +} diff --git a/public/index.html b/public/index.html index 413f523..0dcbb93 100644 --- a/public/index.html +++ b/public/index.html @@ -3,90 +3,27 @@ cssarrowplease - + + +
-
-
-
-
-

css arrow please!

-
-
-
-

Arrow configuration

-
-
    -
  1. - - - - - -
  2. -
-
    -
  1. - - px -
  2. -
  3. - - -
  4. -
-
    -
  1. - - px -
  2. -
  3. - - -
  4. -
-
-
-
- -
- -
-
- - - Fork me on Github - - -
- - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/js/vendor/jasmine/jasmine-html.js b/public/js/vendor/jasmine/jasmine-html.js deleted file mode 100644 index 3de4e8a..0000000 --- a/public/js/vendor/jasmine/jasmine-html.js +++ /dev/null @@ -1,676 +0,0 @@ -jasmine.HtmlReporterHelpers = {}; - -jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) { - var el = document.createElement(type); - - for (var i = 2; i < arguments.length; i++) { - var child = arguments[i]; - - if (typeof child === 'string') { - el.appendChild(document.createTextNode(child)); - } else { - if (child) { - el.appendChild(child); - } - } - } - - for (var attr in attrs) { - if (attr == "className") { - el[attr] = attrs[attr]; - } else { - el.setAttribute(attr, attrs[attr]); - } - } - - return el; -}; - -jasmine.HtmlReporterHelpers.getSpecStatus = function(child) { - var results = child.results(); - var status = results.passed() ? 'passed' : 'failed'; - if (results.skipped) { - status = 'skipped'; - } - - return status; -}; - -jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) { - var parentDiv = this.dom.summary; - var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite'; - var parent = child[parentSuite]; - - if (parent) { - if (typeof this.views.suites[parent.id] == 'undefined') { - this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views); - } - parentDiv = this.views.suites[parent.id].element; - } - - parentDiv.appendChild(childElement); -}; - - -jasmine.HtmlReporterHelpers.addHelpers = function(ctor) { - for(var fn in jasmine.HtmlReporterHelpers) { - ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn]; - } -}; - -jasmine.HtmlReporter = function(_doc) { - var self = this; - var doc = _doc || window.document; - - var reporterView; - - var dom = {}; - - // Jasmine Reporter Public Interface - self.logRunningSpecs = false; - - self.reportRunnerStarting = function(runner) { - var specs = runner.specs() || []; - - if (specs.length == 0) { - return; - } - - createReporterDom(runner.env.versionString()); - doc.body.appendChild(dom.reporter); - - reporterView = new jasmine.HtmlReporter.ReporterView(dom); - reporterView.addSpecs(specs, self.specFilter); - }; - - self.reportRunnerResults = function(runner) { - reporterView.complete(); - }; - - self.reportSuiteResults = function(suite) { - reporterView.suiteComplete(suite); - }; - - self.reportSpecStarting = function(spec) { - if (self.logRunningSpecs) { - self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); - } - }; - - self.reportSpecResults = function(spec) { - reporterView.specComplete(spec); - }; - - self.log = function() { - var console = jasmine.getGlobal().console; - if (console && console.log) { - if (console.log.apply) { - console.log.apply(console, arguments); - } else { - console.log(arguments); // ie fix: console.log.apply doesn't exist on ie - } - } - }; - - self.specFilter = function(spec) { - if (!focusedSpecName()) { - return true; - } - - return spec.getFullName().indexOf(focusedSpecName()) === 0; - }; - - return self; - - function focusedSpecName() { - var specName; - - (function memoizeFocusedSpec() { - if (specName) { - return; - } - - var paramMap = []; - var params = doc.location.search.substring(1).split('&'); - - for (var i = 0; i < params.length; i++) { - var p = params[i].split('='); - paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); - } - - specName = paramMap.spec; - })(); - - return specName; - } - - function createReporterDom(version) { - dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' }, - dom.banner = self.createDom('div', { className: 'banner' }, - self.createDom('span', { className: 'title' }, "Jasmine "), - self.createDom('span', { className: 'version' }, version)), - - dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}), - dom.alert = self.createDom('div', {className: 'alert'}), - dom.results = self.createDom('div', {className: 'results'}, - dom.summary = self.createDom('div', { className: 'summary' }), - dom.details = self.createDom('div', { id: 'details' })) - ); - } -}; -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporterHelpers = {}; - -jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) { - var el = document.createElement(type); - - for (var i = 2; i < arguments.length; i++) { - var child = arguments[i]; - - if (typeof child === 'string') { - el.appendChild(document.createTextNode(child)); - } else { - if (child) { - el.appendChild(child); - } - } - } - - for (var attr in attrs) { - if (attr == "className") { - el[attr] = attrs[attr]; - } else { - el.setAttribute(attr, attrs[attr]); - } - } - - return el; -}; - -jasmine.HtmlReporterHelpers.getSpecStatus = function(child) { - var results = child.results(); - var status = results.passed() ? 'passed' : 'failed'; - if (results.skipped) { - status = 'skipped'; - } - - return status; -}; - -jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) { - var parentDiv = this.dom.summary; - var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite'; - var parent = child[parentSuite]; - - if (parent) { - if (typeof this.views.suites[parent.id] == 'undefined') { - this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views); - } - parentDiv = this.views.suites[parent.id].element; - } - - parentDiv.appendChild(childElement); -}; - - -jasmine.HtmlReporterHelpers.addHelpers = function(ctor) { - for(var fn in jasmine.HtmlReporterHelpers) { - ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn]; - } -}; - -jasmine.HtmlReporter.ReporterView = function(dom) { - this.startedAt = new Date(); - this.runningSpecCount = 0; - this.completeSpecCount = 0; - this.passedCount = 0; - this.failedCount = 0; - this.skippedCount = 0; - - this.createResultsMenu = function() { - this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'}, - this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'), - ' | ', - this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing')); - - this.summaryMenuItem.onclick = function() { - dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, ''); - }; - - this.detailsMenuItem.onclick = function() { - showDetails(); - }; - }; - - this.addSpecs = function(specs, specFilter) { - this.totalSpecCount = specs.length; - - this.views = { - specs: {}, - suites: {} - }; - - for (var i = 0; i < specs.length; i++) { - var spec = specs[i]; - this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views); - if (specFilter(spec)) { - this.runningSpecCount++; - } - } - }; - - this.specComplete = function(spec) { - this.completeSpecCount++; - - if (isUndefined(this.views.specs[spec.id])) { - this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom); - } - - var specView = this.views.specs[spec.id]; - - switch (specView.status()) { - case 'passed': - this.passedCount++; - break; - - case 'failed': - this.failedCount++; - break; - - case 'skipped': - this.skippedCount++; - break; - } - - specView.refresh(); - this.refresh(); - }; - - this.suiteComplete = function(suite) { - var suiteView = this.views.suites[suite.id]; - if (isUndefined(suiteView)) { - return; - } - suiteView.refresh(); - }; - - this.refresh = function() { - - if (isUndefined(this.resultsMenu)) { - this.createResultsMenu(); - } - - // currently running UI - if (isUndefined(this.runningAlert)) { - this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"}); - dom.alert.appendChild(this.runningAlert); - } - this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount); - - // skipped specs UI - if (isUndefined(this.skippedAlert)) { - this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"}); - } - - this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; - - if (this.skippedCount === 1 && isDefined(dom.alert)) { - dom.alert.appendChild(this.skippedAlert); - } - - // passing specs UI - if (isUndefined(this.passedAlert)) { - this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"}); - } - this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount); - - // failing specs UI - if (isUndefined(this.failedAlert)) { - this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"}); - } - this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount); - - if (this.failedCount === 1 && isDefined(dom.alert)) { - dom.alert.appendChild(this.failedAlert); - dom.alert.appendChild(this.resultsMenu); - } - - // summary info - this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount); - this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing"; - }; - - this.complete = function() { - dom.alert.removeChild(this.runningAlert); - - this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; - - if (this.failedCount === 0) { - dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount))); - } else { - showDetails(); - } - - dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s")); - }; - - return this; - - function showDetails() { - if (dom.reporter.className.search(/showDetails/) === -1) { - dom.reporter.className += " showDetails"; - } - } - - function isUndefined(obj) { - return typeof obj === 'undefined'; - } - - function isDefined(obj) { - return !isUndefined(obj); - } - - function specPluralizedFor(count) { - var str = count + " spec"; - if (count > 1) { - str += "s" - } - return str; - } - -}; - -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView); - - -jasmine.HtmlReporter.SpecView = function(spec, dom, views) { - this.spec = spec; - this.dom = dom; - this.views = views; - - this.symbol = this.createDom('li', { className: 'pending' }); - this.dom.symbolSummary.appendChild(this.symbol); - - this.summary = this.createDom('div', { className: 'specSummary' }, - this.createDom('a', { - className: 'description', - href: '?spec=' + encodeURIComponent(this.spec.getFullName()), - title: this.spec.getFullName() - }, this.spec.description) - ); - - this.detail = this.createDom('div', { className: 'specDetail' }, - this.createDom('a', { - className: 'description', - href: '?spec=' + encodeURIComponent(this.spec.getFullName()), - title: this.spec.getFullName() - }, this.spec.getFullName()) - ); -}; - -jasmine.HtmlReporter.SpecView.prototype.status = function() { - return this.getSpecStatus(this.spec); -}; - -jasmine.HtmlReporter.SpecView.prototype.refresh = function() { - this.symbol.className = this.status(); - - switch (this.status()) { - case 'skipped': - break; - - case 'passed': - this.appendSummaryToSuiteDiv(); - break; - - case 'failed': - this.appendSummaryToSuiteDiv(); - this.appendFailureDetail(); - break; - } -}; - -jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() { - this.summary.className += ' ' + this.status(); - this.appendToSummary(this.spec, this.summary); -}; - -jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() { - this.detail.className += ' ' + this.status(); - - var resultItems = this.spec.results().getItems(); - var messagesDiv = this.createDom('div', { className: 'messages' }); - - for (var i = 0; i < resultItems.length; i++) { - var result = resultItems[i]; - - if (result.type == 'log') { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); - } else if (result.type == 'expect' && result.passed && !result.passed()) { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); - - if (result.trace.stack) { - messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); - } - } - } - - if (messagesDiv.childNodes.length > 0) { - this.detail.appendChild(messagesDiv); - this.dom.details.appendChild(this.detail); - } -}; - -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) { - this.suite = suite; - this.dom = dom; - this.views = views; - - this.element = this.createDom('div', { className: 'suite' }, - this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description) - ); - - this.appendToSummary(this.suite, this.element); -}; - -jasmine.HtmlReporter.SuiteView.prototype.status = function() { - return this.getSpecStatus(this.suite); -}; - -jasmine.HtmlReporter.SuiteView.prototype.refresh = function() { - this.element.className += " " + this.status(); -}; - -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView); - -/* @deprecated Use jasmine.HtmlReporter instead - */ -jasmine.TrivialReporter = function(doc) { - this.document = doc || document; - this.suiteDivs = {}; - this.logRunningSpecs = false; -}; - -jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) { - var el = document.createElement(type); - - for (var i = 2; i < arguments.length; i++) { - var child = arguments[i]; - - if (typeof child === 'string') { - el.appendChild(document.createTextNode(child)); - } else { - if (child) { el.appendChild(child); } - } - } - - for (var attr in attrs) { - if (attr == "className") { - el[attr] = attrs[attr]; - } else { - el.setAttribute(attr, attrs[attr]); - } - } - - return el; -}; - -jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { - var showPassed, showSkipped; - - this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' }, - this.createDom('div', { className: 'banner' }, - this.createDom('div', { className: 'logo' }, - this.createDom('span', { className: 'title' }, "Jasmine"), - this.createDom('span', { className: 'version' }, runner.env.versionString())), - this.createDom('div', { className: 'options' }, - "Show ", - showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }), - this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "), - showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }), - this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped") - ) - ), - - this.runnerDiv = this.createDom('div', { className: 'runner running' }, - this.createDom('a', { className: 'run_spec', href: '?' }, "run all"), - this.runnerMessageSpan = this.createDom('span', {}, "Running..."), - this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, "")) - ); - - this.document.body.appendChild(this.outerDiv); - - var suites = runner.suites(); - for (var i = 0; i < suites.length; i++) { - var suite = suites[i]; - var suiteDiv = this.createDom('div', { className: 'suite' }, - this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"), - this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description)); - this.suiteDivs[suite.id] = suiteDiv; - var parentDiv = this.outerDiv; - if (suite.parentSuite) { - parentDiv = this.suiteDivs[suite.parentSuite.id]; - } - parentDiv.appendChild(suiteDiv); - } - - this.startedAt = new Date(); - - var self = this; - showPassed.onclick = function(evt) { - if (showPassed.checked) { - self.outerDiv.className += ' show-passed'; - } else { - self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, ''); - } - }; - - showSkipped.onclick = function(evt) { - if (showSkipped.checked) { - self.outerDiv.className += ' show-skipped'; - } else { - self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, ''); - } - }; -}; - -jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) { - var results = runner.results(); - var className = (results.failedCount > 0) ? "runner failed" : "runner passed"; - this.runnerDiv.setAttribute("class", className); - //do it twice for IE - this.runnerDiv.setAttribute("className", className); - var specs = runner.specs(); - var specCount = 0; - for (var i = 0; i < specs.length; i++) { - if (this.specFilter(specs[i])) { - specCount++; - } - } - var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s"); - message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"; - this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild); - - this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString())); -}; - -jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) { - var results = suite.results(); - var status = results.passed() ? 'passed' : 'failed'; - if (results.totalCount === 0) { // todo: change this to check results.skipped - status = 'skipped'; - } - this.suiteDivs[suite.id].className += " " + status; -}; - -jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) { - if (this.logRunningSpecs) { - this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); - } -}; - -jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) { - var results = spec.results(); - var status = results.passed() ? 'passed' : 'failed'; - if (results.skipped) { - status = 'skipped'; - } - var specDiv = this.createDom('div', { className: 'spec ' + status }, - this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"), - this.createDom('a', { - className: 'description', - href: '?spec=' + encodeURIComponent(spec.getFullName()), - title: spec.getFullName() - }, spec.description)); - - - var resultItems = results.getItems(); - var messagesDiv = this.createDom('div', { className: 'messages' }); - for (var i = 0; i < resultItems.length; i++) { - var result = resultItems[i]; - - if (result.type == 'log') { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); - } else if (result.type == 'expect' && result.passed && !result.passed()) { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); - - if (result.trace.stack) { - messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); - } - } - } - - if (messagesDiv.childNodes.length > 0) { - specDiv.appendChild(messagesDiv); - } - - this.suiteDivs[spec.suite.id].appendChild(specDiv); -}; - -jasmine.TrivialReporter.prototype.log = function() { - var console = jasmine.getGlobal().console; - if (console && console.log) { - if (console.log.apply) { - console.log.apply(console, arguments); - } else { - console.log(arguments); // ie fix: console.log.apply doesn't exist on ie - } - } -}; - -jasmine.TrivialReporter.prototype.getLocation = function() { - return this.document.location; -}; - -jasmine.TrivialReporter.prototype.specFilter = function(spec) { - var paramMap = {}; - var params = this.getLocation().search.substring(1).split('&'); - for (var i = 0; i < params.length; i++) { - var p = params[i].split('='); - paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); - } - - if (!paramMap.spec) { - return true; - } - return spec.getFullName().indexOf(paramMap.spec) === 0; -}; diff --git a/public/js/vendor/jasmine/jasmine.css b/public/js/vendor/jasmine/jasmine.css deleted file mode 100644 index 826e575..0000000 --- a/public/js/vendor/jasmine/jasmine.css +++ /dev/null @@ -1,81 +0,0 @@ -body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } - -#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } -#HTMLReporter a { text-decoration: none; } -#HTMLReporter a:hover { text-decoration: underline; } -#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } -#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } -#HTMLReporter #jasmine_content { position: fixed; right: 100%; } -#HTMLReporter .version { color: #aaaaaa; } -#HTMLReporter .banner { margin-top: 14px; } -#HTMLReporter .duration { color: #aaaaaa; float: right; } -#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } -#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } -#HTMLReporter .symbolSummary li.passed { font-size: 14px; } -#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } -#HTMLReporter .symbolSummary li.failed { line-height: 9px; } -#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } -#HTMLReporter .symbolSummary li.skipped { font-size: 14px; } -#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } -#HTMLReporter .symbolSummary li.pending { line-height: 11px; } -#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } -#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } -#HTMLReporter .runningAlert { background-color: #666666; } -#HTMLReporter .skippedAlert { background-color: #aaaaaa; } -#HTMLReporter .skippedAlert:first-child { background-color: #333333; } -#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } -#HTMLReporter .passingAlert { background-color: #a6b779; } -#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } -#HTMLReporter .failingAlert { background-color: #cf867e; } -#HTMLReporter .failingAlert:first-child { background-color: #b03911; } -#HTMLReporter .results { margin-top: 14px; } -#HTMLReporter #details { display: none; } -#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } -#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } -#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } -#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } -#HTMLReporter.showDetails .summary { display: none; } -#HTMLReporter.showDetails #details { display: block; } -#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } -#HTMLReporter .summary { margin-top: 14px; } -#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } -#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } -#HTMLReporter .summary .specSummary.failed a { color: #b03911; } -#HTMLReporter .description + .suite { margin-top: 0; } -#HTMLReporter .suite { margin-top: 14px; } -#HTMLReporter .suite a { color: #333333; } -#HTMLReporter #details .specDetail { margin-bottom: 28px; } -#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } -#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } -#HTMLReporter .resultMessage span.result { display: block; } -#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } - -#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } -#TrivialReporter a:visited, #TrivialReporter a { color: #303; } -#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } -#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } -#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } -#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } -#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } -#TrivialReporter .runner.running { background-color: yellow; } -#TrivialReporter .options { text-align: right; font-size: .8em; } -#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } -#TrivialReporter .suite .suite { margin: 5px; } -#TrivialReporter .suite.passed { background-color: #dfd; } -#TrivialReporter .suite.failed { background-color: #fdd; } -#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } -#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } -#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } -#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } -#TrivialReporter .spec.skipped { background-color: #bbb; } -#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } -#TrivialReporter .passed { background-color: #cfc; display: none; } -#TrivialReporter .failed { background-color: #fbb; } -#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } -#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } -#TrivialReporter .resultMessage .mismatch { color: black; } -#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } -#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } -#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } -#TrivialReporter #jasmine_content { position: fixed; right: 100%; } -#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } diff --git a/public/js/vendor/jasmine/jasmine.js b/public/js/vendor/jasmine/jasmine.js deleted file mode 100644 index 2e562a8..0000000 --- a/public/js/vendor/jasmine/jasmine.js +++ /dev/null @@ -1,2528 +0,0 @@ -var isCommonJS = typeof window == "undefined"; - -/** - * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. - * - * @namespace - */ -var jasmine = {}; -if (isCommonJS) exports.jasmine = jasmine; -/** - * @private - */ -jasmine.unimplementedMethod_ = function() { - throw new Error("unimplemented method"); -}; - -/** - * Use jasmine.undefined instead of undefined, since undefined is just - * a plain old variable and may be redefined by somebody else. - * - * @private - */ -jasmine.undefined = jasmine.___undefined___; - -/** - * Show diagnostic messages in the console if set to true - * - */ -jasmine.VERBOSE = false; - -/** - * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed. - * - */ -jasmine.DEFAULT_UPDATE_INTERVAL = 250; - -/** - * Default timeout interval in milliseconds for waitsFor() blocks. - */ -jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; - -jasmine.getGlobal = function() { - function getGlobal() { - return this; - } - - return getGlobal(); -}; - -/** - * Allows for bound functions to be compared. Internal use only. - * - * @ignore - * @private - * @param base {Object} bound 'this' for the function - * @param name {Function} function to find - */ -jasmine.bindOriginal_ = function(base, name) { - var original = base[name]; - if (original.apply) { - return function() { - return original.apply(base, arguments); - }; - } else { - // IE support - return jasmine.getGlobal()[name]; - } -}; - -jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); -jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); -jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); -jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); - -jasmine.MessageResult = function(values) { - this.type = 'log'; - this.values = values; - this.trace = new Error(); // todo: test better -}; - -jasmine.MessageResult.prototype.toString = function() { - var text = ""; - for (var i = 0; i < this.values.length; i++) { - if (i > 0) text += " "; - if (jasmine.isString_(this.values[i])) { - text += this.values[i]; - } else { - text += jasmine.pp(this.values[i]); - } - } - return text; -}; - -jasmine.ExpectationResult = function(params) { - this.type = 'expect'; - this.matcherName = params.matcherName; - this.passed_ = params.passed; - this.expected = params.expected; - this.actual = params.actual; - this.message = this.passed_ ? 'Passed.' : params.message; - - var trace = (params.trace || new Error(this.message)); - this.trace = this.passed_ ? '' : trace; -}; - -jasmine.ExpectationResult.prototype.toString = function () { - return this.message; -}; - -jasmine.ExpectationResult.prototype.passed = function () { - return this.passed_; -}; - -/** - * Getter for the Jasmine environment. Ensures one gets created - */ -jasmine.getEnv = function() { - var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); - return env; -}; - -/** - * @ignore - * @private - * @param value - * @returns {Boolean} - */ -jasmine.isArray_ = function(value) { - return jasmine.isA_("Array", value); -}; - -/** - * @ignore - * @private - * @param value - * @returns {Boolean} - */ -jasmine.isString_ = function(value) { - return jasmine.isA_("String", value); -}; - -/** - * @ignore - * @private - * @param value - * @returns {Boolean} - */ -jasmine.isNumber_ = function(value) { - return jasmine.isA_("Number", value); -}; - -/** - * @ignore - * @private - * @param {String} typeName - * @param value - * @returns {Boolean} - */ -jasmine.isA_ = function(typeName, value) { - return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; -}; - -/** - * Pretty printer for expecations. Takes any object and turns it into a human-readable string. - * - * @param value {Object} an object to be outputted - * @returns {String} - */ -jasmine.pp = function(value) { - var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); - stringPrettyPrinter.format(value); - return stringPrettyPrinter.string; -}; - -/** - * Returns true if the object is a DOM Node. - * - * @param {Object} obj object to check - * @returns {Boolean} - */ -jasmine.isDomNode = function(obj) { - return obj.nodeType > 0; -}; - -/** - * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. - * - * @example - * // don't care about which function is passed in, as long as it's a function - * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function)); - * - * @param {Class} clazz - * @returns matchable object of the type clazz - */ -jasmine.any = function(clazz) { - return new jasmine.Matchers.Any(clazz); -}; - -/** - * Returns a matchable subset of a hash/JSON object. For use in expectations when you don't care about all of the - * attributes on the object. - * - * @example - * // don't care about any other attributes than foo. - * expect(mySpy).toHaveBeenCalledWith(jasmine.hashContaining({foo: "bar"}); - * - * @param sample {Object} sample - * @returns matchable object for the sample - */ -jasmine.objectContaining = function (sample) { - return new jasmine.Matchers.ObjectContaining(sample); -}; - -/** - * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. - * - * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine - * expectation syntax. Spies can be checked if they were called or not and what the calling params were. - * - * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs). - * - * Spies are torn down at the end of every spec. - * - * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. - * - * @example - * // a stub - * var myStub = jasmine.createSpy('myStub'); // can be used anywhere - * - * // spy example - * var foo = { - * not: function(bool) { return !bool; } - * } - * - * // actual foo.not will not be called, execution stops - * spyOn(foo, 'not'); - - // foo.not spied upon, execution will continue to implementation - * spyOn(foo, 'not').andCallThrough(); - * - * // fake example - * var foo = { - * not: function(bool) { return !bool; } - * } - * - * // foo.not(val) will return val - * spyOn(foo, 'not').andCallFake(function(value) {return value;}); - * - * // mock example - * foo.not(7 == 7); - * expect(foo.not).toHaveBeenCalled(); - * expect(foo.not).toHaveBeenCalledWith(true); - * - * @constructor - * @see spyOn, jasmine.createSpy, jasmine.createSpyObj - * @param {String} name - */ -jasmine.Spy = function(name) { - /** - * The name of the spy, if provided. - */ - this.identity = name || 'unknown'; - /** - * Is this Object a spy? - */ - this.isSpy = true; - /** - * The actual function this spy stubs. - */ - this.plan = function() { - }; - /** - * Tracking of the most recent call to the spy. - * @example - * var mySpy = jasmine.createSpy('foo'); - * mySpy(1, 2); - * mySpy.mostRecentCall.args = [1, 2]; - */ - this.mostRecentCall = {}; - - /** - * Holds arguments for each call to the spy, indexed by call count - * @example - * var mySpy = jasmine.createSpy('foo'); - * mySpy(1, 2); - * mySpy(7, 8); - * mySpy.mostRecentCall.args = [7, 8]; - * mySpy.argsForCall[0] = [1, 2]; - * mySpy.argsForCall[1] = [7, 8]; - */ - this.argsForCall = []; - this.calls = []; -}; - -/** - * Tells a spy to call through to the actual implementation. - * - * @example - * var foo = { - * bar: function() { // do some stuff } - * } - * - * // defining a spy on an existing property: foo.bar - * spyOn(foo, 'bar').andCallThrough(); - */ -jasmine.Spy.prototype.andCallThrough = function() { - this.plan = this.originalValue; - return this; -}; - -/** - * For setting the return value of a spy. - * - * @example - * // defining a spy from scratch: foo() returns 'baz' - * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); - * - * // defining a spy on an existing property: foo.bar() returns 'baz' - * spyOn(foo, 'bar').andReturn('baz'); - * - * @param {Object} value - */ -jasmine.Spy.prototype.andReturn = function(value) { - this.plan = function() { - return value; - }; - return this; -}; - -/** - * For throwing an exception when a spy is called. - * - * @example - * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' - * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); - * - * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' - * spyOn(foo, 'bar').andThrow('baz'); - * - * @param {String} exceptionMsg - */ -jasmine.Spy.prototype.andThrow = function(exceptionMsg) { - this.plan = function() { - throw exceptionMsg; - }; - return this; -}; - -/** - * Calls an alternate implementation when a spy is called. - * - * @example - * var baz = function() { - * // do some stuff, return something - * } - * // defining a spy from scratch: foo() calls the function baz - * var foo = jasmine.createSpy('spy on foo').andCall(baz); - * - * // defining a spy on an existing property: foo.bar() calls an anonymnous function - * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); - * - * @param {Function} fakeFunc - */ -jasmine.Spy.prototype.andCallFake = function(fakeFunc) { - this.plan = fakeFunc; - return this; -}; - -/** - * Resets all of a spy's the tracking variables so that it can be used again. - * - * @example - * spyOn(foo, 'bar'); - * - * foo.bar(); - * - * expect(foo.bar.callCount).toEqual(1); - * - * foo.bar.reset(); - * - * expect(foo.bar.callCount).toEqual(0); - */ -jasmine.Spy.prototype.reset = function() { - this.wasCalled = false; - this.callCount = 0; - this.argsForCall = []; - this.calls = []; - this.mostRecentCall = {}; -}; - -jasmine.createSpy = function(name) { - - var spyObj = function() { - spyObj.wasCalled = true; - spyObj.callCount++; - var args = jasmine.util.argsToArray(arguments); - spyObj.mostRecentCall.object = this; - spyObj.mostRecentCall.args = args; - spyObj.argsForCall.push(args); - spyObj.calls.push({object: this, args: args}); - return spyObj.plan.apply(this, arguments); - }; - - var spy = new jasmine.Spy(name); - - for (var prop in spy) { - spyObj[prop] = spy[prop]; - } - - spyObj.reset(); - - return spyObj; -}; - -/** - * Determines whether an object is a spy. - * - * @param {jasmine.Spy|Object} putativeSpy - * @returns {Boolean} - */ -jasmine.isSpy = function(putativeSpy) { - return putativeSpy && putativeSpy.isSpy; -}; - -/** - * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something - * large in one call. - * - * @param {String} baseName name of spy class - * @param {Array} methodNames array of names of methods to make spies - */ -jasmine.createSpyObj = function(baseName, methodNames) { - if (!jasmine.isArray_(methodNames) || methodNames.length === 0) { - throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); - } - var obj = {}; - for (var i = 0; i < methodNames.length; i++) { - obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); - } - return obj; -}; - -/** - * All parameters are pretty-printed and concatenated together, then written to the current spec's output. - * - * Be careful not to leave calls to jasmine.log in production code. - */ -jasmine.log = function() { - var spec = jasmine.getEnv().currentSpec; - spec.log.apply(spec, arguments); -}; - -/** - * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. - * - * @example - * // spy example - * var foo = { - * not: function(bool) { return !bool; } - * } - * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops - * - * @see jasmine.createSpy - * @param obj - * @param methodName - * @returns a Jasmine spy that can be chained with all spy methods - */ -var spyOn = function(obj, methodName) { - return jasmine.getEnv().currentSpec.spyOn(obj, methodName); -}; -if (isCommonJS) exports.spyOn = spyOn; - -/** - * Creates a Jasmine spec that will be added to the current suite. - * - * // TODO: pending tests - * - * @example - * it('should be true', function() { - * expect(true).toEqual(true); - * }); - * - * @param {String} desc description of this specification - * @param {Function} func defines the preconditions and expectations of the spec - */ -var it = function(desc, func) { - return jasmine.getEnv().it(desc, func); -}; -if (isCommonJS) exports.it = it; - -/** - * Creates a disabled Jasmine spec. - * - * A convenience method that allows existing specs to be disabled temporarily during development. - * - * @param {String} desc description of this specification - * @param {Function} func defines the preconditions and expectations of the spec - */ -var xit = function(desc, func) { - return jasmine.getEnv().xit(desc, func); -}; -if (isCommonJS) exports.xit = xit; - -/** - * Starts a chain for a Jasmine expectation. - * - * It is passed an Object that is the actual value and should chain to one of the many - * jasmine.Matchers functions. - * - * @param {Object} actual Actual value to test against and expected value - */ -var expect = function(actual) { - return jasmine.getEnv().currentSpec.expect(actual); -}; -if (isCommonJS) exports.expect = expect; - -/** - * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. - * - * @param {Function} func Function that defines part of a jasmine spec. - */ -var runs = function(func) { - jasmine.getEnv().currentSpec.runs(func); -}; -if (isCommonJS) exports.runs = runs; - -/** - * Waits a fixed time period before moving to the next block. - * - * @deprecated Use waitsFor() instead - * @param {Number} timeout milliseconds to wait - */ -var waits = function(timeout) { - jasmine.getEnv().currentSpec.waits(timeout); -}; -if (isCommonJS) exports.waits = waits; - -/** - * Waits for the latchFunction to return true before proceeding to the next block. - * - * @param {Function} latchFunction - * @param {String} optional_timeoutMessage - * @param {Number} optional_timeout - */ -var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { - jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); -}; -if (isCommonJS) exports.waitsFor = waitsFor; - -/** - * A function that is called before each spec in a suite. - * - * Used for spec setup, including validating assumptions. - * - * @param {Function} beforeEachFunction - */ -var beforeEach = function(beforeEachFunction) { - jasmine.getEnv().beforeEach(beforeEachFunction); -}; -if (isCommonJS) exports.beforeEach = beforeEach; - -/** - * A function that is called after each spec in a suite. - * - * Used for restoring any state that is hijacked during spec execution. - * - * @param {Function} afterEachFunction - */ -var afterEach = function(afterEachFunction) { - jasmine.getEnv().afterEach(afterEachFunction); -}; -if (isCommonJS) exports.afterEach = afterEach; - -/** - * Defines a suite of specifications. - * - * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared - * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization - * of setup in some tests. - * - * @example - * // TODO: a simple suite - * - * // TODO: a simple suite with a nested describe block - * - * @param {String} description A string, usually the class under test. - * @param {Function} specDefinitions function that defines several specs. - */ -var describe = function(description, specDefinitions) { - return jasmine.getEnv().describe(description, specDefinitions); -}; -if (isCommonJS) exports.describe = describe; - -/** - * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. - * - * @param {String} description A string, usually the class under test. - * @param {Function} specDefinitions function that defines several specs. - */ -var xdescribe = function(description, specDefinitions) { - return jasmine.getEnv().xdescribe(description, specDefinitions); -}; -if (isCommonJS) exports.xdescribe = xdescribe; - - -// Provide the XMLHttpRequest class for IE 5.x-6.x: -jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { - function tryIt(f) { - try { - return f(); - } catch(e) { - } - return null; - } - - var xhr = tryIt(function() { - return new ActiveXObject("Msxml2.XMLHTTP.6.0"); - }) || - tryIt(function() { - return new ActiveXObject("Msxml2.XMLHTTP.3.0"); - }) || - tryIt(function() { - return new ActiveXObject("Msxml2.XMLHTTP"); - }) || - tryIt(function() { - return new ActiveXObject("Microsoft.XMLHTTP"); - }); - - if (!xhr) throw new Error("This browser does not support XMLHttpRequest."); - - return xhr; -} : XMLHttpRequest; -/** - * @namespace - */ -jasmine.util = {}; - -/** - * Declare that a child class inherit it's prototype from the parent class. - * - * @private - * @param {Function} childClass - * @param {Function} parentClass - */ -jasmine.util.inherit = function(childClass, parentClass) { - /** - * @private - */ - var subclass = function() { - }; - subclass.prototype = parentClass.prototype; - childClass.prototype = new subclass(); -}; - -jasmine.util.formatException = function(e) { - var lineNumber; - if (e.line) { - lineNumber = e.line; - } - else if (e.lineNumber) { - lineNumber = e.lineNumber; - } - - var file; - - if (e.sourceURL) { - file = e.sourceURL; - } - else if (e.fileName) { - file = e.fileName; - } - - var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); - - if (file && lineNumber) { - message += ' in ' + file + ' (line ' + lineNumber + ')'; - } - - return message; -}; - -jasmine.util.htmlEscape = function(str) { - if (!str) return str; - return str.replace(/&/g, '&') - .replace(//g, '>'); -}; - -jasmine.util.argsToArray = function(args) { - var arrayOfArgs = []; - for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); - return arrayOfArgs; -}; - -jasmine.util.extend = function(destination, source) { - for (var property in source) destination[property] = source[property]; - return destination; -}; - -/** - * Environment for Jasmine - * - * @constructor - */ -jasmine.Env = function() { - this.currentSpec = null; - this.currentSuite = null; - this.currentRunner_ = new jasmine.Runner(this); - - this.reporter = new jasmine.MultiReporter(); - - this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; - this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; - this.lastUpdate = 0; - this.specFilter = function() { - return true; - }; - - this.nextSpecId_ = 0; - this.nextSuiteId_ = 0; - this.equalityTesters_ = []; - - // wrap matchers - this.matchersClass = function() { - jasmine.Matchers.apply(this, arguments); - }; - jasmine.util.inherit(this.matchersClass, jasmine.Matchers); - - jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); -}; - - -jasmine.Env.prototype.setTimeout = jasmine.setTimeout; -jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; -jasmine.Env.prototype.setInterval = jasmine.setInterval; -jasmine.Env.prototype.clearInterval = jasmine.clearInterval; - -/** - * @returns an object containing jasmine version build info, if set. - */ -jasmine.Env.prototype.version = function () { - if (jasmine.version_) { - return jasmine.version_; - } else { - throw new Error('Version not set'); - } -}; - -/** - * @returns string containing jasmine version build info, if set. - */ -jasmine.Env.prototype.versionString = function() { - if (!jasmine.version_) { - return "version unknown"; - } - - var version = this.version(); - var versionString = version.major + "." + version.minor + "." + version.build; - if (version.release_candidate) { - versionString += ".rc" + version.release_candidate; - } - versionString += " revision " + version.revision; - return versionString; -}; - -/** - * @returns a sequential integer starting at 0 - */ -jasmine.Env.prototype.nextSpecId = function () { - return this.nextSpecId_++; -}; - -/** - * @returns a sequential integer starting at 0 - */ -jasmine.Env.prototype.nextSuiteId = function () { - return this.nextSuiteId_++; -}; - -/** - * Register a reporter to receive status updates from Jasmine. - * @param {jasmine.Reporter} reporter An object which will receive status updates. - */ -jasmine.Env.prototype.addReporter = function(reporter) { - this.reporter.addReporter(reporter); -}; - -jasmine.Env.prototype.execute = function() { - this.currentRunner_.execute(); -}; - -jasmine.Env.prototype.describe = function(description, specDefinitions) { - var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); - - var parentSuite = this.currentSuite; - if (parentSuite) { - parentSuite.add(suite); - } else { - this.currentRunner_.add(suite); - } - - this.currentSuite = suite; - - var declarationError = null; - try { - specDefinitions.call(suite); - } catch(e) { - declarationError = e; - } - - if (declarationError) { - this.it("encountered a declaration exception", function() { - throw declarationError; - }); - } - - this.currentSuite = parentSuite; - - return suite; -}; - -jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { - if (this.currentSuite) { - this.currentSuite.beforeEach(beforeEachFunction); - } else { - this.currentRunner_.beforeEach(beforeEachFunction); - } -}; - -jasmine.Env.prototype.currentRunner = function () { - return this.currentRunner_; -}; - -jasmine.Env.prototype.afterEach = function(afterEachFunction) { - if (this.currentSuite) { - this.currentSuite.afterEach(afterEachFunction); - } else { - this.currentRunner_.afterEach(afterEachFunction); - } - -}; - -jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { - return { - execute: function() { - } - }; -}; - -jasmine.Env.prototype.it = function(description, func) { - var spec = new jasmine.Spec(this, this.currentSuite, description); - this.currentSuite.add(spec); - this.currentSpec = spec; - - if (func) { - spec.runs(func); - } - - return spec; -}; - -jasmine.Env.prototype.xit = function(desc, func) { - return { - id: this.nextSpecId(), - runs: function() { - } - }; -}; - -jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { - if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { - return true; - } - - a.__Jasmine_been_here_before__ = b; - b.__Jasmine_been_here_before__ = a; - - var hasKey = function(obj, keyName) { - return obj !== null && obj[keyName] !== jasmine.undefined; - }; - - for (var property in b) { - if (!hasKey(a, property) && hasKey(b, property)) { - mismatchKeys.push("expected has key '" + property + "', but missing from actual."); - } - } - for (property in a) { - if (!hasKey(b, property) && hasKey(a, property)) { - mismatchKeys.push("expected missing key '" + property + "', but present in actual."); - } - } - for (property in b) { - if (property == '__Jasmine_been_here_before__') continue; - if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { - mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); - } - } - - if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { - mismatchValues.push("arrays were not the same length"); - } - - delete a.__Jasmine_been_here_before__; - delete b.__Jasmine_been_here_before__; - return (mismatchKeys.length === 0 && mismatchValues.length === 0); -}; - -jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { - mismatchKeys = mismatchKeys || []; - mismatchValues = mismatchValues || []; - - for (var i = 0; i < this.equalityTesters_.length; i++) { - var equalityTester = this.equalityTesters_[i]; - var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); - if (result !== jasmine.undefined) return result; - } - - if (a === b) return true; - - if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { - return (a == jasmine.undefined && b == jasmine.undefined); - } - - if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { - return a === b; - } - - if (a instanceof Date && b instanceof Date) { - return a.getTime() == b.getTime(); - } - - if (a instanceof jasmine.Matchers.Any) { - return a.matches(b); - } - - if (b instanceof jasmine.Matchers.Any) { - return b.matches(a); - } - - if (a instanceof jasmine.Matchers.ObjectContaining) { - return a.matches(b); - } - - if (b instanceof jasmine.Matchers.ObjectContaining) { - return b.matches(a); - } - - if (jasmine.isString_(a) && jasmine.isString_(b)) { - return (a == b); - } - - if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { - return (a == b); - } - - if (typeof a === "object" && typeof b === "object") { - return this.compareObjects_(a, b, mismatchKeys, mismatchValues); - } - - //Straight check - return (a === b); -}; - -jasmine.Env.prototype.contains_ = function(haystack, needle) { - if (jasmine.isArray_(haystack)) { - for (var i = 0; i < haystack.length; i++) { - if (this.equals_(haystack[i], needle)) return true; - } - return false; - } - return haystack.indexOf(needle) >= 0; -}; - -jasmine.Env.prototype.addEqualityTester = function(equalityTester) { - this.equalityTesters_.push(equalityTester); -}; -/** No-op base class for Jasmine reporters. - * - * @constructor - */ -jasmine.Reporter = function() { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportRunnerResults = function(runner) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportSuiteResults = function(suite) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportSpecStarting = function(spec) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportSpecResults = function(spec) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.log = function(str) { -}; - -/** - * Blocks are functions with executable code that make up a spec. - * - * @constructor - * @param {jasmine.Env} env - * @param {Function} func - * @param {jasmine.Spec} spec - */ -jasmine.Block = function(env, func, spec) { - this.env = env; - this.func = func; - this.spec = spec; -}; - -jasmine.Block.prototype.execute = function(onComplete) { - try { - this.func.apply(this.spec); - } catch (e) { - this.spec.fail(e); - } - onComplete(); -}; -/** JavaScript API reporter. - * - * @constructor - */ -jasmine.JsApiReporter = function() { - this.started = false; - this.finished = false; - this.suites_ = []; - this.results_ = {}; -}; - -jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { - this.started = true; - var suites = runner.topLevelSuites(); - for (var i = 0; i < suites.length; i++) { - var suite = suites[i]; - this.suites_.push(this.summarize_(suite)); - } -}; - -jasmine.JsApiReporter.prototype.suites = function() { - return this.suites_; -}; - -jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { - var isSuite = suiteOrSpec instanceof jasmine.Suite; - var summary = { - id: suiteOrSpec.id, - name: suiteOrSpec.description, - type: isSuite ? 'suite' : 'spec', - children: [] - }; - - if (isSuite) { - var children = suiteOrSpec.children(); - for (var i = 0; i < children.length; i++) { - summary.children.push(this.summarize_(children[i])); - } - } - return summary; -}; - -jasmine.JsApiReporter.prototype.results = function() { - return this.results_; -}; - -jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { - return this.results_[specId]; -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { - this.finished = true; -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { - this.results_[spec.id] = { - messages: spec.results().getItems(), - result: spec.results().failedCount > 0 ? "failed" : "passed" - }; -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.log = function(str) { -}; - -jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ - var results = {}; - for (var i = 0; i < specIds.length; i++) { - var specId = specIds[i]; - results[specId] = this.summarizeResult_(this.results_[specId]); - } - return results; -}; - -jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ - var summaryMessages = []; - var messagesLength = result.messages.length; - for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { - var resultMessage = result.messages[messageIndex]; - summaryMessages.push({ - text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, - passed: resultMessage.passed ? resultMessage.passed() : true, - type: resultMessage.type, - message: resultMessage.message, - trace: { - stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined - } - }); - } - - return { - result : result.result, - messages : summaryMessages - }; -}; - -/** - * @constructor - * @param {jasmine.Env} env - * @param actual - * @param {jasmine.Spec} spec - */ -jasmine.Matchers = function(env, actual, spec, opt_isNot) { - this.env = env; - this.actual = actual; - this.spec = spec; - this.isNot = opt_isNot || false; - this.reportWasCalled_ = false; -}; - -// todo: @deprecated as of Jasmine 0.11, remove soon [xw] -jasmine.Matchers.pp = function(str) { - throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); -}; - -// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] -jasmine.Matchers.prototype.report = function(result, failing_message, details) { - throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); -}; - -jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { - for (var methodName in prototype) { - if (methodName == 'report') continue; - var orig = prototype[methodName]; - matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); - } -}; - -jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { - return function() { - var matcherArgs = jasmine.util.argsToArray(arguments); - var result = matcherFunction.apply(this, arguments); - - if (this.isNot) { - result = !result; - } - - if (this.reportWasCalled_) return result; - - var message; - if (!result) { - if (this.message) { - message = this.message.apply(this, arguments); - if (jasmine.isArray_(message)) { - message = message[this.isNot ? 1 : 0]; - } - } else { - var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); - message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; - if (matcherArgs.length > 0) { - for (var i = 0; i < matcherArgs.length; i++) { - if (i > 0) message += ","; - message += " " + jasmine.pp(matcherArgs[i]); - } - } - message += "."; - } - } - var expectationResult = new jasmine.ExpectationResult({ - matcherName: matcherName, - passed: result, - expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], - actual: this.actual, - message: message - }); - this.spec.addMatcherResult(expectationResult); - return jasmine.undefined; - }; -}; - - - - -/** - * toBe: compares the actual to the expected using === - * @param expected - */ -jasmine.Matchers.prototype.toBe = function(expected) { - return this.actual === expected; -}; - -/** - * toNotBe: compares the actual to the expected using !== - * @param expected - * @deprecated as of 1.0. Use not.toBe() instead. - */ -jasmine.Matchers.prototype.toNotBe = function(expected) { - return this.actual !== expected; -}; - -/** - * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. - * - * @param expected - */ -jasmine.Matchers.prototype.toEqual = function(expected) { - return this.env.equals_(this.actual, expected); -}; - -/** - * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual - * @param expected - * @deprecated as of 1.0. Use not.toNotEqual() instead. - */ -jasmine.Matchers.prototype.toNotEqual = function(expected) { - return !this.env.equals_(this.actual, expected); -}; - -/** - * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes - * a pattern or a String. - * - * @param expected - */ -jasmine.Matchers.prototype.toMatch = function(expected) { - return new RegExp(expected).test(this.actual); -}; - -/** - * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch - * @param expected - * @deprecated as of 1.0. Use not.toMatch() instead. - */ -jasmine.Matchers.prototype.toNotMatch = function(expected) { - return !(new RegExp(expected).test(this.actual)); -}; - -/** - * Matcher that compares the actual to jasmine.undefined. - */ -jasmine.Matchers.prototype.toBeDefined = function() { - return (this.actual !== jasmine.undefined); -}; - -/** - * Matcher that compares the actual to jasmine.undefined. - */ -jasmine.Matchers.prototype.toBeUndefined = function() { - return (this.actual === jasmine.undefined); -}; - -/** - * Matcher that compares the actual to null. - */ -jasmine.Matchers.prototype.toBeNull = function() { - return (this.actual === null); -}; - -/** - * Matcher that boolean not-nots the actual. - */ -jasmine.Matchers.prototype.toBeTruthy = function() { - return !!this.actual; -}; - - -/** - * Matcher that boolean nots the actual. - */ -jasmine.Matchers.prototype.toBeFalsy = function() { - return !this.actual; -}; - - -/** - * Matcher that checks to see if the actual, a Jasmine spy, was called. - */ -jasmine.Matchers.prototype.toHaveBeenCalled = function() { - if (arguments.length > 0) { - throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); - } - - if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); - } - - this.message = function() { - return [ - "Expected spy " + this.actual.identity + " to have been called.", - "Expected spy " + this.actual.identity + " not to have been called." - ]; - }; - - return this.actual.wasCalled; -}; - -/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ -jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; - -/** - * Matcher that checks to see if the actual, a Jasmine spy, was not called. - * - * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead - */ -jasmine.Matchers.prototype.wasNotCalled = function() { - if (arguments.length > 0) { - throw new Error('wasNotCalled does not take arguments'); - } - - if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); - } - - this.message = function() { - return [ - "Expected spy " + this.actual.identity + " to not have been called.", - "Expected spy " + this.actual.identity + " to have been called." - ]; - }; - - return !this.actual.wasCalled; -}; - -/** - * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. - * - * @example - * - */ -jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { - var expectedArgs = jasmine.util.argsToArray(arguments); - if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); - } - this.message = function() { - if (this.actual.callCount === 0) { - // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw] - return [ - "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.", - "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was." - ]; - } else { - return [ - "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall), - "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall) - ]; - } - }; - - return this.env.contains_(this.actual.argsForCall, expectedArgs); -}; - -/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ -jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; - -/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ -jasmine.Matchers.prototype.wasNotCalledWith = function() { - var expectedArgs = jasmine.util.argsToArray(arguments); - if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); - } - - this.message = function() { - return [ - "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", - "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" - ]; - }; - - return !this.env.contains_(this.actual.argsForCall, expectedArgs); -}; - -/** - * Matcher that checks that the expected item is an element in the actual Array. - * - * @param {Object} expected - */ -jasmine.Matchers.prototype.toContain = function(expected) { - return this.env.contains_(this.actual, expected); -}; - -/** - * Matcher that checks that the expected item is NOT an element in the actual Array. - * - * @param {Object} expected - * @deprecated as of 1.0. Use not.toNotContain() instead. - */ -jasmine.Matchers.prototype.toNotContain = function(expected) { - return !this.env.contains_(this.actual, expected); -}; - -jasmine.Matchers.prototype.toBeLessThan = function(expected) { - return this.actual < expected; -}; - -jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { - return this.actual > expected; -}; - -/** - * Matcher that checks that the expected item is equal to the actual item - * up to a given level of decimal precision (default 2). - * - * @param {Number} expected - * @param {Number} precision - */ -jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) { - if (!(precision === 0)) { - precision = precision || 2; - } - var multiplier = Math.pow(10, precision); - var actual = Math.round(this.actual * multiplier); - expected = Math.round(expected * multiplier); - return expected == actual; -}; - -/** - * Matcher that checks that the expected exception was thrown by the actual. - * - * @param {String} expected - */ -jasmine.Matchers.prototype.toThrow = function(expected) { - var result = false; - var exception; - if (typeof this.actual != 'function') { - throw new Error('Actual is not a function'); - } - try { - this.actual(); - } catch (e) { - exception = e; - } - if (exception) { - result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); - } - - var not = this.isNot ? "not " : ""; - - this.message = function() { - if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { - return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' '); - } else { - return "Expected function to throw an exception."; - } - }; - - return result; -}; - -jasmine.Matchers.Any = function(expectedClass) { - this.expectedClass = expectedClass; -}; - -jasmine.Matchers.Any.prototype.matches = function(other) { - if (this.expectedClass == String) { - return typeof other == 'string' || other instanceof String; - } - - if (this.expectedClass == Number) { - return typeof other == 'number' || other instanceof Number; - } - - if (this.expectedClass == Function) { - return typeof other == 'function' || other instanceof Function; - } - - if (this.expectedClass == Object) { - return typeof other == 'object'; - } - - return other instanceof this.expectedClass; -}; - -jasmine.Matchers.Any.prototype.toString = function() { - return ''; -}; - -jasmine.Matchers.ObjectContaining = function (sample) { - this.sample = sample; -}; - -jasmine.Matchers.ObjectContaining.prototype.matches = function(other, mismatchKeys, mismatchValues) { - mismatchKeys = mismatchKeys || []; - mismatchValues = mismatchValues || []; - - var env = jasmine.getEnv(); - - var hasKey = function(obj, keyName) { - return obj != null && obj[keyName] !== jasmine.undefined; - }; - - for (var property in this.sample) { - if (!hasKey(other, property) && hasKey(this.sample, property)) { - mismatchKeys.push("expected has key '" + property + "', but missing from actual."); - } - else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) { - mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual."); - } - } - - return (mismatchKeys.length === 0 && mismatchValues.length === 0); -}; - -jasmine.Matchers.ObjectContaining.prototype.toString = function () { - return ""; -}; -/** - * @constructor - */ -jasmine.MultiReporter = function() { - this.subReporters_ = []; -}; -jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); - -jasmine.MultiReporter.prototype.addReporter = function(reporter) { - this.subReporters_.push(reporter); -}; - -(function() { - var functionNames = [ - "reportRunnerStarting", - "reportRunnerResults", - "reportSuiteResults", - "reportSpecStarting", - "reportSpecResults", - "log" - ]; - for (var i = 0; i < functionNames.length; i++) { - var functionName = functionNames[i]; - jasmine.MultiReporter.prototype[functionName] = (function(functionName) { - return function() { - for (var j = 0; j < this.subReporters_.length; j++) { - var subReporter = this.subReporters_[j]; - if (subReporter[functionName]) { - subReporter[functionName].apply(subReporter, arguments); - } - } - }; - })(functionName); - } -})(); -/** - * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults - * - * @constructor - */ -jasmine.NestedResults = function() { - /** - * The total count of results - */ - this.totalCount = 0; - /** - * Number of passed results - */ - this.passedCount = 0; - /** - * Number of failed results - */ - this.failedCount = 0; - /** - * Was this suite/spec skipped? - */ - this.skipped = false; - /** - * @ignore - */ - this.items_ = []; -}; - -/** - * Roll up the result counts. - * - * @param result - */ -jasmine.NestedResults.prototype.rollupCounts = function(result) { - this.totalCount += result.totalCount; - this.passedCount += result.passedCount; - this.failedCount += result.failedCount; -}; - -/** - * Adds a log message. - * @param values Array of message parts which will be concatenated later. - */ -jasmine.NestedResults.prototype.log = function(values) { - this.items_.push(new jasmine.MessageResult(values)); -}; - -/** - * Getter for the results: message & results. - */ -jasmine.NestedResults.prototype.getItems = function() { - return this.items_; -}; - -/** - * Adds a result, tracking counts (total, passed, & failed) - * @param {jasmine.ExpectationResult|jasmine.NestedResults} result - */ -jasmine.NestedResults.prototype.addResult = function(result) { - if (result.type != 'log') { - if (result.items_) { - this.rollupCounts(result); - } else { - this.totalCount++; - if (result.passed()) { - this.passedCount++; - } else { - this.failedCount++; - } - } - } - this.items_.push(result); -}; - -/** - * @returns {Boolean} True if everything below passed - */ -jasmine.NestedResults.prototype.passed = function() { - return this.passedCount === this.totalCount; -}; -/** - * Base class for pretty printing for expectation results. - */ -jasmine.PrettyPrinter = function() { - this.ppNestLevel_ = 0; -}; - -/** - * Formats a value in a nice, human-readable string. - * - * @param value - */ -jasmine.PrettyPrinter.prototype.format = function(value) { - if (this.ppNestLevel_ > 40) { - throw new Error('jasmine.PrettyPrinter: format() nested too deeply!'); - } - - this.ppNestLevel_++; - try { - if (value === jasmine.undefined) { - this.emitScalar('undefined'); - } else if (value === null) { - this.emitScalar('null'); - } else if (value === jasmine.getGlobal()) { - this.emitScalar(''); - } else if (value instanceof jasmine.Matchers.Any) { - this.emitScalar(value.toString()); - } else if (typeof value === 'string') { - this.emitString(value); - } else if (jasmine.isSpy(value)) { - this.emitScalar("spy on " + value.identity); - } else if (value instanceof RegExp) { - this.emitScalar(value.toString()); - } else if (typeof value === 'function') { - this.emitScalar('Function'); - } else if (typeof value.nodeType === 'number') { - this.emitScalar('HTMLNode'); - } else if (value instanceof Date) { - this.emitScalar('Date(' + value + ')'); - } else if (value.__Jasmine_been_here_before__) { - this.emitScalar(''); - } else if (jasmine.isArray_(value) || typeof value == 'object') { - value.__Jasmine_been_here_before__ = true; - if (jasmine.isArray_(value)) { - this.emitArray(value); - } else { - this.emitObject(value); - } - delete value.__Jasmine_been_here_before__; - } else { - this.emitScalar(value.toString()); - } - } finally { - this.ppNestLevel_--; - } -}; - -jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { - for (var property in obj) { - if (property == '__Jasmine_been_here_before__') continue; - fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && - obj.__lookupGetter__(property) !== null) : false); - } -}; - -jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; -jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; -jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; -jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; - -jasmine.StringPrettyPrinter = function() { - jasmine.PrettyPrinter.call(this); - - this.string = ''; -}; -jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); - -jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { - this.append(value); -}; - -jasmine.StringPrettyPrinter.prototype.emitString = function(value) { - this.append("'" + value + "'"); -}; - -jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { - this.append('[ '); - for (var i = 0; i < array.length; i++) { - if (i > 0) { - this.append(', '); - } - this.format(array[i]); - } - this.append(' ]'); -}; - -jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { - var self = this; - this.append('{ '); - var first = true; - - this.iterateObject(obj, function(property, isGetter) { - if (first) { - first = false; - } else { - self.append(', '); - } - - self.append(property); - self.append(' : '); - if (isGetter) { - self.append(''); - } else { - self.format(obj[property]); - } - }); - - this.append(' }'); -}; - -jasmine.StringPrettyPrinter.prototype.append = function(value) { - this.string += value; -}; -jasmine.Queue = function(env) { - this.env = env; - this.blocks = []; - this.running = false; - this.index = 0; - this.offset = 0; - this.abort = false; -}; - -jasmine.Queue.prototype.addBefore = function(block) { - this.blocks.unshift(block); -}; - -jasmine.Queue.prototype.add = function(block) { - this.blocks.push(block); -}; - -jasmine.Queue.prototype.insertNext = function(block) { - this.blocks.splice((this.index + this.offset + 1), 0, block); - this.offset++; -}; - -jasmine.Queue.prototype.start = function(onComplete) { - this.running = true; - this.onComplete = onComplete; - this.next_(); -}; - -jasmine.Queue.prototype.isRunning = function() { - return this.running; -}; - -jasmine.Queue.LOOP_DONT_RECURSE = true; - -jasmine.Queue.prototype.next_ = function() { - var self = this; - var goAgain = true; - - while (goAgain) { - goAgain = false; - - if (self.index < self.blocks.length && !this.abort) { - var calledSynchronously = true; - var completedSynchronously = false; - - var onComplete = function () { - if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { - completedSynchronously = true; - return; - } - - if (self.blocks[self.index].abort) { - self.abort = true; - } - - self.offset = 0; - self.index++; - - var now = new Date().getTime(); - if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { - self.env.lastUpdate = now; - self.env.setTimeout(function() { - self.next_(); - }, 0); - } else { - if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { - goAgain = true; - } else { - self.next_(); - } - } - }; - self.blocks[self.index].execute(onComplete); - - calledSynchronously = false; - if (completedSynchronously) { - onComplete(); - } - - } else { - self.running = false; - if (self.onComplete) { - self.onComplete(); - } - } - } -}; - -jasmine.Queue.prototype.results = function() { - var results = new jasmine.NestedResults(); - for (var i = 0; i < this.blocks.length; i++) { - if (this.blocks[i].results) { - results.addResult(this.blocks[i].results()); - } - } - return results; -}; - - -/** - * Runner - * - * @constructor - * @param {jasmine.Env} env - */ -jasmine.Runner = function(env) { - var self = this; - self.env = env; - self.queue = new jasmine.Queue(env); - self.before_ = []; - self.after_ = []; - self.suites_ = []; -}; - -jasmine.Runner.prototype.execute = function() { - var self = this; - if (self.env.reporter.reportRunnerStarting) { - self.env.reporter.reportRunnerStarting(this); - } - self.queue.start(function () { - self.finishCallback(); - }); -}; - -jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { - beforeEachFunction.typeName = 'beforeEach'; - this.before_.splice(0,0,beforeEachFunction); -}; - -jasmine.Runner.prototype.afterEach = function(afterEachFunction) { - afterEachFunction.typeName = 'afterEach'; - this.after_.splice(0,0,afterEachFunction); -}; - - -jasmine.Runner.prototype.finishCallback = function() { - this.env.reporter.reportRunnerResults(this); -}; - -jasmine.Runner.prototype.addSuite = function(suite) { - this.suites_.push(suite); -}; - -jasmine.Runner.prototype.add = function(block) { - if (block instanceof jasmine.Suite) { - this.addSuite(block); - } - this.queue.add(block); -}; - -jasmine.Runner.prototype.specs = function () { - var suites = this.suites(); - var specs = []; - for (var i = 0; i < suites.length; i++) { - specs = specs.concat(suites[i].specs()); - } - return specs; -}; - -jasmine.Runner.prototype.suites = function() { - return this.suites_; -}; - -jasmine.Runner.prototype.topLevelSuites = function() { - var topLevelSuites = []; - for (var i = 0; i < this.suites_.length; i++) { - if (!this.suites_[i].parentSuite) { - topLevelSuites.push(this.suites_[i]); - } - } - return topLevelSuites; -}; - -jasmine.Runner.prototype.results = function() { - return this.queue.results(); -}; -/** - * Internal representation of a Jasmine specification, or test. - * - * @constructor - * @param {jasmine.Env} env - * @param {jasmine.Suite} suite - * @param {String} description - */ -jasmine.Spec = function(env, suite, description) { - if (!env) { - throw new Error('jasmine.Env() required'); - } - if (!suite) { - throw new Error('jasmine.Suite() required'); - } - var spec = this; - spec.id = env.nextSpecId ? env.nextSpecId() : null; - spec.env = env; - spec.suite = suite; - spec.description = description; - spec.queue = new jasmine.Queue(env); - - spec.afterCallbacks = []; - spec.spies_ = []; - - spec.results_ = new jasmine.NestedResults(); - spec.results_.description = description; - spec.matchersClass = null; -}; - -jasmine.Spec.prototype.getFullName = function() { - return this.suite.getFullName() + ' ' + this.description + '.'; -}; - - -jasmine.Spec.prototype.results = function() { - return this.results_; -}; - -/** - * All parameters are pretty-printed and concatenated together, then written to the spec's output. - * - * Be careful not to leave calls to jasmine.log in production code. - */ -jasmine.Spec.prototype.log = function() { - return this.results_.log(arguments); -}; - -jasmine.Spec.prototype.runs = function (func) { - var block = new jasmine.Block(this.env, func, this); - this.addToQueue(block); - return this; -}; - -jasmine.Spec.prototype.addToQueue = function (block) { - if (this.queue.isRunning()) { - this.queue.insertNext(block); - } else { - this.queue.add(block); - } -}; - -/** - * @param {jasmine.ExpectationResult} result - */ -jasmine.Spec.prototype.addMatcherResult = function(result) { - this.results_.addResult(result); -}; - -jasmine.Spec.prototype.expect = function(actual) { - var positive = new (this.getMatchersClass_())(this.env, actual, this); - positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); - return positive; -}; - -/** - * Waits a fixed time period before moving to the next block. - * - * @deprecated Use waitsFor() instead - * @param {Number} timeout milliseconds to wait - */ -jasmine.Spec.prototype.waits = function(timeout) { - var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); - this.addToQueue(waitsFunc); - return this; -}; - -/** - * Waits for the latchFunction to return true before proceeding to the next block. - * - * @param {Function} latchFunction - * @param {String} optional_timeoutMessage - * @param {Number} optional_timeout - */ -jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { - var latchFunction_ = null; - var optional_timeoutMessage_ = null; - var optional_timeout_ = null; - - for (var i = 0; i < arguments.length; i++) { - var arg = arguments[i]; - switch (typeof arg) { - case 'function': - latchFunction_ = arg; - break; - case 'string': - optional_timeoutMessage_ = arg; - break; - case 'number': - optional_timeout_ = arg; - break; - } - } - - var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); - this.addToQueue(waitsForFunc); - return this; -}; - -jasmine.Spec.prototype.fail = function (e) { - var expectationResult = new jasmine.ExpectationResult({ - passed: false, - message: e ? jasmine.util.formatException(e) : 'Exception', - trace: { stack: e.stack } - }); - this.results_.addResult(expectationResult); -}; - -jasmine.Spec.prototype.getMatchersClass_ = function() { - return this.matchersClass || this.env.matchersClass; -}; - -jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { - var parent = this.getMatchersClass_(); - var newMatchersClass = function() { - parent.apply(this, arguments); - }; - jasmine.util.inherit(newMatchersClass, parent); - jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); - this.matchersClass = newMatchersClass; -}; - -jasmine.Spec.prototype.finishCallback = function() { - this.env.reporter.reportSpecResults(this); -}; - -jasmine.Spec.prototype.finish = function(onComplete) { - this.removeAllSpies(); - this.finishCallback(); - if (onComplete) { - onComplete(); - } -}; - -jasmine.Spec.prototype.after = function(doAfter) { - if (this.queue.isRunning()) { - this.queue.add(new jasmine.Block(this.env, doAfter, this)); - } else { - this.afterCallbacks.unshift(doAfter); - } -}; - -jasmine.Spec.prototype.execute = function(onComplete) { - var spec = this; - if (!spec.env.specFilter(spec)) { - spec.results_.skipped = true; - spec.finish(onComplete); - return; - } - - this.env.reporter.reportSpecStarting(this); - - spec.env.currentSpec = spec; - - spec.addBeforesAndAftersToQueue(); - - spec.queue.start(function () { - spec.finish(onComplete); - }); -}; - -jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { - var runner = this.env.currentRunner(); - var i; - - for (var suite = this.suite; suite; suite = suite.parentSuite) { - for (i = 0; i < suite.before_.length; i++) { - this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); - } - } - for (i = 0; i < runner.before_.length; i++) { - this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); - } - for (i = 0; i < this.afterCallbacks.length; i++) { - this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this)); - } - for (suite = this.suite; suite; suite = suite.parentSuite) { - for (i = 0; i < suite.after_.length; i++) { - this.queue.add(new jasmine.Block(this.env, suite.after_[i], this)); - } - } - for (i = 0; i < runner.after_.length; i++) { - this.queue.add(new jasmine.Block(this.env, runner.after_[i], this)); - } -}; - -jasmine.Spec.prototype.explodes = function() { - throw 'explodes function should not have been called'; -}; - -jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { - if (obj == jasmine.undefined) { - throw "spyOn could not find an object to spy upon for " + methodName + "()"; - } - - if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { - throw methodName + '() method does not exist'; - } - - if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { - throw new Error(methodName + ' has already been spied upon'); - } - - var spyObj = jasmine.createSpy(methodName); - - this.spies_.push(spyObj); - spyObj.baseObj = obj; - spyObj.methodName = methodName; - spyObj.originalValue = obj[methodName]; - - obj[methodName] = spyObj; - - return spyObj; -}; - -jasmine.Spec.prototype.removeAllSpies = function() { - for (var i = 0; i < this.spies_.length; i++) { - var spy = this.spies_[i]; - spy.baseObj[spy.methodName] = spy.originalValue; - } - this.spies_ = []; -}; - -/** - * Internal representation of a Jasmine suite. - * - * @constructor - * @param {jasmine.Env} env - * @param {String} description - * @param {Function} specDefinitions - * @param {jasmine.Suite} parentSuite - */ -jasmine.Suite = function(env, description, specDefinitions, parentSuite) { - var self = this; - self.id = env.nextSuiteId ? env.nextSuiteId() : null; - self.description = description; - self.queue = new jasmine.Queue(env); - self.parentSuite = parentSuite; - self.env = env; - self.before_ = []; - self.after_ = []; - self.children_ = []; - self.suites_ = []; - self.specs_ = []; -}; - -jasmine.Suite.prototype.getFullName = function() { - var fullName = this.description; - for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { - fullName = parentSuite.description + ' ' + fullName; - } - return fullName; -}; - -jasmine.Suite.prototype.finish = function(onComplete) { - this.env.reporter.reportSuiteResults(this); - this.finished = true; - if (typeof(onComplete) == 'function') { - onComplete(); - } -}; - -jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { - beforeEachFunction.typeName = 'beforeEach'; - this.before_.unshift(beforeEachFunction); -}; - -jasmine.Suite.prototype.afterEach = function(afterEachFunction) { - afterEachFunction.typeName = 'afterEach'; - this.after_.unshift(afterEachFunction); -}; - -jasmine.Suite.prototype.results = function() { - return this.queue.results(); -}; - -jasmine.Suite.prototype.add = function(suiteOrSpec) { - this.children_.push(suiteOrSpec); - if (suiteOrSpec instanceof jasmine.Suite) { - this.suites_.push(suiteOrSpec); - this.env.currentRunner().addSuite(suiteOrSpec); - } else { - this.specs_.push(suiteOrSpec); - } - this.queue.add(suiteOrSpec); -}; - -jasmine.Suite.prototype.specs = function() { - return this.specs_; -}; - -jasmine.Suite.prototype.suites = function() { - return this.suites_; -}; - -jasmine.Suite.prototype.children = function() { - return this.children_; -}; - -jasmine.Suite.prototype.execute = function(onComplete) { - var self = this; - this.queue.start(function () { - self.finish(onComplete); - }); -}; -jasmine.WaitsBlock = function(env, timeout, spec) { - this.timeout = timeout; - jasmine.Block.call(this, env, null, spec); -}; - -jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); - -jasmine.WaitsBlock.prototype.execute = function (onComplete) { - if (jasmine.VERBOSE) { - this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); - } - this.env.setTimeout(function () { - onComplete(); - }, this.timeout); -}; -/** - * A block which waits for some condition to become true, with timeout. - * - * @constructor - * @extends jasmine.Block - * @param {jasmine.Env} env The Jasmine environment. - * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. - * @param {Function} latchFunction A function which returns true when the desired condition has been met. - * @param {String} message The message to display if the desired condition hasn't been met within the given time period. - * @param {jasmine.Spec} spec The Jasmine spec. - */ -jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { - this.timeout = timeout || env.defaultTimeoutInterval; - this.latchFunction = latchFunction; - this.message = message; - this.totalTimeSpentWaitingForLatch = 0; - jasmine.Block.call(this, env, null, spec); -}; -jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); - -jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; - -jasmine.WaitsForBlock.prototype.execute = function(onComplete) { - if (jasmine.VERBOSE) { - this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); - } - var latchFunctionResult; - try { - latchFunctionResult = this.latchFunction.apply(this.spec); - } catch (e) { - this.spec.fail(e); - onComplete(); - return; - } - - if (latchFunctionResult) { - onComplete(); - } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { - var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); - this.spec.fail({ - name: 'timeout', - message: message - }); - - this.abort = true; - onComplete(); - } else { - this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; - var self = this; - this.env.setTimeout(function() { - self.execute(onComplete); - }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); - } -}; -// Mock setTimeout, clearTimeout -// Contributed by Pivotal Computer Systems, www.pivotalsf.com - -jasmine.FakeTimer = function() { - this.reset(); - - var self = this; - self.setTimeout = function(funcToCall, millis) { - self.timeoutsMade++; - self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); - return self.timeoutsMade; - }; - - self.setInterval = function(funcToCall, millis) { - self.timeoutsMade++; - self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); - return self.timeoutsMade; - }; - - self.clearTimeout = function(timeoutKey) { - self.scheduledFunctions[timeoutKey] = jasmine.undefined; - }; - - self.clearInterval = function(timeoutKey) { - self.scheduledFunctions[timeoutKey] = jasmine.undefined; - }; - -}; - -jasmine.FakeTimer.prototype.reset = function() { - this.timeoutsMade = 0; - this.scheduledFunctions = {}; - this.nowMillis = 0; -}; - -jasmine.FakeTimer.prototype.tick = function(millis) { - var oldMillis = this.nowMillis; - var newMillis = oldMillis + millis; - this.runFunctionsWithinRange(oldMillis, newMillis); - this.nowMillis = newMillis; -}; - -jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { - var scheduledFunc; - var funcsToRun = []; - for (var timeoutKey in this.scheduledFunctions) { - scheduledFunc = this.scheduledFunctions[timeoutKey]; - if (scheduledFunc != jasmine.undefined && - scheduledFunc.runAtMillis >= oldMillis && - scheduledFunc.runAtMillis <= nowMillis) { - funcsToRun.push(scheduledFunc); - this.scheduledFunctions[timeoutKey] = jasmine.undefined; - } - } - - if (funcsToRun.length > 0) { - funcsToRun.sort(function(a, b) { - return a.runAtMillis - b.runAtMillis; - }); - for (var i = 0; i < funcsToRun.length; ++i) { - try { - var funcToRun = funcsToRun[i]; - this.nowMillis = funcToRun.runAtMillis; - funcToRun.funcToCall(); - if (funcToRun.recurring) { - this.scheduleFunction(funcToRun.timeoutKey, - funcToRun.funcToCall, - funcToRun.millis, - true); - } - } catch(e) { - } - } - this.runFunctionsWithinRange(oldMillis, nowMillis); - } -}; - -jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { - this.scheduledFunctions[timeoutKey] = { - runAtMillis: this.nowMillis + millis, - funcToCall: funcToCall, - recurring: recurring, - timeoutKey: timeoutKey, - millis: millis - }; -}; - -/** - * @namespace - */ -jasmine.Clock = { - defaultFakeTimer: new jasmine.FakeTimer(), - - reset: function() { - jasmine.Clock.assertInstalled(); - jasmine.Clock.defaultFakeTimer.reset(); - }, - - tick: function(millis) { - jasmine.Clock.assertInstalled(); - jasmine.Clock.defaultFakeTimer.tick(millis); - }, - - runFunctionsWithinRange: function(oldMillis, nowMillis) { - jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); - }, - - scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { - jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); - }, - - useMock: function() { - if (!jasmine.Clock.isInstalled()) { - var spec = jasmine.getEnv().currentSpec; - spec.after(jasmine.Clock.uninstallMock); - - jasmine.Clock.installMock(); - } - }, - - installMock: function() { - jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; - }, - - uninstallMock: function() { - jasmine.Clock.assertInstalled(); - jasmine.Clock.installed = jasmine.Clock.real; - }, - - real: { - setTimeout: jasmine.getGlobal().setTimeout, - clearTimeout: jasmine.getGlobal().clearTimeout, - setInterval: jasmine.getGlobal().setInterval, - clearInterval: jasmine.getGlobal().clearInterval - }, - - assertInstalled: function() { - if (!jasmine.Clock.isInstalled()) { - throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); - } - }, - - isInstalled: function() { - return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; - }, - - installed: null -}; -jasmine.Clock.installed = jasmine.Clock.real; - -//else for IE support -jasmine.getGlobal().setTimeout = function(funcToCall, millis) { - if (jasmine.Clock.installed.setTimeout.apply) { - return jasmine.Clock.installed.setTimeout.apply(this, arguments); - } else { - return jasmine.Clock.installed.setTimeout(funcToCall, millis); - } -}; - -jasmine.getGlobal().setInterval = function(funcToCall, millis) { - if (jasmine.Clock.installed.setInterval.apply) { - return jasmine.Clock.installed.setInterval.apply(this, arguments); - } else { - return jasmine.Clock.installed.setInterval(funcToCall, millis); - } -}; - -jasmine.getGlobal().clearTimeout = function(timeoutKey) { - if (jasmine.Clock.installed.clearTimeout.apply) { - return jasmine.Clock.installed.clearTimeout.apply(this, arguments); - } else { - return jasmine.Clock.installed.clearTimeout(timeoutKey); - } -}; - -jasmine.getGlobal().clearInterval = function(timeoutKey) { - if (jasmine.Clock.installed.clearTimeout.apply) { - return jasmine.Clock.installed.clearInterval.apply(this, arguments); - } else { - return jasmine.Clock.installed.clearInterval(timeoutKey); - } -}; - -jasmine.version_= { - "major": 1, - "minor": 1, - "build": 0, - "revision": 1299963843 -}; diff --git a/public/js/vendor/jscolor/arrow.gif b/public/js/vendor/jscolor/arrow.gif deleted file mode 100644 index 246478a..0000000 Binary files a/public/js/vendor/jscolor/arrow.gif and /dev/null differ diff --git a/public/js/vendor/jscolor/cross.gif b/public/js/vendor/jscolor/cross.gif deleted file mode 100644 index 0ee9c7a..0000000 Binary files a/public/js/vendor/jscolor/cross.gif and /dev/null differ diff --git a/public/js/vendor/jscolor/hs.png b/public/js/vendor/jscolor/hs.png deleted file mode 100644 index 1d7da8d..0000000 Binary files a/public/js/vendor/jscolor/hs.png and /dev/null differ diff --git a/public/js/vendor/jscolor/hv.png b/public/js/vendor/jscolor/hv.png deleted file mode 100644 index 6bc4937..0000000 Binary files a/public/js/vendor/jscolor/hv.png and /dev/null differ diff --git a/public/js/vendor/jscolor/jscolor.css b/public/js/vendor/jscolor/jscolor.css deleted file mode 100644 index df4c329..0000000 --- a/public/js/vendor/jscolor/jscolor.css +++ /dev/null @@ -1,15 +0,0 @@ -.jscolor .saturation .pad { - background: url('hs.png') no-repeat; -} - -.jscolor .value .pad { - background: url('hv.png') no-repeat; -} - -.jscolor .cross { - background: url('cross.gif') no-repeat; -} - -.jscolor .arrow { - background: url('arrow.gif') no-repeat; -} \ No newline at end of file diff --git a/public/js/vendor/jscolor/jscolor.js b/public/js/vendor/jscolor/jscolor.js deleted file mode 100644 index 2a70007..0000000 --- a/public/js/vendor/jscolor/jscolor.js +++ /dev/null @@ -1,919 +0,0 @@ -/** - * jscolor, JavaScript Color Picker - * - * @version 1.3.13 - * @license GNU Lesser General Public License, http://www.gnu.org/copyleft/lesser.html - * @author Jan Odvarko, http://odvarko.cz - * @created 2008-06-15 - * @updated 2012-01-19 - * @link http://jscolor.com - */ - - -var jscolor = { - - - dir : '', // location of jscolor directory (leave empty to autodetect) - bindClass : 'color', // class name - binding : true, // automatic binding via - preloading : true, // use image preloading? - - - install : function() { - jscolor.addEvent(window, 'load', jscolor.init); - }, - - - init : function() { - if(jscolor.binding) { - jscolor.bind(); - } - if(jscolor.preloading) { - jscolor.preload(); - } - }, - - - getDir : function() { - if(!jscolor.dir) { - var detected = jscolor.detectDir(); - jscolor.dir = detected!==false ? detected : 'jscolor/'; - } - return jscolor.dir; - }, - - - detectDir : function() { - var base = location.href; - - var e = document.getElementsByTagName('base'); - for(var i=0; i vs[a] ? - (-vp[a]+tp[a]+ts[a]/2 > vs[a]/2 && tp[a]+ts[a]-ps[a] >= 0 ? tp[a]+ts[a]-ps[a] : tp[a]) : - tp[a], - -vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ? - (-vp[b]+tp[b]+ts[b]/2 > vs[b]/2 && tp[b]+ts[b]-l-l*c >= 0 ? tp[b]+ts[b]-l-l*c : tp[b]+ts[b]-l+l*c) : - (tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c) - ]; - } - drawPicker(pp[a], pp[b]); - } - }; - - - this.importColor = function() { - if(!valueElement) { - this.exportColor(); - } else { - if(!this.adjust) { - if(!this.fromString(valueElement.value, leaveValue)) { - styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage; - styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor; - styleElement.style.color = styleElement.jscStyle.color; - this.exportColor(leaveValue | leaveStyle); - } - } else if(!this.required && /^\s*$/.test(valueElement.value)) { - valueElement.value = ''; - styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage; - styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor; - styleElement.style.color = styleElement.jscStyle.color; - this.exportColor(leaveValue | leaveStyle); - - } else if(this.fromString(valueElement.value)) { - // OK - } else { - this.exportColor(); - } - } - }; - - - this.exportColor = function(flags) { - if(!(flags & leaveValue) && valueElement) { - var value = this.toString(); - if(this.caps) { value = value.toUpperCase(); } - if(this.hash) { value = '#'+value; } - valueElement.value = value; - } - if(!(flags & leaveStyle) && styleElement) { - styleElement.style.backgroundImage = "none"; - styleElement.style.backgroundColor = - '#'+this.toString(); - styleElement.style.color = - 0.213 * this.rgb[0] + - 0.715 * this.rgb[1] + - 0.072 * this.rgb[2] - < 0.5 ? '#FFF' : '#000'; - } - if(!(flags & leavePad) && isPickerOwner()) { - redrawPad(); - } - if(!(flags & leaveSld) && isPickerOwner()) { - redrawSld(); - } - }; - - - this.fromHSV = function(h, s, v, flags) { // null = don't change - h<0 && (h=0) || h>6 && (h=6); - s<0 && (s=0) || s>1 && (s=1); - v<0 && (v=0) || v>1 && (v=1); - this.rgb = HSV_RGB( - h===null ? this.hsv[0] : (this.hsv[0]=h), - s===null ? this.hsv[1] : (this.hsv[1]=s), - v===null ? this.hsv[2] : (this.hsv[2]=v) - ); - this.exportColor(flags); - }; - - - this.fromRGB = function(r, g, b, flags) { // null = don't change - r<0 && (r=0) || r>1 && (r=1); - g<0 && (g=0) || g>1 && (g=1); - b<0 && (b=0) || b>1 && (b=1); - var hsv = RGB_HSV( - r===null ? this.rgb[0] : (this.rgb[0]=r), - g===null ? this.rgb[1] : (this.rgb[1]=g), - b===null ? this.rgb[2] : (this.rgb[2]=b) - ); - if(hsv[0] !== null) { - this.hsv[0] = hsv[0]; - } - if(hsv[2] !== 0) { - this.hsv[1] = hsv[1]; - } - this.hsv[2] = hsv[2]; - this.exportColor(flags); - }; - - - this.fromString = function(hex, flags) { - var m = hex.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i); - if(!m) { - return false; - } else { - if(m[1].length === 6) { // 6-char notation - this.fromRGB( - parseInt(m[1].substr(0,2),16) / 255, - parseInt(m[1].substr(2,2),16) / 255, - parseInt(m[1].substr(4,2),16) / 255, - flags - ); - } else { // 3-char notation - this.fromRGB( - parseInt(m[1].charAt(0)+m[1].charAt(0),16) / 255, - parseInt(m[1].charAt(1)+m[1].charAt(1),16) / 255, - parseInt(m[1].charAt(2)+m[1].charAt(2),16) / 255, - flags - ); - } - return true; - } - }; - - - this.toString = function() { - return ( - (0x100 | Math.round(255*this.rgb[0])).toString(16).substr(1) + - (0x100 | Math.round(255*this.rgb[1])).toString(16).substr(1) + - (0x100 | Math.round(255*this.rgb[2])).toString(16).substr(1) - ); - }; - - - function RGB_HSV(r, g, b) { - var n = Math.min(Math.min(r,g),b); - var v = Math.max(Math.max(r,g),b); - var m = v - n; - if(m === 0) { return [ null, 0, v ]; } - var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m); - return [ h===6?0:h, m/v, v ]; - } - - - function HSV_RGB(h, s, v) { - if(h === null) { return [ v, v, v ]; } - var i = Math.floor(h); - var f = i%2 ? h-i : 1-(h-i); - var m = v * (1 - s); - var n = v * (1 - s*f); - switch(i) { - case 6: - case 0: return [v,n,m]; - case 1: return [n,v,m]; - case 2: return [m,v,n]; - case 3: return [m,n,v]; - case 4: return [n,m,v]; - case 5: return [v,m,n]; - } - } - - - function removePicker() { - delete jscolor.picker.owner; - document.getElementsByTagName('body')[0].removeChild(jscolor.picker.boxB); - } - - - function drawPicker(x, y) { - if(!jscolor.picker) { - jscolor.picker = { - box : document.createElement('div'), - boxB : document.createElement('div'), - pad : document.createElement('div'), - padB : document.createElement('div'), - padM : document.createElement('div'), - sld : document.createElement('div'), - sldB : document.createElement('div'), - sldM : document.createElement('div'), - btn : document.createElement('div'), - btnS : document.createElement('span'), - btnT : document.createTextNode(THIS.pickerCloseText) - }; - for(var i=0,segSize=4; i< 2 ? insetColors[0] : insetColors[1] + ' ' + insetColors[0] + ' ' + insetColors[0] + ' ' + insetColors[1]; - p.btn.style.borderColor = pickerOutsetColor; - } - p.btn.style.display = THIS.pickerClosable ? 'block' : 'none'; - p.btn.style.position = 'absolute'; - p.btn.style.left = THIS.pickerFace + 'px'; - p.btn.style.bottom = THIS.pickerFace + 'px'; - p.btn.style.padding = '0 15px'; - p.btn.style.height = '18px'; - p.btn.style.border = THIS.pickerInset + 'px solid'; - setBtnBorder(); - p.btn.style.color = THIS.pickerButtonColor; - p.btn.style.font = '12px sans-serif'; - p.btn.style.textAlign = 'center'; - try { - p.btn.style.cursor = 'pointer'; - } catch(eOldIE) { - p.btn.style.cursor = 'hand'; - } - p.btn.onmousedown = function () { - THIS.hidePicker(); - }; - p.btnS.style.lineHeight = p.btn.style.height; - - // place pointers - redrawPad(); - redrawSld(); - - jscolor.picker.owner = THIS; - document.getElementsByTagName('body')[0].appendChild(p.boxB); - } - - - function getPickerDims(o) { - var dims = [ - 2*o.pickerInset + 2*o.pickerFace + jscolor.images.pad[0] + - (o.slider ? 2*o.pickerInset + 2*jscolor.images.arrow[0] + jscolor.images.sld[0] : 0), - o.pickerClosable ? - 4*o.pickerInset + 3*o.pickerFace + jscolor.images.pad[1] + o.pickerButtonHeight : - 2*o.pickerInset + 2*o.pickerFace + jscolor.images.pad[1] - ]; - return dims; - } - - - function redrawPad() { - // redraw the pad pointer - switch(modeID) { - case 0: var yComponent = 1; break; - case 1: var yComponent = 2; break; - } - var x = Math.round((THIS.hsv[0]/6) * (jscolor.images.pad[0]-1)); - var y = Math.round((1-THIS.hsv[yComponent]) * (jscolor.images.pad[1]-1)); - jscolor.picker.padM.style.backgroundPosition = - (THIS.pickerFace+THIS.pickerInset+x - Math.floor(jscolor.images.cross[0]/2)) + 'px ' + - (THIS.pickerFace+THIS.pickerInset+y - Math.floor(jscolor.images.cross[1]/2)) + 'px'; - - // redraw the slider image - var seg = jscolor.picker.sld.childNodes; - - switch(modeID) { - case 0: - var rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 1); - for(var i=0; i -1; +} + +var CSSCore = { + + /** + * Adds the class passed in to the element if it doesn't already have it. + * + * @param {DOMElement} element the element to set the class on + * @param {string} className the CSS className + * @return {DOMElement} the element passed in + */ + addClass: function(element, className) { + invariant( + !/\s/.test(className), + 'CSSCore.addClass takes only a single class name. "%s" contains ' + + 'multiple classes.', className + ); + + if (className) { + if (element.classList) { + element.classList.add(className); + } else if (!hasClass(element, className)) { + element.className = element.className + ' ' + className; + } + } + return element; + }, + + /** + * Removes the class passed in from the element + * + * @param {DOMElement} element the element to set the class on + * @param {string} className the CSS className + * @return {DOMElement} the element passed in + */ + removeClass: function(element, className) { + invariant( + !/\s/.test(className), + 'CSSCore.removeClass takes only a single class name. "%s" contains ' + + 'multiple classes.', className + ); + + if (className) { + if (element.classList) { + element.classList.remove(className); + } else if (hasClass(element, className)) { + element.className = element.className + .replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)', 'g'), '$1') + .replace(/\s+/g, ' ') // multiple spaces to one + .replace(/^\s*|\s*$/g, ''); // trim the ends + } + } + return element; + }, + + /** + * Helper to add or remove a class from an element based on a condition. + * + * @param {DOMElement} element the element to set the class on + * @param {string} className the CSS className + * @param {*} bool condition to whether to add or remove the class + * @return {DOMElement} the element passed in + */ + conditionClass: function(element, className, bool) { + return (bool ? CSSCore.addClass : CSSCore.removeClass)(element, className); + } +}; + +module.exports = CSSCore; + +},{"./invariant":106}],3:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule CSSProperty + */ + +"use strict"; + +/** + * CSS properties which accept numbers but are not in units of "px". + */ +var isUnitlessNumber = { + fillOpacity: true, + fontWeight: true, + lineHeight: true, + opacity: true, + orphans: true, + zIndex: true, + zoom: true +}; + +/** + * Most style properties can be unset by doing .style[prop] = '' but IE8 + * doesn't like doing that with shorthand properties so for the properties that + * IE8 breaks on, which are listed here, we instead unset each of the + * individual properties. See http://bugs.jquery.com/ticket/12385. + * The 4-value 'clock' properties like margin, padding, border-width seem to + * behave without any problems. Curiously, list-style works too without any + * special prodding. + */ +var shorthandPropertyExpansions = { + background: { + backgroundImage: true, + backgroundPosition: true, + backgroundRepeat: true, + backgroundColor: true + }, + border: { + borderWidth: true, + borderStyle: true, + borderColor: true + }, + borderBottom: { + borderBottomWidth: true, + borderBottomStyle: true, + borderBottomColor: true + }, + borderLeft: { + borderLeftWidth: true, + borderLeftStyle: true, + borderLeftColor: true + }, + borderRight: { + borderRightWidth: true, + borderRightStyle: true, + borderRightColor: true + }, + borderTop: { + borderTopWidth: true, + borderTopStyle: true, + borderTopColor: true + }, + font: { + fontStyle: true, + fontVariant: true, + fontWeight: true, + fontSize: true, + lineHeight: true, + fontFamily: true + } +}; + +var CSSProperty = { + isUnitlessNumber: isUnitlessNumber, + shorthandPropertyExpansions: shorthandPropertyExpansions +}; + +module.exports = CSSProperty; + +},{}],4:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule CSSPropertyOperations + * @typechecks static-only + */ + +"use strict"; + +var CSSProperty = require("./CSSProperty"); + +var dangerousStyleValue = require("./dangerousStyleValue"); +var escapeTextForBrowser = require("./escapeTextForBrowser"); +var hyphenate = require("./hyphenate"); +var memoizeStringOnly = require("./memoizeStringOnly"); + +var processStyleName = memoizeStringOnly(function(styleName) { + return escapeTextForBrowser(hyphenate(styleName)); +}); + +/** + * Operations for dealing with CSS properties. + */ +var CSSPropertyOperations = { + + /** + * Serializes a mapping of style properties for use as inline styles: + * + * > createMarkupForStyles({width: '200px', height: 0}) + * "width:200px;height:0;" + * + * Undefined values are ignored so that declarative programming is easier. + * + * @param {object} styles + * @return {?string} + */ + createMarkupForStyles: function(styles) { + var serialized = ''; + for (var styleName in styles) { + if (!styles.hasOwnProperty(styleName)) { + continue; + } + var styleValue = styles[styleName]; + if (styleValue != null) { + serialized += processStyleName(styleName) + ':'; + serialized += dangerousStyleValue(styleName, styleValue) + ';'; + } + } + return serialized || null; + }, + + /** + * Sets the value for multiple styles on a node. If a value is specified as + * '' (empty string), the corresponding style property will be unset. + * + * @param {DOMElement} node + * @param {object} styles + */ + setValueForStyles: function(node, styles) { + var style = node.style; + for (var styleName in styles) { + if (!styles.hasOwnProperty(styleName)) { + continue; + } + var styleValue = dangerousStyleValue(styleName, styles[styleName]); + if (styleValue) { + style[styleName] = styleValue; + } else { + var expansion = CSSProperty.shorthandPropertyExpansions[styleName]; + if (expansion) { + // Shorthand property that IE8 won't like unsetting, so unset each + // component to placate it + for (var individualStyleName in expansion) { + style[individualStyleName] = ''; + } + } else { + style[styleName] = ''; + } + } + } + } + +}; + +module.exports = CSSPropertyOperations; + +},{"./CSSProperty":3,"./dangerousStyleValue":91,"./escapeTextForBrowser":93,"./hyphenate":105,"./memoizeStringOnly":114}],5:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule CallbackRegistry + * @typechecks static-only + */ + +"use strict"; + +var listenerBank = {}; + +/** + * Stores "listeners" by `registrationName`/`id`. There should be at most one + * "listener" per `registrationName`/`id` in the `listenerBank`. + * + * Access listeners via `listenerBank[registrationName][id]`. + * + * @class CallbackRegistry + * @internal + */ +var CallbackRegistry = { + + /** + * Stores `listener` at `listenerBank[registrationName][id]`. Is idempotent. + * + * @param {string} id ID of the DOM element. + * @param {string} registrationName Name of listener (e.g. `onClick`). + * @param {?function} listener The callback to store. + */ + putListener: function(id, registrationName, listener) { + var bankForRegistrationName = + listenerBank[registrationName] || (listenerBank[registrationName] = {}); + bankForRegistrationName[id] = listener; + }, + + /** + * @param {string} id ID of the DOM element. + * @param {string} registrationName Name of listener (e.g. `onClick`). + * @return {?function} The stored callback. + */ + getListener: function(id, registrationName) { + var bankForRegistrationName = listenerBank[registrationName]; + return bankForRegistrationName && bankForRegistrationName[id]; + }, + + /** + * Deletes a listener from the registration bank. + * + * @param {string} id ID of the DOM element. + * @param {string} registrationName Name of listener (e.g. `onClick`). + */ + deleteListener: function(id, registrationName) { + var bankForRegistrationName = listenerBank[registrationName]; + if (bankForRegistrationName) { + delete bankForRegistrationName[id]; + } + }, + + /** + * Deletes all listeners for the DOM element with the supplied ID. + * + * @param {string} id ID of the DOM element. + */ + deleteAllListeners: function(id) { + for (var registrationName in listenerBank) { + delete listenerBank[registrationName][id]; + } + }, + + /** + * This is needed for tests only. Do not use! + */ + __purge: function() { + listenerBank = {}; + } + +}; + +module.exports = CallbackRegistry; + +},{}],6:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule ChangeEventPlugin + */ + +"use strict"; + +var EventConstants = require("./EventConstants"); +var EventPluginHub = require("./EventPluginHub"); +var EventPropagators = require("./EventPropagators"); +var ExecutionEnvironment = require("./ExecutionEnvironment"); +var SyntheticEvent = require("./SyntheticEvent"); + +var isEventSupported = require("./isEventSupported"); +var isTextInputElement = require("./isTextInputElement"); +var keyOf = require("./keyOf"); + +var topLevelTypes = EventConstants.topLevelTypes; + +var eventTypes = { + change: { + phasedRegistrationNames: { + bubbled: keyOf({onChange: null}), + captured: keyOf({onChangeCapture: null}) + } + } +}; + +/** + * For IE shims + */ +var activeElement = null; +var activeElementID = null; +var activeElementValue = null; +var activeElementValueProp = null; + +/** + * SECTION: handle `change` event + */ +function shouldUseChangeEvent(elem) { + return ( + elem.nodeName === 'SELECT' || + (elem.nodeName === 'INPUT' && elem.type === 'file') + ); +} + +var doesChangeEventBubble = false; +if (ExecutionEnvironment.canUseDOM) { + // See `handleChange` comment below + doesChangeEventBubble = isEventSupported('change') && ( + !('documentMode' in document) || document.documentMode > 8 + ); +} + +function manualDispatchChangeEvent(nativeEvent) { + var event = SyntheticEvent.getPooled( + eventTypes.change, + activeElementID, + nativeEvent + ); + EventPropagators.accumulateTwoPhaseDispatches(event); + + // If change bubbled, we'd just bind to it like all the other events + // and have it go through ReactEventTopLevelCallback. Since it doesn't, we + // manually listen for the change event and so we have to enqueue and + // process the abstract event manually. + EventPluginHub.enqueueEvents(event); + EventPluginHub.processEventQueue(); +} + +function startWatchingForChangeEventIE8(target, targetID) { + activeElement = target; + activeElementID = targetID; + activeElement.attachEvent('onchange', manualDispatchChangeEvent); +} + +function stopWatchingForChangeEventIE8() { + if (!activeElement) { + return; + } + activeElement.detachEvent('onchange', manualDispatchChangeEvent); + activeElement = null; + activeElementID = null; +} + +function getTargetIDForChangeEvent( + topLevelType, + topLevelTarget, + topLevelTargetID) { + if (topLevelType === topLevelTypes.topChange) { + return topLevelTargetID; + } +} +function handleEventsForChangeEventIE8( + topLevelType, + topLevelTarget, + topLevelTargetID) { + if (topLevelType === topLevelTypes.topFocus) { + // stopWatching() should be a noop here but we call it just in case we + // missed a blur event somehow. + stopWatchingForChangeEventIE8(); + startWatchingForChangeEventIE8(topLevelTarget, topLevelTargetID); + } else if (topLevelType === topLevelTypes.topBlur) { + stopWatchingForChangeEventIE8(); + } +} + + +/** + * SECTION: handle `input` event + */ +var isInputEventSupported = false; +if (ExecutionEnvironment.canUseDOM) { + // IE9 claims to support the input event but fails to trigger it when + // deleting text, so we ignore its input events + isInputEventSupported = isEventSupported('input') && ( + !('documentMode' in document) || document.documentMode > 9 + ); +} + +/** + * (For old IE.) Replacement getter/setter for the `value` property that gets + * set on the active element. + */ +var newValueProp = { + get: function() { + return activeElementValueProp.get.call(this); + }, + set: function(val) { + // Cast to a string so we can do equality checks. + activeElementValue = '' + val; + activeElementValueProp.set.call(this, val); + } +}; + +/** + * (For old IE.) Starts tracking propertychange events on the passed-in element + * and override the value property so that we can distinguish user events from + * value changes in JS. + */ +function startWatchingForValueChange(target, targetID) { + activeElement = target; + activeElementID = targetID; + activeElementValue = target.value; + activeElementValueProp = Object.getOwnPropertyDescriptor( + target.constructor.prototype, + 'value' + ); + + Object.defineProperty(activeElement, 'value', newValueProp); + activeElement.attachEvent('onpropertychange', handlePropertyChange); +} + +/** + * (For old IE.) Removes the event listeners from the currently-tracked element, + * if any exists. + */ +function stopWatchingForValueChange() { + if (!activeElement) { + return; + } + + // delete restores the original property definition + delete activeElement.value; + activeElement.detachEvent('onpropertychange', handlePropertyChange); + + activeElement = null; + activeElementID = null; + activeElementValue = null; + activeElementValueProp = null; +} + +/** + * (For old IE.) Handles a propertychange event, sending a `change` event if + * the value of the active element has changed. + */ +function handlePropertyChange(nativeEvent) { + if (nativeEvent.propertyName !== 'value') { + return; + } + var value = nativeEvent.srcElement.value; + if (value === activeElementValue) { + return; + } + activeElementValue = value; + + manualDispatchChangeEvent(nativeEvent); +} + +/** + * If a `change` event should be fired, returns the target's ID. + */ +function getTargetIDForInputEvent( + topLevelType, + topLevelTarget, + topLevelTargetID) { + if (topLevelType === topLevelTypes.topInput) { + // In modern browsers (i.e., not IE8 or IE9), the input event is exactly + // what we want so fall through here and trigger an abstract event + return topLevelTargetID; + } +} + +// For IE8 and IE9. +function handleEventsForInputEventIE( + topLevelType, + topLevelTarget, + topLevelTargetID) { + if (topLevelType === topLevelTypes.topFocus) { + // In IE8, we can capture almost all .value changes by adding a + // propertychange handler and looking for events with propertyName + // equal to 'value' + // In IE9, propertychange fires for most input events but is buggy and + // doesn't fire when text is deleted, but conveniently, selectionchange + // appears to fire in all of the remaining cases so we catch those and + // forward the event if the value has changed + // In either case, we don't want to call the event handler if the value + // is changed from JS so we redefine a setter for `.value` that updates + // our activeElementValue variable, allowing us to ignore those changes + // + // stopWatching() should be a noop here but we call it just in case we + // missed a blur event somehow. + stopWatchingForValueChange(); + startWatchingForValueChange(topLevelTarget, topLevelTargetID); + } else if (topLevelType === topLevelTypes.topBlur) { + stopWatchingForValueChange(); + } +} + +// For IE8 and IE9. +function getTargetIDForInputEventIE( + topLevelType, + topLevelTarget, + topLevelTargetID) { + if (topLevelType === topLevelTypes.topSelectionChange || + topLevelType === topLevelTypes.topKeyUp || + topLevelType === topLevelTypes.topKeyDown) { + // On the selectionchange event, the target is just document which isn't + // helpful for us so just check activeElement instead. + // + // 99% of the time, keydown and keyup aren't necessary. IE8 fails to fire + // propertychange on the first input event after setting `value` from a + // script and fires only keydown, keypress, keyup. Catching keyup usually + // gets it and catching keydown lets us fire an event for the first + // keystroke if user does a key repeat (it'll be a little delayed: right + // before the second keystroke). Other input methods (e.g., paste) seem to + // fire selectionchange normally. + if (activeElement && activeElement.value !== activeElementValue) { + activeElementValue = activeElement.value; + return activeElementID; + } + } +} + + +/** + * SECTION: handle `click` event + */ +function shouldUseClickEvent(elem) { + // Use the `click` event to detect changes to checkbox and radio inputs. + // This approach works across all browsers, whereas `change` does not fire + // until `blur` in IE8. + return ( + elem.nodeName === 'INPUT' && + (elem.type === 'checkbox' || elem.type === 'radio') + ); +} + +function getTargetIDForClickEvent( + topLevelType, + topLevelTarget, + topLevelTargetID) { + if (topLevelType === topLevelTypes.topClick) { + return topLevelTargetID; + } +} + +/** + * This plugin creates an `onChange` event that normalizes change events + * across form elements. This event fires at a time when it's possible to + * change the element's value without seeing a flicker. + * + * Supported elements are: + * - input (see `isTextInputElement`) + * - textarea + * - select + */ +var ChangeEventPlugin = { + + eventTypes: eventTypes, + + /** + * @param {string} topLevelType Record from `EventConstants`. + * @param {DOMEventTarget} topLevelTarget The listening component root node. + * @param {string} topLevelTargetID ID of `topLevelTarget`. + * @param {object} nativeEvent Native browser event. + * @return {*} An accumulation of synthetic events. + * @see {EventPluginHub.extractEvents} + */ + extractEvents: function( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent) { + + var getTargetIDFunc, handleEventFunc; + if (shouldUseChangeEvent(topLevelTarget)) { + if (doesChangeEventBubble) { + getTargetIDFunc = getTargetIDForChangeEvent; + } else { + handleEventFunc = handleEventsForChangeEventIE8; + } + } else if (isTextInputElement(topLevelTarget)) { + if (isInputEventSupported) { + getTargetIDFunc = getTargetIDForInputEvent; + } else { + getTargetIDFunc = getTargetIDForInputEventIE; + handleEventFunc = handleEventsForInputEventIE; + } + } else if (shouldUseClickEvent(topLevelTarget)) { + getTargetIDFunc = getTargetIDForClickEvent; + } + + if (getTargetIDFunc) { + var targetID = getTargetIDFunc( + topLevelType, + topLevelTarget, + topLevelTargetID + ); + if (targetID) { + var event = SyntheticEvent.getPooled( + eventTypes.change, + targetID, + nativeEvent + ); + EventPropagators.accumulateTwoPhaseDispatches(event); + return event; + } + } + + if (handleEventFunc) { + handleEventFunc( + topLevelType, + topLevelTarget, + topLevelTargetID + ); + } + } + +}; + +module.exports = ChangeEventPlugin; + +},{"./EventConstants":15,"./EventPluginHub":17,"./EventPropagators":20,"./ExecutionEnvironment":21,"./SyntheticEvent":75,"./isEventSupported":107,"./isTextInputElement":109,"./keyOf":113}],7:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule CompositionEventPlugin + * @typechecks static-only + */ + +"use strict"; + +var EventConstants = require("./EventConstants"); +var EventPropagators = require("./EventPropagators"); +var ExecutionEnvironment = require("./ExecutionEnvironment"); +var ReactInputSelection = require("./ReactInputSelection"); +var SyntheticCompositionEvent = require("./SyntheticCompositionEvent"); + +var getTextContentAccessor = require("./getTextContentAccessor"); +var keyOf = require("./keyOf"); + +var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space +var START_KEYCODE = 229; + +var useCompositionEvent = ExecutionEnvironment.canUseDOM && + 'CompositionEvent' in window; +var topLevelTypes = EventConstants.topLevelTypes; +var currentComposition = null; + +// Events and their corresponding property names. +var eventTypes = { + compositionEnd: { + phasedRegistrationNames: { + bubbled: keyOf({onCompositionEnd: null}), + captured: keyOf({onCompositionEndCapture: null}) + } + }, + compositionStart: { + phasedRegistrationNames: { + bubbled: keyOf({onCompositionStart: null}), + captured: keyOf({onCompositionStartCapture: null}) + } + }, + compositionUpdate: { + phasedRegistrationNames: { + bubbled: keyOf({onCompositionUpdate: null}), + captured: keyOf({onCompositionUpdateCapture: null}) + } + } +}; + +/** + * Translate native top level events into event types. + * + * @param {string} topLevelType + * @return {object} + */ +function getCompositionEventType(topLevelType) { + switch (topLevelType) { + case topLevelTypes.topCompositionStart: + return eventTypes.compositionStart; + case topLevelTypes.topCompositionEnd: + return eventTypes.compositionEnd; + case topLevelTypes.topCompositionUpdate: + return eventTypes.compositionUpdate; + } +} + +/** + * Does our fallback best-guess model think this event signifies that + * composition has begun? + * + * @param {string} topLevelType + * @param {object} nativeEvent + * @return {boolean} + */ +function isFallbackStart(topLevelType, nativeEvent) { + return ( + topLevelType === topLevelTypes.topKeyDown && + nativeEvent.keyCode === START_KEYCODE + ); +} + +/** + * Does our fallback mode think that this event is the end of composition? + * + * @param {string} topLevelType + * @param {object} nativeEvent + * @return {boolean} + */ +function isFallbackEnd(topLevelType, nativeEvent) { + switch (topLevelType) { + case topLevelTypes.topKeyUp: + // Command keys insert or clear IME input. + return (END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1); + case topLevelTypes.topKeyDown: + // Expect IME keyCode on each keydown. If we get any other + // code we must have exited earlier. + return (nativeEvent.keyCode !== START_KEYCODE); + case topLevelTypes.topKeyPress: + case topLevelTypes.topMouseDown: + case topLevelTypes.topBlur: + // Events are not possible without cancelling IME. + return true; + default: + return false; + } +} + +/** + * Helper class stores information about selection and document state + * so we can figure out what changed at a later date. + * + * @param {DOMEventTarget} root + */ +function FallbackCompositionState(root) { + this.root = root; + this.startSelection = ReactInputSelection.getSelection(root); + this.startValue = this.getText(); +} + +/** + * Get current text of input. + * + * @return {string} + */ +FallbackCompositionState.prototype.getText = function() { + return this.root.value || this.root[getTextContentAccessor()]; +}; + +/** + * Text that has changed since the start of composition. + * + * @return {string} + */ +FallbackCompositionState.prototype.getData = function() { + var endValue = this.getText(); + var prefixLength = this.startSelection.start; + var suffixLength = this.startValue.length - this.startSelection.end; + + return endValue.substr( + prefixLength, + endValue.length - suffixLength - prefixLength + ); +}; + +/** + * This plugin creates `onCompositionStart`, `onCompositionUpdate` and + * `onCompositionEnd` events on inputs, textareas and contentEditable + * nodes. + */ +var CompositionEventPlugin = { + + eventTypes: eventTypes, + + /** + * @param {string} topLevelType Record from `EventConstants`. + * @param {DOMEventTarget} topLevelTarget The listening component root node. + * @param {string} topLevelTargetID ID of `topLevelTarget`. + * @param {object} nativeEvent Native browser event. + * @return {*} An accumulation of synthetic events. + * @see {EventPluginHub.extractEvents} + */ + extractEvents: function( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent) { + + var eventType; + var data; + + if (useCompositionEvent) { + eventType = getCompositionEventType(topLevelType); + } else if (!currentComposition) { + if (isFallbackStart(topLevelType, nativeEvent)) { + eventType = eventTypes.start; + currentComposition = new FallbackCompositionState(topLevelTarget); + } + } else if (isFallbackEnd(topLevelType, nativeEvent)) { + eventType = eventTypes.compositionEnd; + data = currentComposition.getData(); + currentComposition = null; + } + + if (eventType) { + var event = SyntheticCompositionEvent.getPooled( + eventType, + topLevelTargetID, + nativeEvent + ); + if (data) { + // Inject data generated from fallback path into the synthetic event. + // This matches the property of native CompositionEventInterface. + event.data = data; + } + EventPropagators.accumulateTwoPhaseDispatches(event); + return event; + } + } +}; + +module.exports = CompositionEventPlugin; + +},{"./EventConstants":15,"./EventPropagators":20,"./ExecutionEnvironment":21,"./ReactInputSelection":49,"./SyntheticCompositionEvent":74,"./getTextContentAccessor":104,"./keyOf":113}],8:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule DOMChildrenOperations + * @typechecks static-only + */ + +"use strict"; + +var Danger = require("./Danger"); +var ReactMultiChildUpdateTypes = require("./ReactMultiChildUpdateTypes"); + +var getTextContentAccessor = require("./getTextContentAccessor"); + +/** + * The DOM property to use when setting text content. + * + * @type {string} + * @private + */ +var textContentAccessor = getTextContentAccessor() || 'NA'; + +/** + * Inserts `childNode` as a child of `parentNode` at the `index`. + * + * @param {DOMElement} parentNode Parent node in which to insert. + * @param {DOMElement} childNode Child node to insert. + * @param {number} index Index at which to insert the child. + * @internal + */ +function insertChildAt(parentNode, childNode, index) { + var childNodes = parentNode.childNodes; + if (childNodes[index] === childNode) { + return; + } + // If `childNode` is already a child of `parentNode`, remove it so that + // computing `childNodes[index]` takes into account the removal. + if (childNode.parentNode === parentNode) { + parentNode.removeChild(childNode); + } + if (index >= childNodes.length) { + parentNode.appendChild(childNode); + } else { + parentNode.insertBefore(childNode, childNodes[index]); + } +} + +/** + * Operations for updating with DOM children. + */ +var DOMChildrenOperations = { + + dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup, + + /** + * Updates a component's children by processing a series of updates. The + * update configurations are each expected to have a `parentNode` property. + * + * @param {array} updates List of update configurations. + * @param {array} markupList List of markup strings. + * @internal + */ + processUpdates: function(updates, markupList) { + var update; + // Mapping from parent IDs to initial child orderings. + var initialChildren = null; + // List of children that will be moved or removed. + var updatedChildren = null; + + for (var i = 0; update = updates[i]; i++) { + if (update.type === ReactMultiChildUpdateTypes.MOVE_EXISTING || + update.type === ReactMultiChildUpdateTypes.REMOVE_NODE) { + var updatedIndex = update.fromIndex; + var updatedChild = update.parentNode.childNodes[updatedIndex]; + var parentID = update.parentID; + + initialChildren = initialChildren || {}; + initialChildren[parentID] = initialChildren[parentID] || []; + initialChildren[parentID][updatedIndex] = updatedChild; + + updatedChildren = updatedChildren || []; + updatedChildren.push(updatedChild); + } + } + + var renderedMarkup = Danger.dangerouslyRenderMarkup(markupList); + + // Remove updated children first so that `toIndex` is consistent. + if (updatedChildren) { + for (var j = 0; j < updatedChildren.length; j++) { + updatedChildren[j].parentNode.removeChild(updatedChildren[j]); + } + } + + for (var k = 0; update = updates[k]; k++) { + switch (update.type) { + case ReactMultiChildUpdateTypes.INSERT_MARKUP: + insertChildAt( + update.parentNode, + renderedMarkup[update.markupIndex], + update.toIndex + ); + break; + case ReactMultiChildUpdateTypes.MOVE_EXISTING: + insertChildAt( + update.parentNode, + initialChildren[update.parentID][update.fromIndex], + update.toIndex + ); + break; + case ReactMultiChildUpdateTypes.TEXT_CONTENT: + update.parentNode[textContentAccessor] = update.textContent; + break; + case ReactMultiChildUpdateTypes.REMOVE_NODE: + // Already removed by the for-loop above. + break; + } + } + } + +}; + +module.exports = DOMChildrenOperations; + +},{"./Danger":11,"./ReactMultiChildUpdateTypes":56,"./getTextContentAccessor":104}],9:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule DOMProperty + * @typechecks static-only + */ + +/*jslint bitwise: true */ + +"use strict"; + +var invariant = require("./invariant"); + +var DOMPropertyInjection = { + /** + * Mapping from normalized, camelcased property names to a configuration that + * specifies how the associated DOM property should be accessed or rendered. + */ + MUST_USE_ATTRIBUTE: 0x1, + MUST_USE_PROPERTY: 0x2, + HAS_BOOLEAN_VALUE: 0x4, + HAS_SIDE_EFFECTS: 0x8, + + /** + * Inject some specialized knowledge about the DOM. This takes a config object + * with the following properties: + * + * isCustomAttribute: function that given an attribute name will return true + * if it can be inserted into the DOM verbatim. Useful for data-* or aria-* + * attributes where it's impossible to enumerate all of the possible + * attribute names, + * + * Properties: object mapping DOM property name to one of the + * DOMPropertyInjection constants or null. If your attribute isn't in here, + * it won't get written to the DOM. + * + * DOMAttributeNames: object mapping React attribute name to the DOM + * attribute name. Attribute names not specified use the **lowercase** + * normalized name. + * + * DOMPropertyNames: similar to DOMAttributeNames but for DOM properties. + * Property names not specified use the normalized name. + * + * DOMMutationMethods: Properties that require special mutation methods. If + * `value` is undefined, the mutation method should unset the property. + * + * @param {object} domPropertyConfig the config as described above. + */ + injectDOMPropertyConfig: function(domPropertyConfig) { + var Properties = domPropertyConfig.Properties || {}; + var DOMAttributeNames = domPropertyConfig.DOMAttributeNames || {}; + var DOMPropertyNames = domPropertyConfig.DOMPropertyNames || {}; + var DOMMutationMethods = domPropertyConfig.DOMMutationMethods || {}; + + if (domPropertyConfig.isCustomAttribute) { + DOMProperty._isCustomAttributeFunctions.push( + domPropertyConfig.isCustomAttribute + ); + } + + for (var propName in Properties) { + invariant( + !DOMProperty.isStandardName[propName], + 'injectDOMPropertyConfig(...): You\'re trying to inject DOM property ' + + '\'%s\' which has already been injected. You may be accidentally ' + + 'injecting the same DOM property config twice, or you may be ' + + 'injecting two configs that have conflicting property names.', + propName + ); + + DOMProperty.isStandardName[propName] = true; + + var lowerCased = propName.toLowerCase(); + DOMProperty.getPossibleStandardName[lowerCased] = propName; + + var attributeName = DOMAttributeNames[propName]; + if (attributeName) { + DOMProperty.getPossibleStandardName[attributeName] = propName; + } + + DOMProperty.getAttributeName[propName] = attributeName || lowerCased; + + DOMProperty.getPropertyName[propName] = + DOMPropertyNames[propName] || propName; + + var mutationMethod = DOMMutationMethods[propName]; + if (mutationMethod) { + DOMProperty.getMutationMethod[propName] = mutationMethod; + } + + var propConfig = Properties[propName]; + DOMProperty.mustUseAttribute[propName] = + propConfig & DOMPropertyInjection.MUST_USE_ATTRIBUTE; + DOMProperty.mustUseProperty[propName] = + propConfig & DOMPropertyInjection.MUST_USE_PROPERTY; + DOMProperty.hasBooleanValue[propName] = + propConfig & DOMPropertyInjection.HAS_BOOLEAN_VALUE; + DOMProperty.hasSideEffects[propName] = + propConfig & DOMPropertyInjection.HAS_SIDE_EFFECTS; + + invariant( + !DOMProperty.mustUseAttribute[propName] || + !DOMProperty.mustUseProperty[propName], + 'DOMProperty: Cannot use require using both attribute and property: %s', + propName + ); + invariant( + DOMProperty.mustUseProperty[propName] || + !DOMProperty.hasSideEffects[propName], + 'DOMProperty: Properties that have side effects must use property: %s', + propName + ); + } + } +}; +var defaultValueCache = {}; + +/** + * DOMProperty exports lookup objects that can be used like functions: + * + * > DOMProperty.isValid['id'] + * true + * > DOMProperty.isValid['foobar'] + * undefined + * + * Although this may be confusing, it performs better in general. + * + * @see http://jsperf.com/key-exists + * @see http://jsperf.com/key-missing + */ +var DOMProperty = { + + /** + * Checks whether a property name is a standard property. + * @type {Object} + */ + isStandardName: {}, + + /** + * Mapping from lowercase property names to the properly cased version, used + * to warn in the case of missing properties. + * @type {Object} + */ + getPossibleStandardName: {}, + + /** + * Mapping from normalized names to attribute names that differ. Attribute + * names are used when rendering markup or with `*Attribute()`. + * @type {Object} + */ + getAttributeName: {}, + + /** + * Mapping from normalized names to properties on DOM node instances. + * (This includes properties that mutate due to external factors.) + * @type {Object} + */ + getPropertyName: {}, + + /** + * Mapping from normalized names to mutation methods. This will only exist if + * mutation cannot be set simply by the property or `setAttribute()`. + * @type {Object} + */ + getMutationMethod: {}, + + /** + * Whether the property must be accessed and mutated as an object property. + * @type {Object} + */ + mustUseAttribute: {}, + + /** + * Whether the property must be accessed and mutated using `*Attribute()`. + * (This includes anything that fails ` in `.) + * @type {Object} + */ + mustUseProperty: {}, + + /** + * Whether the property should be removed when set to a falsey value. + * @type {Object} + */ + hasBooleanValue: {}, + + /** + * Whether or not setting a value causes side effects such as triggering + * resources to be loaded or text selection changes. We must ensure that + * the value is only set if it has changed. + * @type {Object} + */ + hasSideEffects: {}, + + /** + * All of the isCustomAttribute() functions that have been injected. + */ + _isCustomAttributeFunctions: [], + + /** + * Checks whether a property name is a custom attribute. + * @method + */ + isCustomAttribute: function(attributeName) { + return DOMProperty._isCustomAttributeFunctions.some( + function(isCustomAttributeFn) { + return isCustomAttributeFn.call(null, attributeName); + } + ); + }, + + /** + * Returns the default property value for a DOM property (i.e., not an + * attribute). Most default values are '' or false, but not all. Worse yet, + * some (in particular, `type`) vary depending on the type of element. + * + * TODO: Is it better to grab all the possible properties when creating an + * element to avoid having to create the same element twice? + */ + getDefaultValueForProperty: function(nodeName, prop) { + var nodeDefaults = defaultValueCache[nodeName]; + var testElement; + if (!nodeDefaults) { + defaultValueCache[nodeName] = nodeDefaults = {}; + } + if (!(prop in nodeDefaults)) { + testElement = document.createElement(nodeName); + nodeDefaults[prop] = testElement[prop]; + } + return nodeDefaults[prop]; + }, + + injection: DOMPropertyInjection +}; + +module.exports = DOMProperty; + +},{"./invariant":106}],10:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule DOMPropertyOperations + * @typechecks static-only + */ + +"use strict"; + +var DOMProperty = require("./DOMProperty"); + +var escapeTextForBrowser = require("./escapeTextForBrowser"); +var memoizeStringOnly = require("./memoizeStringOnly"); + +var processAttributeNameAndPrefix = memoizeStringOnly(function(name) { + return escapeTextForBrowser(name) + '="'; +}); + +if (true) { + var reactProps = { + __owner__: true, + children: true, + dangerouslySetInnerHTML: true, + key: true, + ref: true + }; + var warnedProperties = {}; + + var warnUnknownProperty = function(name) { + if (reactProps[name] || warnedProperties[name]) { + return; + } + + warnedProperties[name] = true; + var lowerCasedName = name.toLowerCase(); + + // data-* attributes should be lowercase; suggest the lowercase version + var standardName = DOMProperty.isCustomAttribute(lowerCasedName) ? + lowerCasedName : DOMProperty.getPossibleStandardName[lowerCasedName]; + + // For now, only warn when we have a suggested correction. This prevents + // logging too much when using transferPropsTo. + if (standardName != null) { + console.warn( + 'Unknown DOM property ' + name + '. Did you mean ' + standardName + '?' + ); + } + + }; +} + +/** + * Operations for dealing with DOM properties. + */ +var DOMPropertyOperations = { + + /** + * Creates markup for a property. + * + * @param {string} name + * @param {*} value + * @return {?string} Markup string, or null if the property was invalid. + */ + createMarkupForProperty: function(name, value) { + if (DOMProperty.isStandardName[name]) { + if (value == null || DOMProperty.hasBooleanValue[name] && !value) { + return ''; + } + var attributeName = DOMProperty.getAttributeName[name]; + return processAttributeNameAndPrefix(attributeName) + + escapeTextForBrowser(value) + '"'; + } else if (DOMProperty.isCustomAttribute(name)) { + if (value == null) { + return ''; + } + return processAttributeNameAndPrefix(name) + + escapeTextForBrowser(value) + '"'; + } else if (true) { + warnUnknownProperty(name); + } + return null; + }, + + /** + * Sets the value for a property on a node. + * + * @param {DOMElement} node + * @param {string} name + * @param {*} value + */ + setValueForProperty: function(node, name, value) { + if (DOMProperty.isStandardName[name]) { + var mutationMethod = DOMProperty.getMutationMethod[name]; + if (mutationMethod) { + mutationMethod(node, value); + } else if (DOMProperty.mustUseAttribute[name]) { + if (DOMProperty.hasBooleanValue[name] && !value) { + node.removeAttribute(DOMProperty.getAttributeName[name]); + } else { + node.setAttribute(DOMProperty.getAttributeName[name], '' + value); + } + } else { + var propName = DOMProperty.getPropertyName[name]; + if (!DOMProperty.hasSideEffects[name] || node[propName] !== value) { + node[propName] = value; + } + } + } else if (DOMProperty.isCustomAttribute(name)) { + node.setAttribute(name, '' + value); + } else if (true) { + warnUnknownProperty(name); + } + }, + + /** + * Deletes the value for a property on a node. + * + * @param {DOMElement} node + * @param {string} name + */ + deleteValueForProperty: function(node, name) { + if (DOMProperty.isStandardName[name]) { + var mutationMethod = DOMProperty.getMutationMethod[name]; + if (mutationMethod) { + mutationMethod(node, undefined); + } else if (DOMProperty.mustUseAttribute[name]) { + node.removeAttribute(DOMProperty.getAttributeName[name]); + } else { + var propName = DOMProperty.getPropertyName[name]; + node[propName] = DOMProperty.getDefaultValueForProperty( + node.nodeName, + name + ); + } + } else if (DOMProperty.isCustomAttribute(name)) { + node.removeAttribute(name); + } else if (true) { + warnUnknownProperty(name); + } + } + +}; + +module.exports = DOMPropertyOperations; + +},{"./DOMProperty":9,"./escapeTextForBrowser":93,"./memoizeStringOnly":114}],11:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule Danger + * @typechecks static-only + */ + +/*jslint evil: true, sub: true */ + +"use strict"; + +var ExecutionEnvironment = require("./ExecutionEnvironment"); + +var createNodesFromMarkup = require("./createNodesFromMarkup"); +var emptyFunction = require("./emptyFunction"); +var getMarkupWrap = require("./getMarkupWrap"); +var invariant = require("./invariant"); +var mutateHTMLNodeWithMarkup = require("./mutateHTMLNodeWithMarkup"); + +var OPEN_TAG_NAME_EXP = /^(<[^ \/>]+)/; +var RESULT_INDEX_ATTR = 'data-danger-index'; + +/** + * Extracts the `nodeName` from a string of markup. + * + * NOTE: Extracting the `nodeName` does not require a regular expression match + * because we make assumptions about React-generated markup (i.e. there are no + * spaces surrounding the opening tag and there is at least one attribute). + * + * @param {string} markup String of markup. + * @return {string} Node name of the supplied markup. + * @see http://jsperf.com/extract-nodename + */ +function getNodeName(markup) { + return markup.substring(1, markup.indexOf(' ')); +} + +var Danger = { + + /** + * Renders markup into an array of nodes. The markup is expected to render + * into a list of root nodes. Also, the length of `resultList` and + * `markupList` should be the same. + * + * @param {array} markupList List of markup strings to render. + * @return {array} List of rendered nodes. + * @internal + */ + dangerouslyRenderMarkup: function(markupList) { + invariant( + ExecutionEnvironment.canUseDOM, + 'dangerouslyRenderMarkup(...): Cannot render markup in a Worker ' + + 'thread. This is likely a bug in the framework. Please report ' + + 'immediately.' + ); + var nodeName; + var markupByNodeName = {}; + // Group markup by `nodeName` if a wrap is necessary, else by '*'. + for (var i = 0; i < markupList.length; i++) { + invariant( + markupList[i], + 'dangerouslyRenderMarkup(...): Missing markup.' + ); + nodeName = getNodeName(markupList[i]); + nodeName = getMarkupWrap(nodeName) ? nodeName : '*'; + markupByNodeName[nodeName] = markupByNodeName[nodeName] || []; + markupByNodeName[nodeName][i] = markupList[i]; + } + var resultList = []; + var resultListAssignmentCount = 0; + for (nodeName in markupByNodeName) { + if (!markupByNodeName.hasOwnProperty(nodeName)) { + continue; + } + var markupListByNodeName = markupByNodeName[nodeName]; + + // This for-in loop skips the holes of the sparse array. The order of + // iteration should follow the order of assignment, which happens to match + // numerical index order, but we don't rely on that. + for (var resultIndex in markupListByNodeName) { + if (markupListByNodeName.hasOwnProperty(resultIndex)) { + var markup = markupListByNodeName[resultIndex]; + + // Push the requested markup with an additional RESULT_INDEX_ATTR + // attribute. If the markup does not start with a < character, it + // will be discarded below (with an appropriate console.error). + markupListByNodeName[resultIndex] = markup.replace( + OPEN_TAG_NAME_EXP, + // This index will be parsed back out below. + '$1 ' + RESULT_INDEX_ATTR + '="' + resultIndex + '" ' + ); + } + } + + // Render each group of markup with similar wrapping `nodeName`. + var renderNodes = createNodesFromMarkup( + markupListByNodeName.join(''), + emptyFunction // Do nothing special with tags. + ); + + for (i = 0; i < renderNodes.length; ++i) { + var renderNode = renderNodes[i]; + if (renderNode.hasAttribute && + renderNode.hasAttribute(RESULT_INDEX_ATTR)) { + + resultIndex = +renderNode.getAttribute(RESULT_INDEX_ATTR); + renderNode.removeAttribute(RESULT_INDEX_ATTR); + + invariant( + !resultList.hasOwnProperty(resultIndex), + 'Danger: Assigning to an already-occupied result index.' + ); + + resultList[resultIndex] = renderNode; + + // This should match resultList.length and markupList.length when + // we're done. + resultListAssignmentCount += 1; + + } else if (true) { + console.error( + "Danger: Discarding unexpected node:", + renderNode + ); + } + } + } + + // Although resultList was populated out of order, it should now be a dense + // array. + invariant( + resultListAssignmentCount === resultList.length, + 'Danger: Did not assign to every index of resultList.' + ); + + invariant( + resultList.length === markupList.length, + 'Danger: Expected markup to render %s nodes, but rendered %s.', + markupList.length, + resultList.length + ); + + return resultList; + }, + + /** + * Replaces a node with a string of markup at its current position within its + * parent. The markup must render into a single root node. + * + * @param {DOMElement} oldChild Child node to replace. + * @param {string} markup Markup to render in place of the child node. + * @internal + */ + dangerouslyReplaceNodeWithMarkup: function(oldChild, markup) { + invariant( + ExecutionEnvironment.canUseDOM, + 'dangerouslyReplaceNodeWithMarkup(...): Cannot render markup in a ' + + 'worker thread. This is likely a bug in the framework. Please report ' + + 'immediately.' + ); + invariant(markup, 'dangerouslyReplaceNodeWithMarkup(...): Missing markup.'); + // createNodesFromMarkup() won't work if the markup is rooted by + // since it has special semantic meaning. So we use an alternatie strategy. + if (oldChild.tagName.toLowerCase() === 'html') { + mutateHTMLNodeWithMarkup(oldChild, markup); + return; + } + var newChild = createNodesFromMarkup(markup, emptyFunction)[0]; + oldChild.parentNode.replaceChild(newChild, oldChild); + } + +}; + +module.exports = Danger; + +},{"./ExecutionEnvironment":21,"./createNodesFromMarkup":88,"./emptyFunction":92,"./getMarkupWrap":101,"./invariant":106,"./mutateHTMLNodeWithMarkup":119}],12:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule DefaultDOMPropertyConfig + */ + +/*jslint bitwise: true*/ + +"use strict"; + +var DOMProperty = require("./DOMProperty"); + +var MUST_USE_ATTRIBUTE = DOMProperty.injection.MUST_USE_ATTRIBUTE; +var MUST_USE_PROPERTY = DOMProperty.injection.MUST_USE_PROPERTY; +var HAS_BOOLEAN_VALUE = DOMProperty.injection.HAS_BOOLEAN_VALUE; +var HAS_SIDE_EFFECTS = DOMProperty.injection.HAS_SIDE_EFFECTS; + +var DefaultDOMPropertyConfig = { + isCustomAttribute: RegExp.prototype.test.bind( + /^(data|aria)-[a-z_][a-z\d_.\-]*$/ + ), + Properties: { + /** + * Standard Properties + */ + accept: null, + accessKey: null, + action: null, + allowFullScreen: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, + allowTransparency: MUST_USE_ATTRIBUTE, + alt: null, + autoComplete: null, + autoFocus: HAS_BOOLEAN_VALUE, + autoPlay: HAS_BOOLEAN_VALUE, + cellPadding: null, + cellSpacing: null, + charSet: MUST_USE_ATTRIBUTE, + checked: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + className: MUST_USE_PROPERTY, + colSpan: null, + content: null, + contentEditable: null, + contextMenu: MUST_USE_ATTRIBUTE, + controls: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + data: null, // For `` acts as `src`. + dateTime: MUST_USE_ATTRIBUTE, + dir: null, + disabled: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, + draggable: null, + encType: null, + form: MUST_USE_ATTRIBUTE, + frameBorder: MUST_USE_ATTRIBUTE, + height: MUST_USE_ATTRIBUTE, + hidden: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, + href: null, + htmlFor: null, + httpEquiv: null, + icon: null, + id: MUST_USE_PROPERTY, + label: null, + lang: null, + list: null, + max: null, + maxLength: MUST_USE_ATTRIBUTE, + method: null, + min: null, + multiple: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + name: null, + pattern: null, + placeholder: null, + poster: null, + preload: null, + radioGroup: null, + readOnly: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + rel: null, + required: HAS_BOOLEAN_VALUE, + role: MUST_USE_ATTRIBUTE, + rowSpan: null, + scrollLeft: MUST_USE_PROPERTY, + scrollTop: MUST_USE_PROPERTY, + selected: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + size: null, + spellCheck: null, + src: null, + step: null, + style: null, + tabIndex: null, + target: null, + title: null, + type: null, + value: MUST_USE_PROPERTY | HAS_SIDE_EFFECTS, + width: MUST_USE_ATTRIBUTE, + wmode: MUST_USE_ATTRIBUTE, + + /** + * Non-standard Properties + */ + autoCapitalize: null, // Supported in Mobile Safari for keyboard hints + + /** + * SVG Properties + */ + cx: MUST_USE_ATTRIBUTE, + cy: MUST_USE_ATTRIBUTE, + d: MUST_USE_ATTRIBUTE, + fill: MUST_USE_ATTRIBUTE, + fx: MUST_USE_ATTRIBUTE, + fy: MUST_USE_ATTRIBUTE, + gradientTransform: MUST_USE_ATTRIBUTE, + gradientUnits: MUST_USE_ATTRIBUTE, + offset: MUST_USE_ATTRIBUTE, + points: MUST_USE_ATTRIBUTE, + r: MUST_USE_ATTRIBUTE, + rx: MUST_USE_ATTRIBUTE, + ry: MUST_USE_ATTRIBUTE, + spreadMethod: MUST_USE_ATTRIBUTE, + stopColor: MUST_USE_ATTRIBUTE, + stopOpacity: MUST_USE_ATTRIBUTE, + stroke: MUST_USE_ATTRIBUTE, + strokeLinecap: MUST_USE_ATTRIBUTE, + strokeWidth: MUST_USE_ATTRIBUTE, + transform: MUST_USE_ATTRIBUTE, + version: MUST_USE_ATTRIBUTE, + viewBox: MUST_USE_ATTRIBUTE, + x1: MUST_USE_ATTRIBUTE, + x2: MUST_USE_ATTRIBUTE, + x: MUST_USE_ATTRIBUTE, + y1: MUST_USE_ATTRIBUTE, + y2: MUST_USE_ATTRIBUTE, + y: MUST_USE_ATTRIBUTE + }, + DOMAttributeNames: { + className: 'class', + gradientTransform: 'gradientTransform', + gradientUnits: 'gradientUnits', + htmlFor: 'for', + spreadMethod: 'spreadMethod', + stopColor: 'stop-color', + stopOpacity: 'stop-opacity', + strokeLinecap: 'stroke-linecap', + strokeWidth: 'stroke-width', + viewBox: 'viewBox' + }, + DOMPropertyNames: { + autoCapitalize: 'autocapitalize', + autoComplete: 'autocomplete', + autoFocus: 'autofocus', + autoPlay: 'autoplay', + encType: 'enctype', + radioGroup: 'radiogroup', + spellCheck: 'spellcheck' + }, + DOMMutationMethods: { + /** + * Setting `className` to null may cause it to be set to the string "null". + * + * @param {DOMElement} node + * @param {*} value + */ + className: function(node, value) { + node.className = value || ''; + } + } +}; + +module.exports = DefaultDOMPropertyConfig; + +},{"./DOMProperty":9}],13:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule DefaultEventPluginOrder + */ + +"use strict"; + + var keyOf = require("./keyOf"); + +/** + * Module that is injectable into `EventPluginHub`, that specifies a + * deterministic ordering of `EventPlugin`s. A convenient way to reason about + * plugins, without having to package every one of them. This is better than + * having plugins be ordered in the same order that they are injected because + * that ordering would be influenced by the packaging order. + * `ResponderEventPlugin` must occur before `SimpleEventPlugin` so that + * preventing default on events is convenient in `SimpleEventPlugin` handlers. + */ +var DefaultEventPluginOrder = [ + keyOf({ResponderEventPlugin: null}), + keyOf({SimpleEventPlugin: null}), + keyOf({TapEventPlugin: null}), + keyOf({EnterLeaveEventPlugin: null}), + keyOf({ChangeEventPlugin: null}), + keyOf({SelectEventPlugin: null}), + keyOf({CompositionEventPlugin: null}), + keyOf({AnalyticsEventPlugin: null}), + keyOf({MobileSafariClickEventPlugin: null}) +]; + +module.exports = DefaultEventPluginOrder; + +},{"./keyOf":113}],14:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule EnterLeaveEventPlugin + * @typechecks static-only + */ + +"use strict"; + +var EventConstants = require("./EventConstants"); +var EventPropagators = require("./EventPropagators"); +var SyntheticMouseEvent = require("./SyntheticMouseEvent"); + +var ReactMount = require("./ReactMount"); +var keyOf = require("./keyOf"); + +var topLevelTypes = EventConstants.topLevelTypes; +var getFirstReactDOM = ReactMount.getFirstReactDOM; + +var eventTypes = { + mouseEnter: {registrationName: keyOf({onMouseEnter: null})}, + mouseLeave: {registrationName: keyOf({onMouseLeave: null})} +}; + +var extractedEvents = [null, null]; + +var EnterLeaveEventPlugin = { + + eventTypes: eventTypes, + + /** + * For almost every interaction we care about, there will be both a top-level + * `mouseover` and `mouseout` event that occurs. Only use `mouseout` so that + * we do not extract duplicate events. However, moving the mouse into the + * browser from outside will not fire a `mouseout` event. In this case, we use + * the `mouseover` top-level event. + * + * @param {string} topLevelType Record from `EventConstants`. + * @param {DOMEventTarget} topLevelTarget The listening component root node. + * @param {string} topLevelTargetID ID of `topLevelTarget`. + * @param {object} nativeEvent Native browser event. + * @return {*} An accumulation of synthetic events. + * @see {EventPluginHub.extractEvents} + */ + extractEvents: function( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent) { + if (topLevelType === topLevelTypes.topMouseOver && + (nativeEvent.relatedTarget || nativeEvent.fromElement)) { + return null; + } + if (topLevelType !== topLevelTypes.topMouseOut && + topLevelType !== topLevelTypes.topMouseOver) { + // Must not be a mouse in or mouse out - ignoring. + return null; + } + + var from, to; + if (topLevelType === topLevelTypes.topMouseOut) { + from = topLevelTarget; + to = + getFirstReactDOM(nativeEvent.relatedTarget || nativeEvent.toElement) || + window; + } else { + from = window; + to = topLevelTarget; + } + + if (from === to) { + // Nothing pertains to our managed components. + return null; + } + + var fromID = from ? ReactMount.getID(from) : ''; + var toID = to ? ReactMount.getID(to) : ''; + + var leave = SyntheticMouseEvent.getPooled( + eventTypes.mouseLeave, + fromID, + nativeEvent + ); + var enter = SyntheticMouseEvent.getPooled( + eventTypes.mouseEnter, + toID, + nativeEvent + ); + + EventPropagators.accumulateEnterLeaveDispatches(leave, enter, fromID, toID); + + extractedEvents[0] = leave; + extractedEvents[1] = enter; + + return extractedEvents; + } + +}; + +module.exports = EnterLeaveEventPlugin; + +},{"./EventConstants":15,"./EventPropagators":20,"./ReactMount":53,"./SyntheticMouseEvent":78,"./keyOf":113}],15:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule EventConstants + */ + +"use strict"; + +var keyMirror = require("./keyMirror"); + +var PropagationPhases = keyMirror({bubbled: null, captured: null}); + +/** + * Types of raw signals from the browser caught at the top level. + */ +var topLevelTypes = keyMirror({ + topBlur: null, + topChange: null, + topClick: null, + topCompositionEnd: null, + topCompositionStart: null, + topCompositionUpdate: null, + topCopy: null, + topCut: null, + topDoubleClick: null, + topDrag: null, + topDragEnd: null, + topDragEnter: null, + topDragExit: null, + topDragLeave: null, + topDragOver: null, + topDragStart: null, + topDrop: null, + topFocus: null, + topInput: null, + topKeyDown: null, + topKeyPress: null, + topKeyUp: null, + topMouseDown: null, + topMouseMove: null, + topMouseOut: null, + topMouseOver: null, + topMouseUp: null, + topPaste: null, + topScroll: null, + topSelectionChange: null, + topSubmit: null, + topTouchCancel: null, + topTouchEnd: null, + topTouchMove: null, + topTouchStart: null, + topWheel: null +}); + +var EventConstants = { + topLevelTypes: topLevelTypes, + PropagationPhases: PropagationPhases +}; + +module.exports = EventConstants; + +},{"./keyMirror":112}],16:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule EventListener + */ + +/** + * Upstream version of event listener. Does not take into account specific + * nature of platform. + */ +var EventListener = { + /** + * Listens to bubbled events on a DOM node. + * + * @param {Element} el DOM element to register listener on. + * @param {string} handlerBaseName 'click'/'mouseover' + * @param {Function!} cb Callback function + */ + listen: function(el, handlerBaseName, cb) { + if (el.addEventListener) { + el.addEventListener(handlerBaseName, cb, false); + } else if (el.attachEvent) { + el.attachEvent('on' + handlerBaseName, cb); + } + }, + + /** + * Listens to captured events on a DOM node. + * + * @see `EventListener.listen` for params. + * @throws Exception if addEventListener is not supported. + */ + capture: function(el, handlerBaseName, cb) { + if (!el.addEventListener) { + if (true) { + console.error( + 'You are attempting to use addEventlistener ' + + 'in a browser that does not support it support it.' + + 'This likely means that you will not receive events that ' + + 'your application relies on (such as scroll).'); + } + return; + } else { + el.addEventListener(handlerBaseName, cb, true); + } + } +}; + +module.exports = EventListener; + +},{}],17:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule EventPluginHub + */ + +"use strict"; + +var CallbackRegistry = require("./CallbackRegistry"); +var EventPluginRegistry = require("./EventPluginRegistry"); +var EventPluginUtils = require("./EventPluginUtils"); +var EventPropagators = require("./EventPropagators"); +var ExecutionEnvironment = require("./ExecutionEnvironment"); + +var accumulate = require("./accumulate"); +var forEachAccumulated = require("./forEachAccumulated"); +var invariant = require("./invariant"); + +/** + * Internal queue of events that have accumulated their dispatches and are + * waiting to have their dispatches executed. + */ +var eventQueue = null; + +/** + * Dispatches an event and releases it back into the pool, unless persistent. + * + * @param {?object} event Synthetic event to be dispatched. + * @private + */ +var executeDispatchesAndRelease = function(event) { + if (event) { + var executeDispatch = EventPluginUtils.executeDispatch; + // Plugins can provide custom behavior when dispatching events. + var PluginModule = EventPluginRegistry.getPluginModuleForEvent(event); + if (PluginModule && PluginModule.executeDispatch) { + executeDispatch = PluginModule.executeDispatch; + } + EventPluginUtils.executeDispatchesInOrder(event, executeDispatch); + + if (!event.isPersistent()) { + event.constructor.release(event); + } + } +}; + +/** + * This is a unified interface for event plugins to be installed and configured. + * + * Event plugins can implement the following properties: + * + * `extractEvents` {function(string, DOMEventTarget, string, object): *} + * Required. When a top-level event is fired, this method is expected to + * extract synthetic events that will in turn be queued and dispatched. + * + * `eventTypes` {object} + * Optional, plugins that fire events must publish a mapping of registration + * names that are used to register listeners. Values of this mapping must + * be objects that contain `registrationName` or `phasedRegistrationNames`. + * + * `executeDispatch` {function(object, function, string)} + * Optional, allows plugins to override how an event gets dispatched. By + * default, the listener is simply invoked. + * + * Each plugin that is injected into `EventsPluginHub` is immediately operable. + * + * @public + */ +var EventPluginHub = { + + /** + * Methods for injecting dependencies. + */ + injection: { + + /** + * @param {object} InjectedInstanceHandle + * @public + */ + injectInstanceHandle: EventPropagators.injection.injectInstanceHandle, + + /** + * @param {array} InjectedEventPluginOrder + * @public + */ + injectEventPluginOrder: EventPluginRegistry.injectEventPluginOrder, + + /** + * @param {object} injectedNamesToPlugins Map from names to plugin modules. + */ + injectEventPluginsByName: EventPluginRegistry.injectEventPluginsByName + + }, + + registrationNames: EventPluginRegistry.registrationNames, + + putListener: CallbackRegistry.putListener, + + getListener: CallbackRegistry.getListener, + + deleteListener: CallbackRegistry.deleteListener, + + deleteAllListeners: CallbackRegistry.deleteAllListeners, + + /** + * Allows registered plugins an opportunity to extract events from top-level + * native browser events. + * + * @param {string} topLevelType Record from `EventConstants`. + * @param {DOMEventTarget} topLevelTarget The listening component root node. + * @param {string} topLevelTargetID ID of `topLevelTarget`. + * @param {object} nativeEvent Native browser event. + * @return {*} An accumulation of synthetic events. + * @internal + */ + extractEvents: function( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent) { + var events; + var plugins = EventPluginRegistry.plugins; + for (var i = 0, l = plugins.length; i < l; i++) { + // Not every plugin in the ordering may be loaded at runtime. + var possiblePlugin = plugins[i]; + if (possiblePlugin) { + var extractedEvents = possiblePlugin.extractEvents( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent + ); + if (extractedEvents) { + events = accumulate(events, extractedEvents); + } + } + } + return events; + }, + + /** + * Enqueues a synthetic event that should be dispatched when + * `processEventQueue` is invoked. + * + * @param {*} events An accumulation of synthetic events. + * @internal + */ + enqueueEvents: function(events) { + if (events) { + eventQueue = accumulate(eventQueue, events); + } + }, + + /** + * Dispatches all synthetic events on the event queue. + * + * @internal + */ + processEventQueue: function() { + // Set `eventQueue` to null before processing it so that we can tell if more + // events get enqueued while processing. + var processingEventQueue = eventQueue; + eventQueue = null; + forEachAccumulated(processingEventQueue, executeDispatchesAndRelease); + invariant( + !eventQueue, + 'processEventQueue(): Additional events were enqueued while processing ' + + 'an event queue. Support for this has not yet been implemented.' + ); + } + +}; + +if (ExecutionEnvironment.canUseDOM) { + window.EventPluginHub = EventPluginHub; +} + +module.exports = EventPluginHub; + +},{"./CallbackRegistry":5,"./EventPluginRegistry":18,"./EventPluginUtils":19,"./EventPropagators":20,"./ExecutionEnvironment":21,"./accumulate":84,"./forEachAccumulated":97,"./invariant":106}],18:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule EventPluginRegistry + * @typechecks static-only + */ + +"use strict"; + +var invariant = require("./invariant"); + +/** + * Injectable ordering of event plugins. + */ +var EventPluginOrder = null; + +/** + * Injectable mapping from names to event plugin modules. + */ +var namesToPlugins = {}; + +/** + * Recomputes the plugin list using the injected plugins and plugin ordering. + * + * @private + */ +function recomputePluginOrdering() { + if (!EventPluginOrder) { + // Wait until an `EventPluginOrder` is injected. + return; + } + for (var pluginName in namesToPlugins) { + var PluginModule = namesToPlugins[pluginName]; + var pluginIndex = EventPluginOrder.indexOf(pluginName); + invariant( + pluginIndex > -1, + 'EventPluginRegistry: Cannot inject event plugins that do not exist in ' + + 'the plugin ordering, `%s`.', + pluginName + ); + if (EventPluginRegistry.plugins[pluginIndex]) { + continue; + } + invariant( + PluginModule.extractEvents, + 'EventPluginRegistry: Event plugins must implement an `extractEvents` ' + + 'method, but `%s` does not.', + pluginName + ); + EventPluginRegistry.plugins[pluginIndex] = PluginModule; + var publishedEvents = PluginModule.eventTypes; + for (var eventName in publishedEvents) { + invariant( + publishEventForPlugin(publishedEvents[eventName], PluginModule), + 'EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.', + eventName, + pluginName + ); + } + } +} + +/** + * Publishes an event so that it can be dispatched by the supplied plugin. + * + * @param {object} dispatchConfig Dispatch configuration for the event. + * @param {object} PluginModule Plugin publishing the event. + * @return {boolean} True if the event was successfully published. + * @private + */ +function publishEventForPlugin(dispatchConfig, PluginModule) { + var phasedRegistrationNames = dispatchConfig.phasedRegistrationNames; + if (phasedRegistrationNames) { + for (var phaseName in phasedRegistrationNames) { + if (phasedRegistrationNames.hasOwnProperty(phaseName)) { + var phasedRegistrationName = phasedRegistrationNames[phaseName]; + publishRegistrationName(phasedRegistrationName, PluginModule); + } + } + return true; + } else if (dispatchConfig.registrationName) { + publishRegistrationName(dispatchConfig.registrationName, PluginModule); + return true; + } + return false; +} + +/** + * Publishes a registration name that is used to identify dispatched events and + * can be used with `EventPluginHub.putListener` to register listeners. + * + * @param {string} registrationName Registration name to add. + * @param {object} PluginModule Plugin publishing the event. + * @private + */ +function publishRegistrationName(registrationName, PluginModule) { + invariant( + !EventPluginRegistry.registrationNames[registrationName], + 'EventPluginHub: More than one plugin attempted to publish the same ' + + 'registration name, `%s`.', + registrationName + ); + EventPluginRegistry.registrationNames[registrationName] = PluginModule; + EventPluginRegistry.registrationNamesKeys.push(registrationName); +} + +/** + * Registers plugins so that they can extract and dispatch events. + * + * @see {EventPluginHub} + */ +var EventPluginRegistry = { + + /** + * Ordered list of injected plugins. + */ + plugins: [], + + /** + * Mapping from registration names to plugin modules. + */ + registrationNames: {}, + + /** + * The keys of `registrationNames`. + */ + registrationNamesKeys: [], + + /** + * Injects an ordering of plugins (by plugin name). This allows the ordering + * to be decoupled from injection of the actual plugins so that ordering is + * always deterministic regardless of packaging, on-the-fly injection, etc. + * + * @param {array} InjectedEventPluginOrder + * @internal + * @see {EventPluginHub.injection.injectEventPluginOrder} + */ + injectEventPluginOrder: function(InjectedEventPluginOrder) { + invariant( + !EventPluginOrder, + 'EventPluginRegistry: Cannot inject event plugin ordering more than once.' + ); + // Clone the ordering so it cannot be dynamically mutated. + EventPluginOrder = Array.prototype.slice.call(InjectedEventPluginOrder); + recomputePluginOrdering(); + }, + + /** + * Injects plugins to be used by `EventPluginHub`. The plugin names must be + * in the ordering injected by `injectEventPluginOrder`. + * + * Plugins can be injected as part of page initialization or on-the-fly. + * + * @param {object} injectedNamesToPlugins Map from names to plugin modules. + * @internal + * @see {EventPluginHub.injection.injectEventPluginsByName} + */ + injectEventPluginsByName: function(injectedNamesToPlugins) { + var isOrderingDirty = false; + for (var pluginName in injectedNamesToPlugins) { + if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) { + continue; + } + var PluginModule = injectedNamesToPlugins[pluginName]; + if (namesToPlugins[pluginName] !== PluginModule) { + invariant( + !namesToPlugins[pluginName], + 'EventPluginRegistry: Cannot inject two different event plugins ' + + 'using the same name, `%s`.', + pluginName + ); + namesToPlugins[pluginName] = PluginModule; + isOrderingDirty = true; + } + } + if (isOrderingDirty) { + recomputePluginOrdering(); + } + }, + + /** + * Looks up the plugin for the supplied event. + * + * @param {object} event A synthetic event. + * @return {?object} The plugin that created the supplied event. + * @internal + */ + getPluginModuleForEvent: function(event) { + var dispatchConfig = event.dispatchConfig; + if (dispatchConfig.registrationName) { + return EventPluginRegistry.registrationNames[ + dispatchConfig.registrationName + ] || null; + } + for (var phase in dispatchConfig.phasedRegistrationNames) { + if (!dispatchConfig.phasedRegistrationNames.hasOwnProperty(phase)) { + continue; + } + var PluginModule = EventPluginRegistry.registrationNames[ + dispatchConfig.phasedRegistrationNames[phase] + ]; + if (PluginModule) { + return PluginModule; + } + } + return null; + }, + + /** + * Exposed for unit testing. + * @private + */ + _resetEventPlugins: function() { + EventPluginOrder = null; + for (var pluginName in namesToPlugins) { + if (namesToPlugins.hasOwnProperty(pluginName)) { + delete namesToPlugins[pluginName]; + } + } + EventPluginRegistry.plugins.length = 0; + var registrationNames = EventPluginRegistry.registrationNames; + for (var registrationName in registrationNames) { + if (registrationNames.hasOwnProperty(registrationName)) { + delete registrationNames[registrationName]; + } + } + EventPluginRegistry.registrationNamesKeys.length = 0; + } + +}; + +module.exports = EventPluginRegistry; + +},{"./invariant":106}],19:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule EventPluginUtils + */ + +"use strict"; + +var EventConstants = require("./EventConstants"); + +var invariant = require("./invariant"); + +var topLevelTypes = EventConstants.topLevelTypes; + +function isEndish(topLevelType) { + return topLevelType === topLevelTypes.topMouseUp || + topLevelType === topLevelTypes.topTouchEnd || + topLevelType === topLevelTypes.topTouchCancel; +} + +function isMoveish(topLevelType) { + return topLevelType === topLevelTypes.topMouseMove || + topLevelType === topLevelTypes.topTouchMove; +} +function isStartish(topLevelType) { + return topLevelType === topLevelTypes.topMouseDown || + topLevelType === topLevelTypes.topTouchStart; +} + +var validateEventDispatches; +if (true) { + validateEventDispatches = function(event) { + var dispatchListeners = event._dispatchListeners; + var dispatchIDs = event._dispatchIDs; + + var listenersIsArr = Array.isArray(dispatchListeners); + var idsIsArr = Array.isArray(dispatchIDs); + var IDsLen = idsIsArr ? dispatchIDs.length : dispatchIDs ? 1 : 0; + var listenersLen = listenersIsArr ? + dispatchListeners.length : + dispatchListeners ? 1 : 0; + + invariant( + idsIsArr === listenersIsArr && IDsLen === listenersLen, + 'EventPluginUtils: Invalid `event`.' + ); + }; +} + +/** + * Invokes `cb(event, listener, id)`. Avoids using call if no scope is + * provided. The `(listener,id)` pair effectively forms the "dispatch" but are + * kept separate to conserve memory. + */ +function forEachEventDispatch(event, cb) { + var dispatchListeners = event._dispatchListeners; + var dispatchIDs = event._dispatchIDs; + if (true) { + validateEventDispatches(event); + } + if (Array.isArray(dispatchListeners)) { + for (var i = 0; i < dispatchListeners.length; i++) { + if (event.isPropagationStopped()) { + break; + } + // Listeners and IDs are two parallel arrays that are always in sync. + cb(event, dispatchListeners[i], dispatchIDs[i]); + } + } else if (dispatchListeners) { + cb(event, dispatchListeners, dispatchIDs); + } +} + +/** + * Default implementation of PluginModule.executeDispatch(). + * @param {SyntheticEvent} SyntheticEvent to handle + * @param {function} Application-level callback + * @param {string} domID DOM id to pass to the callback. + */ +function executeDispatch(event, listener, domID) { + listener(event, domID); +} + +/** + * Standard/simple iteration through an event's collected dispatches. + */ +function executeDispatchesInOrder(event, executeDispatch) { + forEachEventDispatch(event, executeDispatch); + event._dispatchListeners = null; + event._dispatchIDs = null; +} + +/** + * Standard/simple iteration through an event's collected dispatches, but stops + * at the first dispatch execution returning true, and returns that id. + * + * @return id of the first dispatch execution who's listener returns true, or + * null if no listener returned true. + */ +function executeDispatchesInOrderStopAtTrue(event) { + var dispatchListeners = event._dispatchListeners; + var dispatchIDs = event._dispatchIDs; + if (true) { + validateEventDispatches(event); + } + if (Array.isArray(dispatchListeners)) { + for (var i = 0; i < dispatchListeners.length; i++) { + if (event.isPropagationStopped()) { + break; + } + // Listeners and IDs are two parallel arrays that are always in sync. + if (dispatchListeners[i](event, dispatchIDs[i])) { + return dispatchIDs[i]; + } + } + } else if (dispatchListeners) { + if (dispatchListeners(event, dispatchIDs)) { + return dispatchIDs; + } + } + return null; +} + +/** + * Execution of a "direct" dispatch - there must be at most one dispatch + * accumulated on the event or it is considered an error. It doesn't really make + * sense for an event with multiple dispatches (bubbled) to keep track of the + * return values at each dispatch execution, but it does tend to make sense when + * dealing with "direct" dispatches. + * + * @return The return value of executing the single dispatch. + */ +function executeDirectDispatch(event) { + if (true) { + validateEventDispatches(event); + } + var dispatchListener = event._dispatchListeners; + var dispatchID = event._dispatchIDs; + invariant( + !Array.isArray(dispatchListener), + 'executeDirectDispatch(...): Invalid `event`.' + ); + var res = dispatchListener ? + dispatchListener(event, dispatchID) : + null; + event._dispatchListeners = null; + event._dispatchIDs = null; + return res; +} + +/** + * @param {SyntheticEvent} event + * @return {bool} True iff number of dispatches accumulated is greater than 0. + */ +function hasDispatches(event) { + return !!event._dispatchListeners; +} + +/** + * General utilities that are useful in creating custom Event Plugins. + */ +var EventPluginUtils = { + isEndish: isEndish, + isMoveish: isMoveish, + isStartish: isStartish, + executeDispatchesInOrder: executeDispatchesInOrder, + executeDispatchesInOrderStopAtTrue: executeDispatchesInOrderStopAtTrue, + executeDirectDispatch: executeDirectDispatch, + hasDispatches: hasDispatches, + executeDispatch: executeDispatch +}; + +module.exports = EventPluginUtils; + +},{"./EventConstants":15,"./invariant":106}],20:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule EventPropagators + */ + +"use strict"; + +var CallbackRegistry = require("./CallbackRegistry"); +var EventConstants = require("./EventConstants"); + +var accumulate = require("./accumulate"); +var forEachAccumulated = require("./forEachAccumulated"); +var getListener = CallbackRegistry.getListener; +var PropagationPhases = EventConstants.PropagationPhases; + +/** + * Injected dependencies: + */ + +/** + * - `InstanceHandle`: [required] Module that performs logical traversals of DOM + * hierarchy given ids of the logical DOM elements involved. + */ +var injection = { + InstanceHandle: null, + injectInstanceHandle: function(InjectedInstanceHandle) { + injection.InstanceHandle = InjectedInstanceHandle; + if (true) { + injection.validate(); + } + }, + validate: function() { + var invalid = !injection.InstanceHandle|| + !injection.InstanceHandle.traverseTwoPhase || + !injection.InstanceHandle.traverseEnterLeave; + if (invalid) { + throw new Error('InstanceHandle not injected before use!'); + } + } +}; + +/** + * Some event types have a notion of different registration names for different + * "phases" of propagation. This finds listeners by a given phase. + */ +function listenerAtPhase(id, event, propagationPhase) { + var registrationName = + event.dispatchConfig.phasedRegistrationNames[propagationPhase]; + return getListener(id, registrationName); +} + +/** + * Tags a `SyntheticEvent` with dispatched listeners. Creating this function + * here, allows us to not have to bind or create functions for each event. + * Mutating the event's members allows us to not have to create a wrapping + * "dispatch" object that pairs the event with the listener. + */ +function accumulateDirectionalDispatches(domID, upwards, event) { + if (true) { + if (!domID) { + throw new Error('Dispatching id must not be null'); + } + injection.validate(); + } + var phase = upwards ? PropagationPhases.bubbled : PropagationPhases.captured; + var listener = listenerAtPhase(domID, event, phase); + if (listener) { + event._dispatchListeners = accumulate(event._dispatchListeners, listener); + event._dispatchIDs = accumulate(event._dispatchIDs, domID); + } +} + +/** + * Collect dispatches (must be entirely collected before dispatching - see unit + * tests). Lazily allocate the array to conserve memory. We must loop through + * each event and perform the traversal for each one. We can not perform a + * single traversal for the entire collection of events because each event may + * have a different target. + */ +function accumulateTwoPhaseDispatchesSingle(event) { + if (event && event.dispatchConfig.phasedRegistrationNames) { + injection.InstanceHandle.traverseTwoPhase( + event.dispatchMarker, + accumulateDirectionalDispatches, + event + ); + } +} + + +/** + * Accumulates without regard to direction, does not look for phased + * registration names. Same as `accumulateDirectDispatchesSingle` but without + * requiring that the `dispatchMarker` be the same as the dispatched ID. + */ +function accumulateDispatches(id, ignoredDirection, event) { + if (event && event.dispatchConfig.registrationName) { + var registrationName = event.dispatchConfig.registrationName; + var listener = getListener(id, registrationName); + if (listener) { + event._dispatchListeners = accumulate(event._dispatchListeners, listener); + event._dispatchIDs = accumulate(event._dispatchIDs, id); + } + } +} + +/** + * Accumulates dispatches on an `SyntheticEvent`, but only for the + * `dispatchMarker`. + * @param {SyntheticEvent} event + */ +function accumulateDirectDispatchesSingle(event) { + if (event && event.dispatchConfig.registrationName) { + accumulateDispatches(event.dispatchMarker, null, event); + } +} + +function accumulateTwoPhaseDispatches(events) { + if (true) { + injection.validate(); + } + forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle); +} + +function accumulateEnterLeaveDispatches(leave, enter, fromID, toID) { + if (true) { + injection.validate(); + } + injection.InstanceHandle.traverseEnterLeave( + fromID, + toID, + accumulateDispatches, + leave, + enter + ); +} + + +function accumulateDirectDispatches(events) { + if (true) { + injection.validate(); + } + forEachAccumulated(events, accumulateDirectDispatchesSingle); +} + + + +/** + * A small set of propagation patterns, each of which will accept a small amount + * of information, and generate a set of "dispatch ready event objects" - which + * are sets of events that have already been annotated with a set of dispatched + * listener functions/ids. The API is designed this way to discourage these + * propagation strategies from actually executing the dispatches, since we + * always want to collect the entire set of dispatches before executing event a + * single one. + * + * @constructor EventPropagators + */ +var EventPropagators = { + accumulateTwoPhaseDispatches: accumulateTwoPhaseDispatches, + accumulateDirectDispatches: accumulateDirectDispatches, + accumulateEnterLeaveDispatches: accumulateEnterLeaveDispatches, + injection: injection +}; + +module.exports = EventPropagators; + +},{"./CallbackRegistry":5,"./EventConstants":15,"./accumulate":84,"./forEachAccumulated":97}],21:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule ExecutionEnvironment + */ + +/*jslint evil: true */ + +"use strict"; + +var canUseDOM = typeof window !== 'undefined'; + +/** + * Simple, lightweight module assisting with the detection and context of + * Worker. Helps avoid circular dependencies and allows code to reason about + * whether or not they are in a Worker, even if they never include the main + * `ReactWorker` dependency. + */ +var ExecutionEnvironment = { + + canUseDOM: canUseDOM, + + canUseWorkers: typeof Worker !== 'undefined', + + isInWorker: !canUseDOM // For now, this is true - might change in the future. + +}; + +module.exports = ExecutionEnvironment; + +},{}],22:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule LinkedStateMixin + * @typechecks static-only + */ + +"use strict"; + +var ReactLink = require("./ReactLink"); +var ReactStateSetters = require("./ReactStateSetters"); + +/** + * A simple mixin around ReactLink.forState(). + */ +var LinkedStateMixin = { + /** + * Create a ReactLink that's linked to part of this component's state. The + * ReactLink will have the current value of this.state[key] and will call + * setState() when a change is requested. + * + * @param {string} key state key to update. Note: you may want to use keyOf() + * if you're using Google Closure Compiler advanced mode. + * @return {ReactLink} ReactLink instance linking to the state. + */ + linkState: function(key) { + return new ReactLink( + this.state[key], + ReactStateSetters.createStateKeySetter(this, key) + ); + } +}; + +module.exports = LinkedStateMixin; + +},{"./ReactLink":51,"./ReactStateSetters":63}],23:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule LinkedValueMixin + * @typechecks static-only + */ + +"use strict"; + +var invariant = require("./invariant"); + +/** + * Provide a linked `value` attribute for controlled forms. You should not use + * this outside of the ReactDOM controlled form components. + */ +var LinkedValueMixin = { + _assertLink: function() { + invariant( + this.props.value == null && this.props.onChange == null, + 'Cannot provide a valueLink and a value or onChange event. If you ' + + 'want to use value or onChange, you probably don\'t want to use ' + + 'valueLink' + ); + }, + + /** + * @return {*} current value of the input either from value prop or link. + */ + getValue: function() { + if (this.props.valueLink) { + this._assertLink(); + return this.props.valueLink.value; + } + return this.props.value; + }, + + /** + * @return {function} change callback either from onChange prop or link. + */ + getOnChange: function() { + if (this.props.valueLink) { + this._assertLink(); + return this._handleLinkedValueChange; + } + return this.props.onChange; + }, + + /** + * @param {SyntheticEvent} e change event to handle + */ + _handleLinkedValueChange: function(e) { + this.props.valueLink.requestChange(e.target.value); + } +}; + +module.exports = LinkedValueMixin; + +},{"./invariant":106}],24:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule MobileSafariClickEventPlugin + * @typechecks static-only + */ + +"use strict"; + +var EventConstants = require("./EventConstants"); + +var emptyFunction = require("./emptyFunction"); + +var topLevelTypes = EventConstants.topLevelTypes; + +/** + * Mobile Safari does not fire properly bubble click events on non-interactive + * elements, which means delegated click listeners do not fire. The workaround + * for this bug involves attaching an empty click listener on the target node. + * + * This particular plugin works around the bug by attaching an empty click + * listener on `touchstart` (which does fire on every element). + */ +var MobileSafariClickEventPlugin = { + + eventTypes: null, + + /** + * @param {string} topLevelType Record from `EventConstants`. + * @param {DOMEventTarget} topLevelTarget The listening component root node. + * @param {string} topLevelTargetID ID of `topLevelTarget`. + * @param {object} nativeEvent Native browser event. + * @return {*} An accumulation of synthetic events. + * @see {EventPluginHub.extractEvents} + */ + extractEvents: function( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent) { + if (topLevelType === topLevelTypes.topTouchStart) { + var target = nativeEvent.target; + if (target && !target.onclick) { + target.onclick = emptyFunction; + } + } + } + +}; + +module.exports = MobileSafariClickEventPlugin; + +},{"./EventConstants":15,"./emptyFunction":92}],25:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule PooledClass + */ + +"use strict"; + +/** + * Static poolers. Several custom versions for each potential number of + * arguments. A completely generic pooler is easy to implement, but would + * require accessing the `arguments` object. In each of these, `this` refers to + * the Class itself, not an instance. If any others are needed, simply add them + * here, or in their own files. + */ +var oneArgumentPooler = function(copyFieldsFrom) { + var Klass = this; + if (Klass.instancePool.length) { + var instance = Klass.instancePool.pop(); + Klass.call(instance, copyFieldsFrom); + return instance; + } else { + return new Klass(copyFieldsFrom); + } +}; + +var twoArgumentPooler = function(a1, a2) { + var Klass = this; + if (Klass.instancePool.length) { + var instance = Klass.instancePool.pop(); + Klass.call(instance, a1, a2); + return instance; + } else { + return new Klass(a1, a2); + } +}; + +var threeArgumentPooler = function(a1, a2, a3) { + var Klass = this; + if (Klass.instancePool.length) { + var instance = Klass.instancePool.pop(); + Klass.call(instance, a1, a2, a3); + return instance; + } else { + return new Klass(a1, a2, a3); + } +}; + +var fiveArgumentPooler = function(a1, a2, a3, a4, a5) { + var Klass = this; + if (Klass.instancePool.length) { + var instance = Klass.instancePool.pop(); + Klass.call(instance, a1, a2, a3, a4, a5); + return instance; + } else { + return new Klass(a1, a2, a3, a4, a5); + } +}; + +var standardReleaser = function(instance) { + var Klass = this; + if (instance.destructor) { + instance.destructor(); + } + if (Klass.instancePool.length < Klass.poolSize) { + Klass.instancePool.push(instance); + } +}; + +var DEFAULT_POOL_SIZE = 10; +var DEFAULT_POOLER = oneArgumentPooler; + +/** + * Augments `CopyConstructor` to be a poolable class, augmenting only the class + * itself (statically) not adding any prototypical fields. Any CopyConstructor + * you give this may have a `poolSize` property, and will look for a + * prototypical `destructor` on instances (optional). + * + * @param {Function} CopyConstructor Constructor that can be used to reset. + * @param {Function} pooler Customizable pooler. + */ +var addPoolingTo = function(CopyConstructor, pooler) { + var NewKlass = CopyConstructor; + NewKlass.instancePool = []; + NewKlass.getPooled = pooler || DEFAULT_POOLER; + if (!NewKlass.poolSize) { + NewKlass.poolSize = DEFAULT_POOL_SIZE; + } + NewKlass.release = standardReleaser; + return NewKlass; +}; + +var PooledClass = { + addPoolingTo: addPoolingTo, + oneArgumentPooler: oneArgumentPooler, + twoArgumentPooler: twoArgumentPooler, + threeArgumentPooler: threeArgumentPooler, + fiveArgumentPooler: fiveArgumentPooler +}; + +module.exports = PooledClass; + +},{}],26:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule React + */ + +"use strict"; + +var ReactComponent = require("./ReactComponent"); +var ReactCompositeComponent = require("./ReactCompositeComponent"); +var ReactCurrentOwner = require("./ReactCurrentOwner"); +var ReactDOM = require("./ReactDOM"); +var ReactDOMComponent = require("./ReactDOMComponent"); +var ReactDefaultInjection = require("./ReactDefaultInjection"); +var ReactInstanceHandles = require("./ReactInstanceHandles"); +var ReactMount = require("./ReactMount"); +var ReactMultiChild = require("./ReactMultiChild"); +var ReactPerf = require("./ReactPerf"); +var ReactPropTypes = require("./ReactPropTypes"); +var ReactServerRendering = require("./ReactServerRendering"); +var ReactTextComponent = require("./ReactTextComponent"); + +ReactDefaultInjection.inject(); + +var React = { + DOM: ReactDOM, + PropTypes: ReactPropTypes, + initializeTouchEvents: function(shouldUseTouch) { + ReactMount.useTouchEvents = shouldUseTouch; + }, + createClass: ReactCompositeComponent.createClass, + constructAndRenderComponent: ReactMount.constructAndRenderComponent, + constructAndRenderComponentByID: ReactMount.constructAndRenderComponentByID, + renderComponent: ReactPerf.measure( + 'React', + 'renderComponent', + ReactMount.renderComponent + ), + renderComponentToString: ReactServerRendering.renderComponentToString, + unmountComponentAtNode: ReactMount.unmountComponentAtNode, + unmountAndReleaseReactRootNode: ReactMount.unmountAndReleaseReactRootNode, + isValidClass: ReactCompositeComponent.isValidClass, + isValidComponent: ReactComponent.isValidComponent, + __internals: { + Component: ReactComponent, + CurrentOwner: ReactCurrentOwner, + DOMComponent: ReactDOMComponent, + InstanceHandles: ReactInstanceHandles, + Mount: ReactMount, + MultiChild: ReactMultiChild, + TextComponent: ReactTextComponent + } +}; + +// Version exists only in the open-source version of React, not in Facebook's +// internal version. +React.version = '0.5.1'; + +module.exports = React; + +},{"./ReactComponent":28,"./ReactCompositeComponent":31,"./ReactCurrentOwner":32,"./ReactDOM":33,"./ReactDOMComponent":35,"./ReactDefaultInjection":44,"./ReactInstanceHandles":50,"./ReactMount":53,"./ReactMultiChild":55,"./ReactPerf":58,"./ReactPropTypes":60,"./ReactServerRendering":62,"./ReactTextComponent":64}],27:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule ReactChildren + */ + +"use strict"; + +var PooledClass = require("./PooledClass"); + +var invariant = require("./invariant"); +var traverseAllChildren = require("./traverseAllChildren"); + +var twoArgumentPooler = PooledClass.twoArgumentPooler; +var threeArgumentPooler = PooledClass.threeArgumentPooler; + +/** + * PooledClass representing the bookkeeping associated with performing a child + * traversal. Allows avoiding binding callbacks. + * + * @constructor ForEachBookKeeping + * @param {!function} forEachFunction Function to perform traversal with. + * @param {?*} forEachContext Context to perform context with. + */ +function ForEachBookKeeping(forEachFunction, forEachContext) { + this.forEachFunction = forEachFunction; + this.forEachContext = forEachContext; +} +PooledClass.addPoolingTo(ForEachBookKeeping, twoArgumentPooler); + +function forEachSingleChild(traverseContext, child, name, i) { + var forEachBookKeeping = traverseContext; + forEachBookKeeping.forEachFunction.call( + forEachBookKeeping.forEachContext, child, i); +} + +/** + * Iterates through children that are typically specified as `props.children`. + * + * The provided forEachFunc(child, index) will be called for each + * leaf child. + * + * @param {array} children + * @param {function(*, int)} forEachFunc. + * @param {*} forEachContext Context for forEachContext. + */ +function forEachChildren(children, forEachFunc, forEachContext) { + if (children == null) { + return children; + } + + var traverseContext = + ForEachBookKeeping.getPooled(forEachFunc, forEachContext); + traverseAllChildren(children, forEachSingleChild, traverseContext); + ForEachBookKeeping.release(traverseContext); +} + +/** + * PooledClass representing the bookkeeping associated with performing a child + * mapping. Allows avoiding binding callbacks. + * + * @constructor MapBookKeeping + * @param {!*} mapResult Object containing the ordered map of results. + * @param {!function} mapFunction Function to perform mapping with. + * @param {?*} mapContext Context to perform mapping with. + */ +function MapBookKeeping(mapResult, mapFunction, mapContext) { + this.mapResult = mapResult; + this.mapFunction = mapFunction; + this.mapContext = mapContext; +} +PooledClass.addPoolingTo(MapBookKeeping, threeArgumentPooler); + +function mapSingleChildIntoContext(traverseContext, child, name, i) { + var mapBookKeeping = traverseContext; + var mapResult = mapBookKeeping.mapResult; + var mappedChild = + mapBookKeeping.mapFunction.call(mapBookKeeping.mapContext, child, i); + // We found a component instance + invariant( + !mapResult.hasOwnProperty(name), + 'ReactChildren.map(...): Encountered two children with the same key, ' + + '`%s`. Children keys must be unique.', + name + ); + mapResult[name] = mappedChild; +} + +/** + * Maps children that are typically specified as `props.children`. + * + * The provided mapFunction(child, key, index) will be called for each + * leaf child. + * + * TODO: This may likely break any calls to `ReactChildren.map` that were + * previously relying on the fact that we guarded against null children. + * + * @param {array} children + * @param {function(*, int)} mapFunction. + * @param {*} mapContext Context for mapFunction. + * @return {array} mirrored array with mapped children. + */ +function mapChildren(children, func, context) { + if (children == null) { + return children; + } + + var mapResult = {}; + var traverseContext = MapBookKeeping.getPooled(mapResult, func, context); + traverseAllChildren(children, mapSingleChildIntoContext, traverseContext); + MapBookKeeping.release(traverseContext); + return mapResult; +} + +var ReactChildren = { + forEach: forEachChildren, + map: mapChildren +}; + +module.exports = ReactChildren; + +},{"./PooledClass":25,"./invariant":106,"./traverseAllChildren":125}],28:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule ReactComponent + */ + +"use strict"; + +var ReactComponentEnvironment = require("./ReactComponentEnvironment"); +var ReactCurrentOwner = require("./ReactCurrentOwner"); +var ReactOwner = require("./ReactOwner"); +var ReactUpdates = require("./ReactUpdates"); + +var invariant = require("./invariant"); +var keyMirror = require("./keyMirror"); +var merge = require("./merge"); + +/** + * Every React component is in one of these life cycles. + */ +var ComponentLifeCycle = keyMirror({ + /** + * Mounted components have a DOM node representation and are capable of + * receiving new props. + */ + MOUNTED: null, + /** + * Unmounted components are inactive and cannot receive new props. + */ + UNMOUNTED: null +}); + +/** + * Warn if there's no key explicitly set on dynamic arrays of children. + * This allows us to keep track of children between updates. + */ + +var ownerHasWarned = {}; + +/** + * Warn if the component doesn't have an explicit key assigned to it. + * This component is in an array. The array could grow and shrink or be + * reordered. All children, that hasn't already been validated, are required to + * have a "key" property assigned to it. + * + * @internal + * @param {ReactComponent} component Component that requires a key. + */ +function validateExplicitKey(component) { + if (component.__keyValidated__ || component.props.key != null) { + return; + } + component.__keyValidated__ = true; + + // We can't provide friendly warnings for top level components. + if (!ReactCurrentOwner.current) { + return; + } + + // Name of the component whose render method tried to pass children. + var currentName = ReactCurrentOwner.current.constructor.displayName; + if (ownerHasWarned.hasOwnProperty(currentName)) { + return; + } + ownerHasWarned[currentName] = true; + + var message = 'Each child in an array should have a unique "key" prop. ' + + 'Check the render method of ' + currentName + '.'; + if (!component.isOwnedBy(ReactCurrentOwner.current)) { + // Name of the component that originally created this child. + var childOwnerName = + component.props.__owner__ && + component.props.__owner__.constructor.displayName; + + // Usually the current owner is the offender, but if it accepts + // children as a property, it may be the creator of the child that's + // responsible for assigning it a key. + message += ' It was passed a child from ' + childOwnerName + '.'; + } + + console.warn(message); +} + +/** + * Ensure that every component either is passed in a static location or, if + * if it's passed in an array, has an explicit key property defined. + * + * @internal + * @param {*} component Statically passed child of any type. + * @return {boolean} + */ +function validateChildKeys(component) { + if (Array.isArray(component)) { + for (var i = 0; i < component.length; i++) { + var child = component[i]; + if (ReactComponent.isValidComponent(child)) { + validateExplicitKey(child); + } + } + } else if (ReactComponent.isValidComponent(component)) { + // This component was passed in a valid location. + component.__keyValidated__ = true; + } +} + +/** + * Components are the basic units of composition in React. + * + * Every component accepts a set of keyed input parameters known as "props" that + * are initialized by the constructor. Once a component is mounted, the props + * can be mutated using `setProps` or `replaceProps`. + * + * Every component is capable of the following operations: + * + * `mountComponent` + * Initializes the component, renders markup, and registers event listeners. + * + * `receiveProps` + * Updates the rendered DOM nodes given a new set of props. + * + * `unmountComponent` + * Releases any resources allocated by this component. + * + * Components can also be "owned" by other components. Being owned by another + * component means being constructed by that component. This is different from + * being the child of a component, which means having a DOM representation that + * is a child of the DOM representation of that component. + * + * @class ReactComponent + */ +var ReactComponent = { + + /** + * @param {?object} object + * @return {boolean} True if `object` is a valid component. + * @final + */ + isValidComponent: function(object) { + return !!( + object && + typeof object.mountComponentIntoNode === 'function' && + typeof object.receiveProps === 'function' + ); + }, + + /** + * Generate a key string that identifies a component within a set. + * + * @param {*} component A component that could contain a manual key. + * @param {number} index Index that is used if a manual key is not provided. + * @return {string} + * @internal + */ + getKey: function(component, index) { + if (component && component.props && component.props.key != null) { + // Explicit key + return '{' + component.props.key + '}'; + } + // Implicit key determined by the index in the set + return '[' + index + ']'; + }, + + /** + * @internal + */ + LifeCycle: ComponentLifeCycle, + + /** + * Injected module that provides ability to mutate individual properties. + * Injected into the base class because many different subclasses need access + * to this. + * + * @internal + */ + DOMIDOperations: ReactComponentEnvironment.DOMIDOperations, + + /** + * Optionally injectable environment dependent cleanup hook. (server vs. + * browser etc). Example: A browser system caches DOM nodes based on component + * ID and must remove that cache entry when this instance is unmounted. + * + * @private + */ + unmountIDFromEnvironment: ReactComponentEnvironment.unmountIDFromEnvironment, + + /** + * The "image" of a component tree, is the platform specific (typically + * serialized) data that represents a tree of lower level UI building blocks. + * On the web, this "image" is HTML markup which describes a construction of + * low level `div` and `span` nodes. Other platforms may have different + * encoding of this "image". This must be injected. + * + * @private + */ + mountImageIntoNode: ReactComponentEnvironment.mountImageIntoNode, + + /** + * React references `ReactReconcileTransaction` using this property in order + * to allow dependency injection. + * + * @internal + */ + ReactReconcileTransaction: + ReactComponentEnvironment.ReactReconcileTransaction, + + /** + * Base functionality for every ReactComponent constructor. Mixed into the + * `ReactComponent` prototype, but exposed statically for easy access. + * + * @lends {ReactComponent.prototype} + */ + Mixin: merge(ReactComponentEnvironment.Mixin, { + + /** + * Checks whether or not this component is mounted. + * + * @return {boolean} True if mounted, false otherwise. + * @final + * @protected + */ + isMounted: function() { + return this._lifeCycleState === ComponentLifeCycle.MOUNTED; + }, + + /** + * Sets a subset of the props. + * + * @param {object} partialProps Subset of the next props. + * @param {?function} callback Called after props are updated. + * @final + * @public + */ + setProps: function(partialProps, callback) { + // Merge with `_pendingProps` if it exists, otherwise with existing props. + this.replaceProps( + merge(this._pendingProps || this.props, partialProps), + callback + ); + }, + + /** + * Replaces all of the props. + * + * @param {object} props New props. + * @param {?function} callback Called after props are updated. + * @final + * @public + */ + replaceProps: function(props, callback) { + invariant( + !this.props.__owner__, + 'replaceProps(...): You called `setProps` or `replaceProps` on a ' + + 'component with an owner. This is an anti-pattern since props will ' + + 'get reactively updated when rendered. Instead, change the owner\'s ' + + '`render` method to pass the correct value as props to the component ' + + 'where it is created.' + ); + invariant( + this.isMounted(), + 'replaceProps(...): Can only update a mounted component.' + ); + this._pendingProps = props; + ReactUpdates.enqueueUpdate(this, callback); + }, + + /** + * Base constructor for all React component. + * + * Subclasses that override this method should make sure to invoke + * `ReactComponent.Mixin.construct.call(this, ...)`. + * + * @param {?object} initialProps + * @param {*} children + * @internal + */ + construct: function(initialProps, children) { + this.props = initialProps || {}; + // Record the component responsible for creating this component. + this.props.__owner__ = ReactCurrentOwner.current; + // All components start unmounted. + this._lifeCycleState = ComponentLifeCycle.UNMOUNTED; + + this._pendingProps = null; + this._pendingCallbacks = null; + + // Children can be more than one argument + var childrenLength = arguments.length - 1; + if (childrenLength === 1) { + if (true) { + validateChildKeys(children); + } + this.props.children = children; + } else if (childrenLength > 1) { + var childArray = Array(childrenLength); + for (var i = 0; i < childrenLength; i++) { + if (true) { + validateChildKeys(arguments[i + 1]); + } + childArray[i] = arguments[i + 1]; + } + this.props.children = childArray; + } + }, + + /** + * Initializes the component, renders markup, and registers event listeners. + * + * NOTE: This does not insert any nodes into the DOM. + * + * Subclasses that override this method should make sure to invoke + * `ReactComponent.Mixin.mountComponent.call(this, ...)`. + * + * @param {string} rootID DOM ID of the root node. + * @param {ReactReconcileTransaction} transaction + * @param {number} mountDepth number of components in the owner hierarchy. + * @return {?string} Rendered markup to be inserted into the DOM. + * @internal + */ + mountComponent: function(rootID, transaction, mountDepth) { + invariant( + !this.isMounted(), + 'mountComponent(%s, ...): Can only mount an unmounted component.', + rootID + ); + var props = this.props; + if (props.ref != null) { + ReactOwner.addComponentAsRefTo(this, props.ref, props.__owner__); + } + this._rootNodeID = rootID; + this._lifeCycleState = ComponentLifeCycle.MOUNTED; + this._mountDepth = mountDepth; + // Effectively: return ''; + }, + + /** + * Releases any resources allocated by `mountComponent`. + * + * NOTE: This does not remove any nodes from the DOM. + * + * Subclasses that override this method should make sure to invoke + * `ReactComponent.Mixin.unmountComponent.call(this)`. + * + * @internal + */ + unmountComponent: function() { + invariant( + this.isMounted(), + 'unmountComponent(): Can only unmount a mounted component.' + ); + var props = this.props; + if (props.ref != null) { + ReactOwner.removeComponentAsRefFrom(this, props.ref, props.__owner__); + } + ReactComponent.unmountIDFromEnvironment(this._rootNodeID); + this._rootNodeID = null; + this._lifeCycleState = ComponentLifeCycle.UNMOUNTED; + }, + + /** + * Updates the rendered DOM nodes given a new set of props. + * + * Subclasses that override this method should make sure to invoke + * `ReactComponent.Mixin.receiveProps.call(this, ...)`. + * + * @param {object} nextProps Next set of properties. + * @param {ReactReconcileTransaction} transaction + * @internal + */ + receiveProps: function(nextProps, transaction) { + invariant( + this.isMounted(), + 'receiveProps(...): Can only update a mounted component.' + ); + this._pendingProps = nextProps; + this._performUpdateIfNecessary(transaction); + }, + + /** + * Call `_performUpdateIfNecessary` within a new transaction. + * + * @param {ReactReconcileTransaction} transaction + * @internal + */ + performUpdateIfNecessary: function() { + var transaction = ReactComponent.ReactReconcileTransaction.getPooled(); + transaction.perform(this._performUpdateIfNecessary, this, transaction); + ReactComponent.ReactReconcileTransaction.release(transaction); + }, + + /** + * If `_pendingProps` is set, update the component. + * + * @param {ReactReconcileTransaction} transaction + * @internal + */ + _performUpdateIfNecessary: function(transaction) { + if (this._pendingProps == null) { + return; + } + var prevProps = this.props; + this.props = this._pendingProps; + this._pendingProps = null; + this.updateComponent(transaction, prevProps); + }, + + /** + * Updates the component's currently mounted representation. + * + * @param {ReactReconcileTransaction} transaction + * @param {object} prevProps + * @internal + */ + updateComponent: function(transaction, prevProps) { + var props = this.props; + // If either the owner or a `ref` has changed, make sure the newest owner + // has stored a reference to `this`, and the previous owner (if different) + // has forgotten the reference to `this`. + if (props.__owner__ !== prevProps.__owner__ || + props.ref !== prevProps.ref) { + if (prevProps.ref != null) { + ReactOwner.removeComponentAsRefFrom( + this, prevProps.ref, prevProps.__owner__ + ); + } + // Correct, even if the owner is the same, and only the ref has changed. + if (props.ref != null) { + ReactOwner.addComponentAsRefTo(this, props.ref, props.__owner__); + } + } + }, + + /** + * Mounts this component and inserts it into the DOM. + * + * @param {string} rootID DOM ID of the root node. + * @param {DOMElement} container DOM element to mount into. + * @param {boolean} shouldReuseMarkup If true, do not insert markup + * @final + * @internal + * @see {ReactMount.renderComponent} + */ + mountComponentIntoNode: function(rootID, container, shouldReuseMarkup) { + var transaction = ReactComponent.ReactReconcileTransaction.getPooled(); + transaction.perform( + this._mountComponentIntoNode, + this, + rootID, + container, + transaction, + shouldReuseMarkup + ); + ReactComponent.ReactReconcileTransaction.release(transaction); + }, + + /** + * @param {string} rootID DOM ID of the root node. + * @param {DOMElement} container DOM element to mount into. + * @param {ReactReconcileTransaction} transaction + * @param {boolean} shouldReuseMarkup If true, do not insert markup + * @final + * @private + */ + _mountComponentIntoNode: function( + rootID, + container, + transaction, + shouldReuseMarkup) { + var markup = this.mountComponent(rootID, transaction, 0); + ReactComponent.mountImageIntoNode(markup, container, shouldReuseMarkup); + }, + + /** + * Checks if this component is owned by the supplied `owner` component. + * + * @param {ReactComponent} owner Component to check. + * @return {boolean} True if `owners` owns this component. + * @final + * @internal + */ + isOwnedBy: function(owner) { + return this.props.__owner__ === owner; + }, + + /** + * Gets another component, that shares the same owner as this one, by ref. + * + * @param {string} ref of a sibling Component. + * @return {?ReactComponent} the actual sibling Component. + * @final + * @internal + */ + getSiblingByRef: function(ref) { + var owner = this.props.__owner__; + if (!owner || !owner.refs) { + return null; + } + return owner.refs[ref]; + } + }) +}; + +module.exports = ReactComponent; + +},{"./ReactComponentEnvironment":30,"./ReactCurrentOwner":32,"./ReactOwner":57,"./ReactUpdates":69,"./invariant":106,"./keyMirror":112,"./merge":115}],29:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule ReactComponentBrowserEnvironment + */ + +/*jslint evil: true */ + +"use strict"; + +var ReactDOMIDOperations = require("./ReactDOMIDOperations"); +var ReactMarkupChecksum = require("./ReactMarkupChecksum"); +var ReactMount = require("./ReactMount"); +var ReactReconcileTransaction = require("./ReactReconcileTransaction"); + +var getReactRootElementInContainer = require("./getReactRootElementInContainer"); +var invariant = require("./invariant"); +var mutateHTMLNodeWithMarkup = require("./mutateHTMLNodeWithMarkup"); + + +var ELEMENT_NODE_TYPE = 1; +var DOC_NODE_TYPE = 9; + + +/** + * Abstracts away all functionality of `ReactComponent` requires knowledge of + * the browser context. + */ +var ReactComponentBrowserEnvironment = { + /** + * Mixed into every component instance. + */ + Mixin: { + /** + * Returns the DOM node rendered by this component. + * + * @return {DOMElement} The root node of this component. + * @final + * @protected + */ + getDOMNode: function() { + invariant( + this.isMounted(), + 'getDOMNode(): A component must be mounted to have a DOM node.' + ); + return ReactMount.getNode(this._rootNodeID); + } + }, + + ReactReconcileTransaction: ReactReconcileTransaction, + + DOMIDOperations: ReactDOMIDOperations, + + /** + * If a particular environment requires that some resources be cleaned up, + * specify this in the injected Mixin. In the DOM, we would likely want to + * purge any cached node ID lookups. + * + * @private + */ + unmountIDFromEnvironment: function(rootNodeID) { + ReactMount.purgeID(rootNodeID); + }, + + /** + * @param {string} markup Markup string to place into the DOM Element. + * @param {DOMElement} container DOM Element to insert markup into. + * @param {boolean} shouldReuseMarkup Should reuse the existing markup in the + * container if possible. + */ + mountImageIntoNode: function(markup, container, shouldReuseMarkup) { + invariant( + container && ( + container.nodeType === ELEMENT_NODE_TYPE || + container.nodeType === DOC_NODE_TYPE && ReactMount.allowFullPageRender + ), + 'mountComponentIntoNode(...): Target container is not valid.' + ); + if (shouldReuseMarkup) { + if (ReactMarkupChecksum.canReuseMarkup( + markup, + getReactRootElementInContainer(container))) { + return; + } else { + if (true) { + console.warn( + 'React attempted to use reuse markup in a container but the ' + + 'checksum was invalid. This generally means that you are using ' + + 'server rendering and the markup generated on the server was ' + + 'not what the client was expecting. React injected new markup ' + + 'to compensate which works but you have lost many of the ' + + 'benefits of server rendering. Instead, figure out why the ' + + 'markup being generated is different on the client or server.' + ); + } + } + } + + // You can't naively set the innerHTML of the entire document. You need + // to mutate documentElement which requires doing some crazy tricks. See + // mutateHTMLNodeWithMarkup() + if (container.nodeType === DOC_NODE_TYPE) { + mutateHTMLNodeWithMarkup(container.documentElement, markup); + return; + } + + // Asynchronously inject markup by ensuring that the container is not in + // the document when settings its `innerHTML`. + var parent = container.parentNode; + if (parent) { + var next = container.nextSibling; + parent.removeChild(container); + container.innerHTML = markup; + if (next) { + parent.insertBefore(container, next); + } else { + parent.appendChild(container); + } + } else { + container.innerHTML = markup; + } + } +}; + +module.exports = ReactComponentBrowserEnvironment; + +},{"./ReactDOMIDOperations":37,"./ReactMarkupChecksum":52,"./ReactMount":53,"./ReactReconcileTransaction":61,"./getReactRootElementInContainer":103,"./invariant":106,"./mutateHTMLNodeWithMarkup":119}],30:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule ReactComponentEnvironment + */ + +var ReactComponentBrowserEnvironment = + require("./ReactComponentBrowserEnvironment"); + +var ReactComponentEnvironment = ReactComponentBrowserEnvironment; + +module.exports = ReactComponentEnvironment; + +},{"./ReactComponentBrowserEnvironment":29}],31:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule ReactCompositeComponent + */ + +"use strict"; + +var ReactComponent = require("./ReactComponent"); +var ReactCurrentOwner = require("./ReactCurrentOwner"); +var ReactOwner = require("./ReactOwner"); +var ReactPerf = require("./ReactPerf"); +var ReactPropTransferer = require("./ReactPropTransferer"); +var ReactUpdates = require("./ReactUpdates"); + +var invariant = require("./invariant"); +var keyMirror = require("./keyMirror"); +var merge = require("./merge"); +var mixInto = require("./mixInto"); +var objMap = require("./objMap"); + +/** + * Policies that describe methods in `ReactCompositeComponentInterface`. + */ +var SpecPolicy = keyMirror({ + /** + * These methods may be defined only once by the class specification or mixin. + */ + DEFINE_ONCE: null, + /** + * These methods may be defined by both the class specification and mixins. + * Subsequent definitions will be chained. These methods must return void. + */ + DEFINE_MANY: null, + /** + * These methods are overriding the base ReactCompositeComponent class. + */ + OVERRIDE_BASE: null, + /** + * These methods are similar to DEFINE_MANY, except we assume they return + * objects. We try to merge the keys of the return values of all the mixed in + * functions. If there is a key conflict we throw. + */ + DEFINE_MANY_MERGED: null +}); + +/** + * Composite components are higher-level components that compose other composite + * or native components. + * + * To create a new type of `ReactCompositeComponent`, pass a specification of + * your new class to `React.createClass`. The only requirement of your class + * specification is that you implement a `render` method. + * + * var MyComponent = React.createClass({ + * render: function() { + * return
Hello World
; + * } + * }); + * + * The class specification supports a specific protocol of methods that have + * special meaning (e.g. `render`). See `ReactCompositeComponentInterface` for + * more the comprehensive protocol. Any other properties and methods in the + * class specification will available on the prototype. + * + * @interface ReactCompositeComponentInterface + * @internal + */ +var ReactCompositeComponentInterface = { + + /** + * An array of Mixin objects to include when defining your component. + * + * @type {array} + * @optional + */ + mixins: SpecPolicy.DEFINE_MANY, + + /** + * Definition of prop types for this component. + * + * @type {object} + * @optional + */ + propTypes: SpecPolicy.DEFINE_ONCE, + + + + // ==== Definition methods ==== + + /** + * Invoked when the component is mounted. Values in the mapping will be set on + * `this.props` if that prop is not specified (i.e. using an `in` check). + * + * This method is invoked before `getInitialState` and therefore cannot rely + * on `this.state` or use `this.setState`. + * + * @return {object} + * @optional + */ + getDefaultProps: SpecPolicy.DEFINE_MANY_MERGED, + + /** + * Invoked once before the component is mounted. The return value will be used + * as the initial value of `this.state`. + * + * getInitialState: function() { + * return { + * isOn: false, + * fooBaz: new BazFoo() + * } + * } + * + * @return {object} + * @optional + */ + getInitialState: SpecPolicy.DEFINE_MANY_MERGED, + + /** + * Uses props from `this.props` and state from `this.state` to render the + * structure of the component. + * + * No guarantees are made about when or how often this method is invoked, so + * it must not have side effects. + * + * render: function() { + * var name = this.props.name; + * return
Hello, {name}!
; + * } + * + * @return {ReactComponent} + * @nosideeffects + * @required + */ + render: SpecPolicy.DEFINE_ONCE, + + + + // ==== Delegate methods ==== + + /** + * Invoked when the component is initially created and about to be mounted. + * This may have side effects, but any external subscriptions or data created + * by this method must be cleaned up in `componentWillUnmount`. + * + * @optional + */ + componentWillMount: SpecPolicy.DEFINE_MANY, + + /** + * Invoked when the component has been mounted and has a DOM representation. + * However, there is no guarantee that the DOM node is in the document. + * + * Use this as an opportunity to operate on the DOM when the component has + * been mounted (initialized and rendered) for the first time. + * + * @param {DOMElement} rootNode DOM element representing the component. + * @optional + */ + componentDidMount: SpecPolicy.DEFINE_MANY, + + /** + * Invoked before the component receives new props. + * + * Use this as an opportunity to react to a prop transition by updating the + * state using `this.setState`. Current props are accessed via `this.props`. + * + * componentWillReceiveProps: function(nextProps) { + * this.setState({ + * likesIncreasing: nextProps.likeCount > this.props.likeCount + * }); + * } + * + * NOTE: There is no equivalent `componentWillReceiveState`. An incoming prop + * transition may cause a state change, but the opposite is not true. If you + * need it, you are probably looking for `componentWillUpdate`. + * + * @param {object} nextProps + * @optional + */ + componentWillReceiveProps: SpecPolicy.DEFINE_MANY, + + /** + * Invoked while deciding if the component should be updated as a result of + * receiving new props and state. + * + * Use this as an opportunity to `return false` when you're certain that the + * transition to the new props and state will not require a component update. + * + * shouldComponentUpdate: function(nextProps, nextState) { + * return !equal(nextProps, this.props) || !equal(nextState, this.state); + * } + * + * @param {object} nextProps + * @param {?object} nextState + * @return {boolean} True if the component should update. + * @optional + */ + shouldComponentUpdate: SpecPolicy.DEFINE_ONCE, + + /** + * Invoked when the component is about to update due to a transition from + * `this.props` and `this.state` to `nextProps` and `nextState`. + * + * Use this as an opportunity to perform preparation before an update occurs. + * + * NOTE: You **cannot** use `this.setState()` in this method. + * + * @param {object} nextProps + * @param {?object} nextState + * @param {ReactReconcileTransaction} transaction + * @optional + */ + componentWillUpdate: SpecPolicy.DEFINE_MANY, + + /** + * Invoked when the component's DOM representation has been updated. + * + * Use this as an opportunity to operate on the DOM when the component has + * been updated. + * + * @param {object} prevProps + * @param {?object} prevState + * @param {DOMElement} rootNode DOM element representing the component. + * @optional + */ + componentDidUpdate: SpecPolicy.DEFINE_MANY, + + /** + * Invoked when the component is about to be removed from its parent and have + * its DOM representation destroyed. + * + * Use this as an opportunity to deallocate any external resources. + * + * NOTE: There is no `componentDidUnmount` since your component will have been + * destroyed by that point. + * + * @optional + */ + componentWillUnmount: SpecPolicy.DEFINE_MANY, + + + + // ==== Advanced methods ==== + + /** + * Updates the component's currently mounted DOM representation. + * + * By default, this implements React's rendering and reconciliation algorithm. + * Sophisticated clients may wish to override this. + * + * @param {ReactReconcileTransaction} transaction + * @internal + * @overridable + */ + updateComponent: SpecPolicy.OVERRIDE_BASE + +}; + +/** + * Mapping from class specification keys to special processing functions. + * + * Although these are declared in the specification when defining classes + * using `React.createClass`, they will not be on the component's prototype. + */ +var RESERVED_SPEC_KEYS = { + displayName: function(Constructor, displayName) { + Constructor.displayName = displayName; + }, + mixins: function(Constructor, mixins) { + if (mixins) { + for (var i = 0; i < mixins.length; i++) { + mixSpecIntoComponent(Constructor, mixins[i]); + } + } + }, + propTypes: function(Constructor, propTypes) { + Constructor.propTypes = propTypes; + } +}; + +function validateMethodOverride(proto, name) { + var specPolicy = ReactCompositeComponentInterface[name]; + + // Disallow overriding of base class methods unless explicitly allowed. + if (ReactCompositeComponentMixin.hasOwnProperty(name)) { + invariant( + specPolicy === SpecPolicy.OVERRIDE_BASE, + 'ReactCompositeComponentInterface: You are attempting to override ' + + '`%s` from your class specification. Ensure that your method names ' + + 'do not overlap with React methods.', + name + ); + } + + // Disallow defining methods more than once unless explicitly allowed. + if (proto.hasOwnProperty(name)) { + invariant( + specPolicy === SpecPolicy.DEFINE_MANY || + specPolicy === SpecPolicy.DEFINE_MANY_MERGED, + 'ReactCompositeComponentInterface: You are attempting to define ' + + '`%s` on your component more than once. This conflict may be due ' + + 'to a mixin.', + name + ); + } +} + + +function validateLifeCycleOnReplaceState(instance) { + var compositeLifeCycleState = instance._compositeLifeCycleState; + invariant( + instance.isMounted() || + compositeLifeCycleState === CompositeLifeCycle.MOUNTING, + 'replaceState(...): Can only update a mounted or mounting component.' + ); + invariant( + compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_STATE && + compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING, + 'replaceState(...): Cannot update while unmounting component or during ' + + 'an existing state transition (such as within `render`).' + ); +} + +/** + * Custom version of `mixInto` which handles policy validation and reserved + * specification keys when building `ReactCompositeComponent` classses. + */ +function mixSpecIntoComponent(Constructor, spec) { + var proto = Constructor.prototype; + for (var name in spec) { + var property = spec[name]; + if (!spec.hasOwnProperty(name) || !property) { + continue; + } + validateMethodOverride(proto, name); + + if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) { + RESERVED_SPEC_KEYS[name](Constructor, property); + } else { + // Setup methods on prototype: + // The following member methods should not be automatically bound: + // 1. Expected ReactCompositeComponent methods (in the "interface"). + // 2. Overridden methods (that were mixed in). + var isCompositeComponentMethod = name in ReactCompositeComponentInterface; + var isInherited = name in proto; + var markedDontBind = property.__reactDontBind; + var isFunction = typeof property === 'function'; + var shouldAutoBind = + isFunction && + !isCompositeComponentMethod && + !isInherited && + !markedDontBind; + + if (shouldAutoBind) { + if (!proto.__reactAutoBindMap) { + proto.__reactAutoBindMap = {}; + } + proto.__reactAutoBindMap[name] = property; + proto[name] = property; + } else { + if (isInherited) { + // For methods which are defined more than once, call the existing + // methods before calling the new property. + if (ReactCompositeComponentInterface[name] === + SpecPolicy.DEFINE_MANY_MERGED) { + proto[name] = createMergedResultFunction(proto[name], property); + } else { + proto[name] = createChainedFunction(proto[name], property); + } + } else { + proto[name] = property; + } + } + } + } +} + +/** + * Merge two objects, but throw if both contain the same key. + * + * @param {object} one The first object, which is mutated. + * @param {object} two The second object + * @return {object} one after it has been mutated to contain everything in two. + */ +function mergeObjectsWithNoDuplicateKeys(one, two) { + invariant( + one && two && typeof one === 'object' && typeof two === 'object', + 'mergeObjectsWithNoDuplicateKeys(): Cannot merge non-objects' + ); + + objMap(two, function(value, key) { + invariant( + one[key] === undefined, + 'mergeObjectsWithNoDuplicateKeys(): ' + + 'Tried to merge two objects with the same key: %s', + key + ); + one[key] = value; + }); + return one; +} + +/** + * Creates a function that invokes two functions and merges their return values. + * + * @param {function} one Function to invoke first. + * @param {function} two Function to invoke second. + * @return {function} Function that invokes the two argument functions. + * @private + */ +function createMergedResultFunction(one, two) { + return function mergedResult() { + return mergeObjectsWithNoDuplicateKeys( + one.apply(this, arguments), + two.apply(this, arguments) + ); + }; +} + +/** + * Creates a function that invokes two functions and ignores their return vales. + * + * @param {function} one Function to invoke first. + * @param {function} two Function to invoke second. + * @return {function} Function that invokes the two argument functions. + * @private + */ +function createChainedFunction(one, two) { + return function chainedFunction() { + one.apply(this, arguments); + two.apply(this, arguments); + }; +} + +/** + * `ReactCompositeComponent` maintains an auxiliary life cycle state in + * `this._compositeLifeCycleState` (which can be null). + * + * This is different from the life cycle state maintained by `ReactComponent` in + * `this._lifeCycleState`. The following diagram shows how the states overlap in + * time. There are times when the CompositeLifeCycle is null - at those times it + * is only meaningful to look at ComponentLifeCycle alone. + * + * Top Row: ReactComponent.ComponentLifeCycle + * Low Row: ReactComponent.CompositeLifeCycle + * + * +-------+------------------------------------------------------+--------+ + * | UN | MOUNTED | UN | + * |MOUNTED| | MOUNTED| + * +-------+------------------------------------------------------+--------+ + * | ^--------+ +------+ +------+ +------+ +--------^ | + * | | | | | | | | | | | | + * | 0--|MOUNTING|-0-|RECEIV|-0-|RECEIV|-0-|RECEIV|-0-| UN |--->0 | + * | | | |PROPS | | PROPS| | STATE| |MOUNTING| | + * | | | | | | | | | | | | + * | | | | | | | | | | | | + * | +--------+ +------+ +------+ +------+ +--------+ | + * | | | | + * +-------+------------------------------------------------------+--------+ + */ +var CompositeLifeCycle = keyMirror({ + /** + * Components in the process of being mounted respond to state changes + * differently. + */ + MOUNTING: null, + /** + * Components in the process of being unmounted are guarded against state + * changes. + */ + UNMOUNTING: null, + /** + * Components that are mounted and receiving new props respond to state + * changes differently. + */ + RECEIVING_PROPS: null, + /** + * Components that are mounted and receiving new state are guarded against + * additional state changes. + */ + RECEIVING_STATE: null +}); + +/** + * @lends {ReactCompositeComponent.prototype} + */ +var ReactCompositeComponentMixin = { + + /** + * Base constructor for all composite component. + * + * @param {?object} initialProps + * @param {*} children + * @final + * @internal + */ + construct: function(initialProps, children) { + // Children can be either an array or more than one argument + ReactComponent.Mixin.construct.apply(this, arguments); + this.state = null; + this._pendingState = null; + this._compositeLifeCycleState = null; + }, + + /** + * Checks whether or not this composite component is mounted. + * @return {boolean} True if mounted, false otherwise. + * @protected + * @final + */ + isMounted: function() { + return ReactComponent.Mixin.isMounted.call(this) && + this._compositeLifeCycleState !== CompositeLifeCycle.MOUNTING; + }, + + /** + * Initializes the component, renders markup, and registers event listeners. + * + * @param {string} rootID DOM ID of the root node. + * @param {ReactReconcileTransaction} transaction + * @param {number} mountDepth number of components in the owner hierarchy + * @return {?string} Rendered markup to be inserted into the DOM. + * @final + * @internal + */ + mountComponent: ReactPerf.measure( + 'ReactCompositeComponent', + 'mountComponent', + function(rootID, transaction, mountDepth) { + ReactComponent.Mixin.mountComponent.call( + this, + rootID, + transaction, + mountDepth + ); + this._compositeLifeCycleState = CompositeLifeCycle.MOUNTING; + + this._defaultProps = this.getDefaultProps ? this.getDefaultProps() : null; + this._processProps(this.props); + + if (this.__reactAutoBindMap) { + this._bindAutoBindMethods(); + } + + this.state = this.getInitialState ? this.getInitialState() : null; + this._pendingState = null; + this._pendingForceUpdate = false; + + if (this.componentWillMount) { + this.componentWillMount(); + // When mounting, calls to `setState` by `componentWillMount` will set + // `this._pendingState` without triggering a re-render. + if (this._pendingState) { + this.state = this._pendingState; + this._pendingState = null; + } + } + + this._renderedComponent = this._renderValidatedComponent(); + + // Done with mounting, `setState` will now trigger UI changes. + this._compositeLifeCycleState = null; + var markup = this._renderedComponent.mountComponent( + rootID, + transaction, + mountDepth + 1 + ); + if (this.componentDidMount) { + transaction.getReactMountReady().enqueue(this, this.componentDidMount); + } + return markup; + } + ), + + /** + * Releases any resources allocated by `mountComponent`. + * + * @final + * @internal + */ + unmountComponent: function() { + this._compositeLifeCycleState = CompositeLifeCycle.UNMOUNTING; + if (this.componentWillUnmount) { + this.componentWillUnmount(); + } + this._compositeLifeCycleState = null; + + this._defaultProps = null; + + ReactComponent.Mixin.unmountComponent.call(this); + this._renderedComponent.unmountComponent(); + this._renderedComponent = null; + + if (this.refs) { + this.refs = null; + } + + // Some existing components rely on this.props even after they've been + // destroyed (in event handlers). + // TODO: this.props = null; + // TODO: this.state = null; + }, + + /** + * Sets a subset of the state. Always use this or `replaceState` to mutate + * state. You should treat `this.state` as immutable. + * + * There is no guarantee that `this.state` will be immediately updated, so + * accessing `this.state` after calling this method may return the old value. + * + * There is no guarantee that calls to `setState` will run synchronously, + * as they may eventually be batched together. You can provide an optional + * callback that will be executed when the call to setState is actually + * completed. + * + * @param {object} partialState Next partial state to be merged with state. + * @param {?function} callback Called after state is updated. + * @final + * @protected + */ + setState: function(partialState, callback) { + // Merge with `_pendingState` if it exists, otherwise with existing state. + this.replaceState( + merge(this._pendingState || this.state, partialState), + callback + ); + }, + + /** + * Replaces all of the state. Always use this or `setState` to mutate state. + * You should treat `this.state` as immutable. + * + * There is no guarantee that `this.state` will be immediately updated, so + * accessing `this.state` after calling this method may return the old value. + * + * @param {object} completeState Next state. + * @param {?function} callback Called after state is updated. + * @final + * @protected + */ + replaceState: function(completeState, callback) { + validateLifeCycleOnReplaceState(this); + this._pendingState = completeState; + ReactUpdates.enqueueUpdate(this, callback); + }, + + /** + * Processes props by setting default values for unspecified props and + * asserting that the props are valid. + * + * @param {object} props + * @private + */ + _processProps: function(props) { + var propName; + var defaultProps = this._defaultProps; + for (propName in defaultProps) { + if (!(propName in props)) { + props[propName] = defaultProps[propName]; + } + } + var propTypes = this.constructor.propTypes; + if (propTypes) { + var componentName = this.constructor.displayName; + for (propName in propTypes) { + var checkProp = propTypes[propName]; + if (checkProp) { + checkProp(props, propName, componentName); + } + } + } + }, + + performUpdateIfNecessary: function() { + var compositeLifeCycleState = this._compositeLifeCycleState; + // Do not trigger a state transition if we are in the middle of mounting or + // receiving props because both of those will already be doing this. + if (compositeLifeCycleState === CompositeLifeCycle.MOUNTING || + compositeLifeCycleState === CompositeLifeCycle.RECEIVING_PROPS) { + return; + } + ReactComponent.Mixin.performUpdateIfNecessary.call(this); + }, + + /** + * If any of `_pendingProps`, `_pendingState`, or `_pendingForceUpdate` is + * set, update the component. + * + * @param {ReactReconcileTransaction} transaction + * @internal + */ + _performUpdateIfNecessary: function(transaction) { + if (this._pendingProps == null && + this._pendingState == null && + !this._pendingForceUpdate) { + return; + } + + var nextProps = this.props; + if (this._pendingProps != null) { + nextProps = this._pendingProps; + this._processProps(nextProps); + this._pendingProps = null; + + this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_PROPS; + if (this.componentWillReceiveProps) { + this.componentWillReceiveProps(nextProps, transaction); + } + } + + this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_STATE; + + var nextState = this._pendingState || this.state; + this._pendingState = null; + + if (this._pendingForceUpdate || + !this.shouldComponentUpdate || + this.shouldComponentUpdate(nextProps, nextState)) { + this._pendingForceUpdate = false; + // Will set `this.props` and `this.state`. + this._performComponentUpdate(nextProps, nextState, transaction); + } else { + // If it's determined that a component should not update, we still want + // to set props and state. + this.props = nextProps; + this.state = nextState; + } + + this._compositeLifeCycleState = null; + }, + + /** + * Merges new props and state, notifies delegate methods of update and + * performs update. + * + * @param {object} nextProps Next object to set as properties. + * @param {?object} nextState Next object to set as state. + * @param {ReactReconcileTransaction} transaction + * @private + */ + _performComponentUpdate: function(nextProps, nextState, transaction) { + var prevProps = this.props; + var prevState = this.state; + + if (this.componentWillUpdate) { + this.componentWillUpdate(nextProps, nextState, transaction); + } + + this.props = nextProps; + this.state = nextState; + + this.updateComponent(transaction, prevProps, prevState); + + if (this.componentDidUpdate) { + transaction.getReactMountReady().enqueue( + this, + this.componentDidUpdate.bind(this, prevProps, prevState) + ); + } + }, + + /** + * Updates the component's currently mounted DOM representation. + * + * By default, this implements React's rendering and reconciliation algorithm. + * Sophisticated clients may wish to override this. + * + * @param {ReactReconcileTransaction} transaction + * @param {object} prevProps + * @param {?object} prevState + * @internal + * @overridable + */ + updateComponent: ReactPerf.measure( + 'ReactCompositeComponent', + 'updateComponent', + function(transaction, prevProps, prevState) { + ReactComponent.Mixin.updateComponent.call(this, transaction, prevProps); + var currentComponent = this._renderedComponent; + var nextComponent = this._renderValidatedComponent(); + if (currentComponent.constructor === nextComponent.constructor) { + currentComponent.receiveProps(nextComponent.props, transaction); + } else { + // These two IDs are actually the same! But nothing should rely on that. + var thisID = this._rootNodeID; + var currentComponentID = currentComponent._rootNodeID; + currentComponent.unmountComponent(); + this._renderedComponent = nextComponent; + var nextMarkup = nextComponent.mountComponent( + thisID, + transaction, + this._mountDepth + 1 + ); + ReactComponent.DOMIDOperations.dangerouslyReplaceNodeWithMarkupByID( + currentComponentID, + nextMarkup + ); + } + } + ), + + /** + * Forces an update. This should only be invoked when it is known with + * certainty that we are **not** in a DOM transaction. + * + * You may want to call this when you know that some deeper aspect of the + * component's state has changed but `setState` was not called. + * + * This will not invoke `shouldUpdateComponent`, but it will invoke + * `componentWillUpdate` and `componentDidUpdate`. + * + * @param {?function} callback Called after update is complete. + * @final + * @protected + */ + forceUpdate: function(callback) { + var compositeLifeCycleState = this._compositeLifeCycleState; + invariant( + this.isMounted() || + compositeLifeCycleState === CompositeLifeCycle.MOUNTING, + 'forceUpdate(...): Can only force an update on mounted or mounting ' + + 'components.' + ); + invariant( + compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_STATE && + compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING, + 'forceUpdate(...): Cannot force an update while unmounting component ' + + 'or during an existing state transition (such as within `render`).' + ); + this._pendingForceUpdate = true; + ReactUpdates.enqueueUpdate(this, callback); + }, + + /** + * @private + */ + _renderValidatedComponent: function() { + var renderedComponent; + ReactCurrentOwner.current = this; + try { + renderedComponent = this.render(); + } catch (error) { + // IE8 requires `catch` in order to use `finally`. + throw error; + } finally { + ReactCurrentOwner.current = null; + } + invariant( + ReactComponent.isValidComponent(renderedComponent), + '%s.render(): A valid ReactComponent must be returned.', + this.constructor.displayName || 'ReactCompositeComponent' + ); + return renderedComponent; + }, + + /** + * @private + */ + _bindAutoBindMethods: function() { + for (var autoBindKey in this.__reactAutoBindMap) { + if (!this.__reactAutoBindMap.hasOwnProperty(autoBindKey)) { + continue; + } + var method = this.__reactAutoBindMap[autoBindKey]; + this[autoBindKey] = this._bindAutoBindMethod(method); + } + }, + + /** + * Binds a method to the component. + * + * @param {function} method Method to be bound. + * @private + */ + _bindAutoBindMethod: function(method) { + var component = this; + var boundMethod = function() { + return method.apply(component, arguments); + }; + if (true) { + boundMethod.__reactBoundContext = component; + boundMethod.__reactBoundMethod = method; + boundMethod.__reactBoundArguments = null; + var componentName = component.constructor.displayName; + var _bind = boundMethod.bind; + boundMethod.bind = function(newThis) { + // User is trying to bind() an autobound method; we effectively will + // ignore the value of "this" that the user is trying to use, so + // let's warn. + if (newThis !== component && newThis !== null) { + console.warn( + 'bind(): React component methods may only be bound to the ' + + 'component instance. See ' + componentName + ); + } else if (arguments.length === 1) { + console.warn( + 'bind(): You are binding a component method to the component. ' + + 'React does this for you automatically in a high-performance ' + + 'way, so you can safely remove this call. See ' + componentName + ); + return boundMethod; + } + var reboundMethod = _bind.apply(boundMethod, arguments); + reboundMethod.__reactBoundContext = component; + reboundMethod.__reactBoundMethod = method; + reboundMethod.__reactBoundArguments = + Array.prototype.slice.call(arguments, 1); + return reboundMethod; + }; + } + return boundMethod; + } +}; + +var ReactCompositeComponentBase = function() {}; +mixInto(ReactCompositeComponentBase, ReactComponent.Mixin); +mixInto(ReactCompositeComponentBase, ReactOwner.Mixin); +mixInto(ReactCompositeComponentBase, ReactPropTransferer.Mixin); +mixInto(ReactCompositeComponentBase, ReactCompositeComponentMixin); + +/** + * Module for creating composite components. + * + * @class ReactCompositeComponent + * @extends ReactComponent + * @extends ReactOwner + * @extends ReactPropTransferer + */ +var ReactCompositeComponent = { + + LifeCycle: CompositeLifeCycle, + + Base: ReactCompositeComponentBase, + + /** + * Creates a composite component class given a class specification. + * + * @param {object} spec Class specification (which must define `render`). + * @return {function} Component constructor function. + * @public + */ + createClass: function(spec) { + var Constructor = function() {}; + Constructor.prototype = new ReactCompositeComponentBase(); + Constructor.prototype.constructor = Constructor; + mixSpecIntoComponent(Constructor, spec); + + invariant( + Constructor.prototype.render, + 'createClass(...): Class specification must implement a `render` method.' + ); + + if (true) { + if (Constructor.prototype.componentShouldUpdate) { + console.warn( + (spec.displayName || 'A component') + ' has a method called ' + + 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + + 'The name is phrased as a question because the function is ' + + 'expected to return a value.' + ); + } + } + + // Reduce time spent doing lookups by setting these on the prototype. + for (var methodName in ReactCompositeComponentInterface) { + if (!Constructor.prototype[methodName]) { + Constructor.prototype[methodName] = null; + } + } + + var ConvenienceConstructor = function(props, children) { + var instance = new Constructor(); + instance.construct.apply(instance, arguments); + return instance; + }; + ConvenienceConstructor.componentConstructor = Constructor; + ConvenienceConstructor.originalSpec = spec; + return ConvenienceConstructor; + }, + + /** + * Checks if a value is a valid component constructor. + * + * @param {*} + * @return {boolean} + * @public + */ + isValidClass: function(componentClass) { + return componentClass instanceof Function && + 'componentConstructor' in componentClass && + componentClass.componentConstructor instanceof Function; + } +}; + +module.exports = ReactCompositeComponent; + +},{"./ReactComponent":28,"./ReactCurrentOwner":32,"./ReactOwner":57,"./ReactPerf":58,"./ReactPropTransferer":59,"./ReactUpdates":69,"./invariant":106,"./keyMirror":112,"./merge":115,"./mixInto":118,"./objMap":121}],32:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule ReactCurrentOwner + */ + +"use strict"; + +/** + * Keeps track of the current owner. + * + * The current owner is the component who should own any components that are + * currently being constructed. + * + * The depth indicate how many composite components are above this render level. + */ +var ReactCurrentOwner = { + + /** + * @internal + * @type {ReactComponent} + */ + current: null + +}; + +module.exports = ReactCurrentOwner; + +},{}],33:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule ReactDOM + * @typechecks static-only + */ + +"use strict"; + +var ReactDOMComponent = require("./ReactDOMComponent"); + +var mergeInto = require("./mergeInto"); +var objMapKeyVal = require("./objMapKeyVal"); + +/** + * Creates a new React class that is idempotent and capable of containing other + * React components. It accepts event listeners and DOM properties that are + * valid according to `DOMProperty`. + * + * - Event listeners: `onClick`, `onMouseDown`, etc. + * - DOM properties: `className`, `name`, `title`, etc. + * + * The `style` property functions differently from the DOM API. It accepts an + * object mapping of style properties to values. + * + * @param {string} tag Tag name (e.g. `div`). + * @param {boolean} omitClose True if the close tag should be omitted. + * @private + */ +function createDOMComponentClass(tag, omitClose) { + var Constructor = function() {}; + Constructor.prototype = new ReactDOMComponent(tag, omitClose); + Constructor.prototype.constructor = Constructor; + Constructor.displayName = tag; + + var ConvenienceConstructor = function(props, children) { + var instance = new Constructor(); + instance.construct.apply(instance, arguments); + return instance; + }; + ConvenienceConstructor.componentConstructor = Constructor; + return ConvenienceConstructor; +} + +/** + * Creates a mapping from supported HTML tags to `ReactDOMComponent` classes. + * This is also accessible via `React.DOM`. + * + * @public + */ +var ReactDOM = objMapKeyVal({ + a: false, + abbr: false, + address: false, + area: false, + article: false, + aside: false, + audio: false, + b: false, + base: false, + bdi: false, + bdo: false, + big: false, + blockquote: false, + body: false, + br: true, + button: false, + canvas: false, + caption: false, + cite: false, + code: false, + col: true, + colgroup: false, + data: false, + datalist: false, + dd: false, + del: false, + details: false, + dfn: false, + div: false, + dl: false, + dt: false, + em: false, + embed: true, + fieldset: false, + figcaption: false, + figure: false, + footer: false, + form: false, // NOTE: Injected, see `ReactDOMForm`. + h1: false, + h2: false, + h3: false, + h4: false, + h5: false, + h6: false, + head: false, + header: false, + hr: true, + html: false, + i: false, + iframe: false, + img: true, + input: true, + ins: false, + kbd: false, + keygen: true, + label: false, + legend: false, + li: false, + link: false, + main: false, + map: false, + mark: false, + menu: false, + menuitem: false, // NOTE: Close tag should be omitted, but causes problems. + meta: true, + meter: false, + nav: false, + noscript: false, + object: false, + ol: false, + optgroup: false, + option: false, + output: false, + p: false, + param: true, + pre: false, + progress: false, + q: false, + rp: false, + rt: false, + ruby: false, + s: false, + samp: false, + script: false, + section: false, + select: false, + small: false, + source: false, + span: false, + strong: false, + style: false, + sub: false, + summary: false, + sup: false, + table: false, + tbody: false, + td: false, + textarea: false, // NOTE: Injected, see `ReactDOMTextarea`. + tfoot: false, + th: false, + thead: false, + time: false, + title: false, + tr: false, + track: true, + u: false, + ul: false, + 'var': false, + video: false, + wbr: false, + + // SVG + circle: false, + g: false, + line: false, + path: false, + polyline: false, + rect: false, + svg: false, + text: false +}, createDOMComponentClass); + +var injection = { + injectComponentClasses: function(componentClasses) { + mergeInto(ReactDOM, componentClasses); + } +}; + +ReactDOM.injection = injection; + +module.exports = ReactDOM; + +},{"./ReactDOMComponent":35,"./mergeInto":117,"./objMapKeyVal":122}],34:[function(require,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule ReactDOMButton + */ + +"use strict"; + +var ReactCompositeComponent = require("./ReactCompositeComponent"); +var ReactDOM = require("./ReactDOM"); + +var keyMirror = require("./keyMirror"); + +// Store a reference to the