diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 0000000..ab4885d
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,152 @@
+{
+ "env": {
+ "node": true,
+ "browser": true
+ },
+ "globals": {
+ "define" : false
+ },
+ "rules": {
+ /**
+ * Strict mode
+ */
+ "strict": [2, "global"], // http://eslint.org/docs/rules/strict
+
+ /**
+ * Variables
+ */
+ "no-shadow": 2, // http://eslint.org/docs/rules/no-shadow
+ "no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names
+ "no-unused-vars": [2, { // http://eslint.org/docs/rules/no-unused-vars
+ "vars": "local",
+ "args": "after-used"
+ }],
+ "no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define
+
+ /**
+ * Node.js
+ */
+ "no-process-exit": 0, // http://eslint.org/docs/rules/no-process-exit
+ "no-sync": 2, // http://eslint.org/docs/rules/no-sync
+
+ /**
+ * Possible errors
+ */
+ "comma-dangle": [2, "never"], // http://eslint.org/docs/rules/comma-dangle
+ "no-cond-assign": [2, "always"], // http://eslint.org/docs/rules/no-cond-assign
+ "no-console": 1, // http://eslint.org/docs/rules/no-console
+ "no-debugger": 1, // http://eslint.org/docs/rules/no-debugger
+ "no-alert": 1, // http://eslint.org/docs/rules/no-alert
+ "no-constant-condition": 1, // http://eslint.org/docs/rules/no-constant-condition
+ "no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys
+ "no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case
+ "no-empty": 2, // http://eslint.org/docs/rules/no-empty
+ "no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign
+ "no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast
+ "no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi
+ "no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign
+ "no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations
+ "no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp
+ "no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace
+ "no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls
+ "no-reserved-keys": 2, // http://eslint.org/docs/rules/no-reserved-keys
+ "no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays
+ "no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable
+ "use-isnan": 2, // http://eslint.org/docs/rules/use-isnan
+ "block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var
+
+ /**
+ * Best practices
+ */
+ "consistent-return": 2, // http://eslint.org/docs/rules/consistent-return
+ "curly": [2, "all"], // http://eslint.org/docs/rules/curly
+ "default-case": 2, // http://eslint.org/docs/rules/default-case
+ "dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation
+ "allowKeywords": true
+ }],
+ "eqeqeq": 2, // http://eslint.org/docs/rules/eqeqeq
+ "guard-for-in": 2, // http://eslint.org/docs/rules/guard-for-in
+ "no-caller": 2, // http://eslint.org/docs/rules/no-caller
+ "no-else-return": 2, // http://eslint.org/docs/rules/no-else-return
+ "no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null
+ "no-eval": 2, // http://eslint.org/docs/rules/no-eval
+ "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native
+ "no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind
+ "no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough
+ "no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal
+ "no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval
+ "no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks
+ "no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func
+ "no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str
+ "no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign
+ "no-new": 2, // http://eslint.org/docs/rules/no-new
+ "no-new-func": 2, // http://eslint.org/docs/rules/no-new-func
+ "no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers
+ "no-octal": 2, // http://eslint.org/docs/rules/no-octal
+ "no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape
+ //"no-param-reassign": 2, // http://eslint.org/docs/rules/no-param-reassign
+ "no-proto": 2, // http://eslint.org/docs/rules/no-proto
+ "no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare
+ "no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign
+ "no-script-url": 2, // http://eslint.org/docs/rules/no-script-url
+ "no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare
+ "no-sequences": 2, // http://eslint.org/docs/rules/no-sequences
+ "no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal
+ "no-with": 2, // http://eslint.org/docs/rules/no-with
+ "radix": 2, // http://eslint.org/docs/rules/radix
+ //"vars-on-top": 2, // http://eslint.org/docs/rules/vars-on-top
+ "wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife
+ "yoda": 2, // http://eslint.org/docs/rules/yoda
+
+ /**
+ * Style
+ */
+ "indent": [2, 2], // http://eslint.org/docs/rules/indent
+ "brace-style": [2, // http://eslint.org/docs/rules/brace-style
+ "1tbs", {
+ "allowSingleLine": true
+ }],
+ "quotes": [
+ 2, "single", "avoid-escape" // http://eslint.org/docs/rules/quotes
+ ],
+ "camelcase": [2, { // http://eslint.org/docs/rules/camelcase
+ "properties": "never"
+ }],
+ "comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing
+ "before": false,
+ "after": true
+ }],
+ "comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style
+ "eol-last": 2, // http://eslint.org/docs/rules/eol-last
+ //"func-names": 1, // http://eslint.org/docs/rules/func-names
+ "key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing
+ "beforeColon": false,
+ "afterColon": true
+ }],
+ "new-cap": [2, { // http://eslint.org/docs/rules/new-cap
+ "newIsCap": true
+ }],
+ "no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines
+ "max": 2
+ }],
+ "no-nested-ternary": 2, // http://eslint.org/docs/rules/no-nested-ternary
+ "no-new-object": 2, // http://eslint.org/docs/rules/no-new-object
+ "no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func
+ "no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces
+ "no-wrap-func": 2, // http://eslint.org/docs/rules/no-wrap-func
+ "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle
+ "one-var": [2, "never"], // http://eslint.org/docs/rules/one-var
+ "padded-blocks": [2, "never"], // http://eslint.org/docs/rules/padded-blocks
+ "semi": [2, "always"], // http://eslint.org/docs/rules/semi
+ "semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing
+ "before": false,
+ "after": true
+ }],
+ "space-after-keywords": 2, // http://eslint.org/docs/rules/space-after-keywords
+ "space-before-blocks": 2, // http://eslint.org/docs/rules/space-before-blocks
+ "space-before-function-paren": [2, "never"], // http://eslint.org/docs/rules/space-before-function-paren
+ "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops
+ "space-return-throw-case": 2, // http://eslint.org/docs/rules/space-return-throw-case
+ "spaced-line-comment": 2, // http://eslint.org/docs/rules/spaced-line-comment
+ }
+}
diff --git a/.gitignore b/.gitignore
index d10542f..48d1e58 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-/dist/
+dist/
+coverage/
node_modules/
-/bower_components/
-tests/coverage
+bower_components/
diff --git a/.jshintrc b/.jshintrc
deleted file mode 100644
index 8249e5b..0000000
--- a/.jshintrc
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "predef": [
- "define",
- "module",
- "Firebase"
- ],
- "bitwise": true,
- "curly": true,
- "eqeqeq": true,
- "forin": true,
- "freeze": true,
- "indent": 2,
- "latedef": true,
- "quotmark": "double",
- "strict": true,
- "trailing": true,
- "undef": true,
- "unused": true
-}
diff --git a/.travis.yml b/.travis.yml
index 8fda8ba..80cf0b7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,9 +2,7 @@ language: node_js
node_js:
- '0.10'
install:
-- npm install -g bower
- npm install
-- bower install
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
diff --git a/bower.json b/bower.json
index e794041..5586a94 100644
--- a/bower.json
+++ b/bower.json
@@ -32,10 +32,7 @@
"changelog.txt"
],
"dependencies": {
- "react": "0.12.x",
- "firebase": "2.0.x"
- },
- "devDependencies": {
- "jasmine": "~2.0.0"
+ "react": "0.13.x",
+ "firebase": "2.2.x"
}
}
diff --git a/build/footer b/build/footer
deleted file mode 100644
index 0867193..0000000
--- a/build/footer
+++ /dev/null
@@ -1,2 +0,0 @@
- return ReactFireMixin;
-}));
\ No newline at end of file
diff --git a/build/header b/build/header
deleted file mode 100644
index 6a63bef..0000000
--- a/build/header
+++ /dev/null
@@ -1,26 +0,0 @@
-/*!
- * ReactFire is an open-source JavaScript library that allows you to add a
- * realtime data source to your React apps by providing and easy way to let
- * Firebase populate the state of React components.
- *
- * ReactFire 0.0.0
- * https://github.com/firebase/reactfire/
- * License: MIT
- */
-
-;(function (root, factory) {
- "use strict";
- if (typeof define === "function" && define.amd) {
- // AMD
- define([], function() {
- return (root.ReactFireMixin = factory());
- });
- } else if (typeof exports === "object") {
- // CommonJS
- module.exports = factory();
- } else {
- // Global variables
- root.ReactFireMixin = factory();
- }
-}(this, function() {
- "use strict";
diff --git a/gulpfile.js b/gulpfile.js
index 0c66962..89e4cdf 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -1,123 +1,94 @@
+'use strict';
+
/**************/
/* REQUIRES */
/**************/
-var gulp = require("gulp");
+var gulp = require('gulp');
+var runSequence = require('run-sequence');
// File IO
-var streamqueue = require("streamqueue");
-var concat = require("gulp-concat");
-var jshint = require("gulp-jshint");
-var uglify = require("gulp-uglify");
+var exit = require('gulp-exit');
+var eslint = require('gulp-eslint');
+var uglify = require('gulp-uglify');
+var extReplace = require('gulp-ext-replace');
// Testing
-var karma = require("gulp-karma");
-
-// Determine if this is being run in Travis
-var travis = false;
+var mocha = require('gulp-mocha');
+var istanbul = require('gulp-istanbul');
/****************/
/* FILE PATHS */
/****************/
var paths = {
- destDir: "dist",
+ destDir: 'dist',
- scripts: {
- src: {
- dir: "src",
- files: [
- "src/*.js"
- ]
- },
- dest: {
- dir: "dist",
- files: {
- unminified: "reactfire.js",
- minified: "reactfire.min.js"
- }
- }
- },
+ srcFiles: [
+ 'src/reactfire.js'
+ ],
- tests: {
- config: "tests/karma.conf.js",
- files: [
- "bower_components/firebase/firebase.js",
- "tests/phantomjs-es5-shim.js",
- "bower_components/react/react-with-addons.js",
- "src/*.js",
- "tests/specs/*.spec.js"
- ]
- }
+ testFiles: [
+ 'tests/helpers.js',
+ 'tests/reactfire.spec.js'
+ ]
};
/***********/
/* TASKS */
/***********/
-/* Lints, minifies, and concatenates the script files */
-gulp.task("scripts", function() {
- // Concatenate all src files together
- var stream = streamqueue({ objectMode: true });
- stream.queue(gulp.src("build/header"));
- stream.queue(gulp.src(paths.scripts.src.files));
- stream.queue(gulp.src("build/footer"));
+// Lints the JavaScript files
+gulp.task('lint', function() {
+ var filesToLint = paths.srcFiles.concat(paths.testFiles).concat(['gulpfile.js']);
+ return gulp.src(filesToLint)
+ .pipe(eslint())
+ .pipe(eslint.format())
+ .pipe(eslint.failAfterError());
+});
- // Output the final concatenated script file
- return stream.done()
- // Rename file
- .pipe(concat(paths.scripts.dest.files.unminified))
-
- // Lint
- .pipe(jshint())
- .pipe(jshint.reporter("jshint-stylish"))
- .pipe(jshint.reporter("fail"))
- .on("error", function(error) {
- if (travis) {
- throw error;
- }
- })
+/* Builds the distribution files */
+gulp.task('build', function() {
+ return gulp.src(paths.srcFiles)
// Write un-minified version
- .pipe(gulp.dest(paths.scripts.dest.dir))
+ .pipe(gulp.dest(paths.destDir))
// Minify
.pipe(uglify({
- preserveComments: "some"
+ preserveComments: 'some'
}))
- // Rename file
- .pipe(concat(paths.scripts.dest.files.minified))
+ // Change the file extension
+ .pipe(extReplace('.min.js'))
- // Write minified version to the distribution directory
- .pipe(gulp.dest(paths.scripts.dest.dir));
+ // Write minified version
+ .pipe(gulp.dest(paths.destDir));
});
-/* Uses the Karma test runner to run the Jasmine tests */
-gulp.task("test", function() {
- return gulp.src(paths.tests.files)
- .pipe(karma({
- configFile: paths.tests.config,
- browsers: travis ? ["Firefox"] : ["Chrome"],
- action: "run"
- }))
- .on("error", function(error) {
- throw error;
+// Runs the Mocha test suite
+gulp.task('test', function() {
+ return gulp.src(paths.srcFiles)
+ .pipe(istanbul())
+ .pipe(istanbul.hookRequire())
+ .on('finish', function() {
+ gulp.src(paths.testFiles)
+ .pipe(mocha({
+ reporter: 'spec',
+ timeout: 5000
+ }))
+ .pipe(istanbul.writeReports())
+ .pipe(exit());
});
});
-/* Re-runs the "scripts" task every time a script file changes */
-gulp.task("watch", function() {
- gulp.watch(["build/*", paths.scripts.src.dir + "/**/*"], ["scripts"]);
+// Re-lints and re-builds every time a source file changes
+gulp.task('watch', function() {
+ gulp.watch([paths.srcFiles], ['lint', 'build']);
});
-/* Builds the distribution files */
-gulp.task("build", ["scripts"]);
-
-/* Tasks to be run within Travis CI */
-gulp.task("travis", function() {
- travis = true;
- gulp.start("build", "test");
+// Default task
+gulp.task('default', function(done) {
+ runSequence('lint', 'build', 'test', function(error) {
+ done(error && error.err);
+ });
});
-
-/* Runs the "scripts" and "test" tasks by default */
-gulp.task("default", ["build", "test"]);
diff --git a/package.json b/package.json
index 778f01f..aa143a7 100644
--- a/package.json
+++ b/package.json
@@ -32,28 +32,24 @@
"package.json"
],
"dependencies": {
- "firebase": "2.0.x",
- "react": "0.12.x"
+ "firebase": "2.2.x",
+ "react": "0.13.x"
},
"devDependencies": {
- "coveralls": "^2.11.1",
- "gulp": "^3.8.7",
- "gulp-concat": "^2.2.0",
- "gulp-jshint": "^1.5.1",
- "gulp-karma": "0.0.4",
- "gulp-uglify": "^0.2.1",
- "jshint-stylish": "^0.2.0",
- "karma": "^0.12.16",
- "karma-chrome-launcher": "^0.1.5",
- "karma-coverage": "^0.2.4",
- "karma-failed-reporter": "0.0.2",
- "karma-firefox-launcher": "^0.1.3",
- "karma-jasmine": "~0.2.0",
- "karma-spec-reporter": "0.0.13",
- "streamqueue": "^0.1.1"
+ "chai": "^3.0.0",
+ "coveralls": "^2.11.2",
+ "gulp": "^3.9.0",
+ "gulp-eslint": "^0.15.0",
+ "gulp-exit": "0.0.2",
+ "gulp-ext-replace": "^0.2.0",
+ "gulp-istanbul": "^0.10.0",
+ "gulp-mocha": "^2.1.2",
+ "gulp-uglify": "^1.2.0",
+ "jsdom": "3.x.x",
+ "run-sequence": "^1.1.1"
},
"scripts": {
"test": "gulp test",
- "travis": "gulp travis"
+ "travis": "gulp"
}
}
diff --git a/src/reactfire.js b/src/reactfire.js
index bbc65af..0f168eb 100644
--- a/src/reactfire.js
+++ b/src/reactfire.js
@@ -1,146 +1,165 @@
-var ReactFireMixin = {
- /********************/
- /* MIXIN LIFETIME */
- /********************/
- /* Initializes the Firebase binding refs array */
- componentWillMount: function() {
- this.firebaseRefs = {};
- this.firebaseListeners = {};
- },
+/*!
+ * ReactFire is an open-source JavaScript library that allows you to add a
+ * realtime data source to your React apps by providing and easy way to let
+ * Firebase populate the state of React components.
+ *
+ * ReactFire 0.0.0
+ * https://github.com/firebase/reactfire/
+ * License: MIT
+ */
+/* eslint "strict": [2, "function"] */
+(function(root, factory) {
+ 'use strict';
- /* Removes any remaining Firebase bindings */
- componentWillUnmount: function() {
- for (var key in this.firebaseRefs) {
- if (this.firebaseRefs.hasOwnProperty(key)) {
- this.unbind(key);
- }
- }
- },
-
-
- /*************/
- /* BINDING */
- /*************/
- /* Creates a binding between Firebase and the inputted bind variable as an array */
- bindAsArray: function(firebaseRef, bindVar, cancelCallback) {
- this._bind(firebaseRef, bindVar, cancelCallback, true);
- },
-
- /* Creates a binding between Firebase and the inputted bind variable as an object */
- bindAsObject: function(firebaseRef, bindVar, cancelCallback) {
- this._bind(firebaseRef, bindVar, cancelCallback, false);
- },
-
- /* Creates a binding between Firebase and the inputted bind variable as either an array or object */
- _bind: function(firebaseRef, bindVar, cancelCallback, bindAsArray) {
- this._validateBindVar(bindVar);
-
- var errorMessage, errorCode;
- if (Object.prototype.toString.call(firebaseRef) !== "[object Object]") {
- errorMessage = "firebaseRef must be an instance of Firebase";
- errorCode = "INVALID_FIREBASE_REF";
- }
- else if (typeof bindAsArray !== "boolean") {
- errorMessage = "bindAsArray must be a boolean. Got: " + bindAsArray;
- errorCode = "INVALID_BIND_AS_ARRAY";
- }
-
- if (typeof errorMessage !== "undefined") {
- var error = new Error("ReactFire: " + errorMessage);
- error.code = errorCode;
- throw error;
- }
-
- this.firebaseRefs[bindVar] = firebaseRef.ref();
- this.firebaseListeners[bindVar] = firebaseRef.on("value", function(dataSnapshot) {
- var newState = {};
- if (bindAsArray) {
- newState[bindVar] = this._toArray(dataSnapshot.val());
- }
- else {
- newState[bindVar] = dataSnapshot.val();
- }
- this.setState(newState);
- }.bind(this), cancelCallback);
- },
-
- /* Removes the binding between Firebase and the inputted bind variable */
- unbind: function(bindVar) {
- this._validateBindVar(bindVar);
-
- if (typeof this.firebaseRefs[bindVar] === "undefined") {
- var error = new Error("ReactFire: unexpected value for bindVar. \"" + bindVar + "\" was either never bound or has already been unbound");
- error.code = "UNBOUND_BIND_VARIABLE";
- throw error;
- }
-
- this.firebaseRefs[bindVar].off("value", this.firebaseListeners[bindVar]);
- delete this.firebaseRefs[bindVar];
- delete this.firebaseListeners[bindVar];
- },
-
-
- /*************/
- /* HELPERS */
- /*************/
- /* Validates the name of the variable which is being bound */
- _validateBindVar: function(bindVar) {
- var errorMessage;
-
- if (typeof bindVar !== "string") {
- errorMessage = "bindVar must be a string. Got: " + bindVar;
- }
- else if (bindVar.length === 0) {
- errorMessage = "bindVar must be a non-empty string. Got: \"\"";
- }
- else if (bindVar.length > 768) {
- // Firebase can only stored child paths up to 768 characters
- errorMessage = "bindVar is too long to be stored in Firebase. Got: " + bindVar;
- }
- else if (/[\[\].#$\/\u0000-\u001F\u007F]/.test(bindVar)) {
- // Firebase does not allow node keys to contain the following characters
- errorMessage = "bindVar cannot contain any of the following characters: . # $ ] [ /. Got: " + bindVar;
- }
-
- if (typeof errorMessage !== "undefined") {
- var error = new Error("ReactFire: " + errorMessage);
- error.code = "INVALID_BIND_VARIABLE";
- throw error;
- }
- },
-
-
- /* Returns true if the inputted object is a JavaScript array */
- _isArray: function(obj) {
- return (Object.prototype.toString.call(obj) === "[object Array]");
- },
-
- /* Converts a Firebase object to a JavaScript array */
- _toArray: function(obj) {
- var item;
- var out = [];
- if (obj) {
- if (this._isArray(obj)) {
- for (var i = 0, length = obj.length; i < length; i++) {
- item = obj[i];
- if (item !== undefined && item !== null) {
- out.push({ $key: i, $value: item });
- }
- }
- }
- else if (typeof(obj) === "object") {
- for (var key in obj) {
- if (obj.hasOwnProperty(key)) {
- item = obj[key];
- if (typeof item !== "object") {
- item = { $value: item };
- }
- item.$key = key;
- out.push(item);
- }
- }
- }
- }
- return out;
+ /* istanbul ignore next */
+ if (typeof define === 'function' && define.amd) {
+ // AMD
+ define([], function() {
+ return (root.ReactFireMixin = factory());
+ });
+ } else if (typeof exports === 'object') {
+ // CommonJS
+ module.exports = factory();
+ } else {
+ // Global variables
+ root.ReactFireMixin = factory();
}
-};
+}(this, function() {
+ 'use strict';
+
+ var ReactFireMixin = {
+ /********************/
+ /* MIXIN LIFETIME */
+ /********************/
+ /* Initializes the Firebase binding refs array */
+ componentWillMount: function() {
+ this.firebaseRefs = {};
+ this.firebaseListeners = {};
+ },
+
+ /* Removes any remaining Firebase bindings */
+ componentWillUnmount: function() {
+ for (var key in this.firebaseRefs) {
+ if (this.firebaseRefs.hasOwnProperty(key)) {
+ this.unbind(key);
+ }
+ }
+ },
+
+
+ /*************/
+ /* BINDING */
+ /*************/
+ /* Creates a binding between Firebase and the inputted bind variable as an array */
+ bindAsArray: function(firebaseRef, bindVar, cancelCallback) {
+ this._bind(firebaseRef, bindVar, cancelCallback, true);
+ },
+
+ /* Creates a binding between Firebase and the inputted bind variable as an object */
+ bindAsObject: function(firebaseRef, bindVar, cancelCallback) {
+ this._bind(firebaseRef, bindVar, cancelCallback, false);
+ },
+
+ /* Throw a formatted error message */
+ _throwError: function(message) {
+ throw new Error('ReactFire: ' + message);
+ },
+
+ /* Creates a binding between Firebase and the inputted bind variable as either an array or object */
+ _bind: function(firebaseRef, bindVar, cancelCallback, bindAsArray) {
+ this._validateBindVar(bindVar);
+
+ if (Object.prototype.toString.call(firebaseRef) !== '[object Object]') {
+ this._throwError('firebaseRef must be an instance of Firebase');
+ }
+
+ this.firebaseRefs[bindVar] = firebaseRef.ref();
+ this.firebaseListeners[bindVar] = firebaseRef.on('value', function(dataSnapshot) {
+ var newState = {};
+ if (bindAsArray) {
+ newState[bindVar] = this._toArray(dataSnapshot.val());
+ } else {
+ newState[bindVar] = dataSnapshot.val();
+ }
+ this.setState(newState);
+ }.bind(this), cancelCallback);
+ },
+
+ /* Removes the binding between Firebase and the inputted bind variable */
+ unbind: function(bindVar, callback) {
+ this._validateBindVar(bindVar);
+
+ if (typeof this.firebaseRefs[bindVar] === 'undefined') {
+ this._throwError('unexpected value for bindVar. "' + bindVar + '" was either never bound or has already been unbound');
+ }
+
+ this.firebaseRefs[bindVar].off('value', this.firebaseListeners[bindVar]);
+ delete this.firebaseRefs[bindVar];
+ delete this.firebaseListeners[bindVar];
+
+ var newState = {};
+ newState[bindVar] = undefined;
+ this.setState(newState, callback);
+ },
+
+
+ /*************/
+ /* HELPERS */
+ /*************/
+ /* Validates the name of the variable which is being bound */
+ _validateBindVar: function(bindVar) {
+ var errorMessage;
+
+ if (typeof bindVar !== 'string') {
+ errorMessage = 'bindVar must be a string. Got: ' + bindVar;
+ } else if (bindVar.length === 0) {
+ errorMessage = 'bindVar must be a non-empty string. Got: ""';
+ } else if (bindVar.length > 768) {
+ // Firebase can only stored child paths up to 768 characters
+ errorMessage = 'bindVar is too long to be stored in Firebase. Got: ' + bindVar;
+ } else if (/[\[\].#$\/\u0000-\u001F\u007F]/.test(bindVar)) {
+ // Firebase does not allow node keys to contain the following characters
+ errorMessage = 'bindVar cannot contain any of the following characters: . # $ ] [ /. Got: ' + bindVar;
+ }
+
+ if (typeof errorMessage !== 'undefined') {
+ this._throwError(errorMessage);
+ }
+ },
+
+ /* Returns true if the inputted object is a JavaScript array */
+ _isArray: function(obj) {
+ return (Object.prototype.toString.call(obj) === '[object Array]');
+ },
+
+ /* Converts a Firebase object to a JavaScript array */
+ _toArray: function(obj) {
+ var item;
+ var out = [];
+ if (obj) {
+ if (this._isArray(obj)) {
+ for (var i = 0, length = obj.length; i < length; i++) {
+ item = obj[i];
+ if (item !== undefined && item !== null) {
+ out.push({ $key: i, $value: item });
+ }
+ }
+ } else if (typeof obj === 'object') {
+ for (var key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ item = obj[key];
+ if (typeof item !== 'object') {
+ item = { $value: item };
+ }
+ item.$key = key;
+ out.push(item);
+ }
+ }
+ }
+ }
+ return out;
+ }
+ };
+
+ return ReactFireMixin;
+}));
diff --git a/tests/.eslintrc b/tests/.eslintrc
new file mode 100644
index 0000000..19e6180
--- /dev/null
+++ b/tests/.eslintrc
@@ -0,0 +1,8 @@
+{
+ "env": {
+ "mocha": true
+ },
+ "rules": {
+ "no-unused-expressions": 0
+ }
+}
diff --git a/tests/helpers.js b/tests/helpers.js
new file mode 100644
index 0000000..d86a7ed
--- /dev/null
+++ b/tests/helpers.js
@@ -0,0 +1,19 @@
+'use strict';
+
+module.exports = {
+ invalidFirebaseRefs: [null, undefined, true, false, [], 0, 5, '', 'a', ['hi', 1]],
+ invalidBindVars: ['', 1, true, false, [], {}, [1, 2], {a: 1}, null, undefined, 'te.st', 'te$st', 'te[st', 'te]st', 'te#st', 'te/st', 'a#i]$da[s', 'te/nst', 'te/rst', 'te/u0000st', 'te/u0015st', 'te/007Fst', Array(800).join('a')],
+
+ /* Returns a random alphabetic string of variable length */
+ generateRandomString: function() {
+ var possibleCharacters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+ var numPossibleCharacters = possibleCharacters.length;
+
+ var text = '';
+ for (var i = 0; i < 10; i++) {
+ text += possibleCharacters.charAt(Math.floor(Math.random() * numPossibleCharacters));
+ }
+
+ return text;
+ }
+};
diff --git a/tests/index.html b/tests/index.html
deleted file mode 100644
index c51064e..0000000
--- a/tests/index.html
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
- ReactFire Test Suite
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/tests/karma.conf.js b/tests/karma.conf.js
deleted file mode 100644
index 84a25ee..0000000
--- a/tests/karma.conf.js
+++ /dev/null
@@ -1,25 +0,0 @@
-module.exports = function(config) {
- config.set({
- frameworks: ["jasmine"],
- autowatch: false,
- singleRun: true,
-
- preprocessors: {
- "../src/*.js": "coverage"
- },
-
- reporters: ["spec", "failed", "coverage"],
- coverageReporter: {
- reporters: [
- {
- type: "lcovonly",
- dir: "coverage",
- subdir: "."
- },
- {
- type: "text-summary"
- }
- ]
- }
- });
-};
diff --git a/tests/phantomjs-es5-shim.js b/tests/phantomjs-es5-shim.js
deleted file mode 100644
index 97613e3..0000000
--- a/tests/phantomjs-es5-shim.js
+++ /dev/null
@@ -1,1432 +0,0 @@
-/*!
- * https://github.com/es-shims/es5-shim
- * @license es5-shim Copyright 2009-2014 by contributors, MIT License
- * see https://github.com/es-shims/es5-shim/blob/master/LICENSE
- */
-
-// vim: ts=4 sts=4 sw=4 expandtab
-
-//Add semicolon to prevent IIFE from being passed as argument to concated code.
-;
-
-// UMD (Universal Module Definition)
-// see https://github.com/umdjs/umd/blob/master/returnExports.js
-(function (root, factory) {
- if (typeof define === 'function' && define.amd) {
- // AMD. Register as an anonymous module.
- define(factory);
- } else if (typeof exports === 'object') {
- // Node. Does not work with strict CommonJS, but
- // only CommonJS-like enviroments that support module.exports,
- // like Node.
- module.exports = factory();
- } else {
- // Browser globals (root is window)
- root.returnExports = factory();
- }
-}(this, function () {
-
-/**
- * Brings an environment as close to ECMAScript 5 compliance
- * as is possible with the facilities of erstwhile engines.
- *
- * Annotated ES5: http://es5.github.com/ (specific links below)
- * ES5 Spec: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
- * Required reading: http://javascriptweblog.wordpress.com/2011/12/05/extending-javascript-natives/
- */
-
-// Shortcut to an often accessed properties, in order to avoid multiple
-// dereference that costs universally.
-var ArrayPrototype = Array.prototype;
-var ObjectPrototype = Object.prototype;
-var FunctionPrototype = Function.prototype;
-var StringPrototype = String.prototype;
-var NumberPrototype = Number.prototype;
-var _Array_slice_ = ArrayPrototype.slice;
-var array_splice = ArrayPrototype.splice;
-var array_push = ArrayPrototype.push;
-var array_unshift = ArrayPrototype.unshift;
-var call = FunctionPrototype.call;
-
-// Having a toString local variable name breaks in Opera so use _toString.
-var _toString = ObjectPrototype.toString;
-
-var isFunction = function (val) {
- return ObjectPrototype.toString.call(val) === '[object Function]';
-};
-var isRegex = function (val) {
- return ObjectPrototype.toString.call(val) === '[object RegExp]';
-};
-var isArray = function isArray(obj) {
- return _toString.call(obj) === "[object Array]";
-};
-var isString = function isString(obj) {
- return _toString.call(obj) === "[object String]";
-};
-var isArguments = function isArguments(value) {
- var str = _toString.call(value);
- var isArgs = str === '[object Arguments]';
- if (!isArgs) {
- isArgs = !isArray(str)
- && value !== null
- && typeof value === 'object'
- && typeof value.length === 'number'
- && value.length >= 0
- && isFunction(value.callee);
- }
- return isArgs;
-};
-
-var supportsDescriptors = Object.defineProperty && (function () {
- try {
- Object.defineProperty({}, 'x', {});
- return true;
- } catch (e) { /* this is ES3 */
- return false;
- }
-}());
-
-// Define configurable, writable and non-enumerable props
-// if they don't exist.
-var defineProperty;
-if (supportsDescriptors) {
- defineProperty = function (object, name, method, forceAssign) {
- if (!forceAssign && (name in object)) { return; }
- Object.defineProperty(object, name, {
- configurable: true,
- enumerable: false,
- writable: true,
- value: method
- });
- };
-} else {
- defineProperty = function (object, name, method, forceAssign) {
- if (!forceAssign && (name in object)) { return; }
- object[name] = method;
- };
-}
-var defineProperties = function (object, map, forceAssign) {
- for (var name in map) {
- if (ObjectPrototype.hasOwnProperty.call(map, name)) {
- defineProperty(object, name, map[name], forceAssign);
- }
- }
-};
-
-//
-// Util
-// ======
-//
-
-// ES5 9.4
-// http://es5.github.com/#x9.4
-// http://jsperf.com/to-integer
-
-function toInteger(n) {
- n = +n;
- if (n !== n) { // isNaN
- n = 0;
- } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
- n = (n > 0 || -1) * Math.floor(Math.abs(n));
- }
- return n;
-}
-
-function isPrimitive(input) {
- var type = typeof input;
- return (
- input === null ||
- type === "undefined" ||
- type === "boolean" ||
- type === "number" ||
- type === "string"
- );
-}
-
-function toPrimitive(input) {
- var val, valueOf, toStr;
- if (isPrimitive(input)) {
- return input;
- }
- valueOf = input.valueOf;
- if (isFunction(valueOf)) {
- val = valueOf.call(input);
- if (isPrimitive(val)) {
- return val;
- }
- }
- toStr = input.toString;
- if (isFunction(toStr)) {
- val = toStr.call(input);
- if (isPrimitive(val)) {
- return val;
- }
- }
- throw new TypeError();
-}
-
-// ES5 9.9
-// http://es5.github.com/#x9.9
-var toObject = function (o) {
- if (o == null) { // this matches both null and undefined
- throw new TypeError("can't convert " + o + " to object");
- }
- return Object(o);
-};
-
-var ToUint32 = function ToUint32(x) {
- return x >>> 0;
-};
-
-//
-// Function
-// ========
-//
-
-// ES-5 15.3.4.5
-// http://es5.github.com/#x15.3.4.5
-
-function Empty() {}
-
-defineProperties(FunctionPrototype, {
- bind: function bind(that) { // .length is 1
- // 1. Let Target be the this value.
- var target = this;
- // 2. If IsCallable(Target) is false, throw a TypeError exception.
- if (!isFunction(target)) {
- throw new TypeError("Function.prototype.bind called on incompatible " + target);
- }
- // 3. Let A be a new (possibly empty) internal list of all of the
- // argument values provided after thisArg (arg1, arg2 etc), in order.
- // XXX slicedArgs will stand in for "A" if used
- var args = _Array_slice_.call(arguments, 1); // for normal call
- // 4. Let F be a new native ECMAScript object.
- // 11. Set the [[Prototype]] internal property of F to the standard
- // built-in Function prototype object as specified in 15.3.3.1.
- // 12. Set the [[Call]] internal property of F as described in
- // 15.3.4.5.1.
- // 13. Set the [[Construct]] internal property of F as described in
- // 15.3.4.5.2.
- // 14. Set the [[HasInstance]] internal property of F as described in
- // 15.3.4.5.3.
- var binder = function () {
-
- if (this instanceof bound) {
- // 15.3.4.5.2 [[Construct]]
- // When the [[Construct]] internal method of a function object,
- // F that was created using the bind function is called with a
- // list of arguments ExtraArgs, the following steps are taken:
- // 1. Let target be the value of F's [[TargetFunction]]
- // internal property.
- // 2. If target has no [[Construct]] internal method, a
- // TypeError exception is thrown.
- // 3. Let boundArgs be the value of F's [[BoundArgs]] internal
- // property.
- // 4. Let args be a new list containing the same values as the
- // list boundArgs in the same order followed by the same
- // values as the list ExtraArgs in the same order.
- // 5. Return the result of calling the [[Construct]] internal
- // method of target providing args as the arguments.
-
- var result = target.apply(
- this,
- args.concat(_Array_slice_.call(arguments))
- );
- if (Object(result) === result) {
- return result;
- }
- return this;
-
- } else {
- // 15.3.4.5.1 [[Call]]
- // When the [[Call]] internal method of a function object, F,
- // which was created using the bind function is called with a
- // this value and a list of arguments ExtraArgs, the following
- // steps are taken:
- // 1. Let boundArgs be the value of F's [[BoundArgs]] internal
- // property.
- // 2. Let boundThis be the value of F's [[BoundThis]] internal
- // property.
- // 3. Let target be the value of F's [[TargetFunction]] internal
- // property.
- // 4. Let args be a new list containing the same values as the
- // list boundArgs in the same order followed by the same
- // values as the list ExtraArgs in the same order.
- // 5. Return the result of calling the [[Call]] internal method
- // of target providing boundThis as the this value and
- // providing args as the arguments.
-
- // equiv: target.call(this, ...boundArgs, ...args)
- return target.apply(
- that,
- args.concat(_Array_slice_.call(arguments))
- );
-
- }
-
- };
-
- // 15. If the [[Class]] internal property of Target is "Function", then
- // a. Let L be the length property of Target minus the length of A.
- // b. Set the length own property of F to either 0 or L, whichever is
- // larger.
- // 16. Else set the length own property of F to 0.
-
- var boundLength = Math.max(0, target.length - args.length);
-
- // 17. Set the attributes of the length own property of F to the values
- // specified in 15.3.5.1.
- var boundArgs = [];
- for (var i = 0; i < boundLength; i++) {
- boundArgs.push("$" + i);
- }
-
- // XXX Build a dynamic function with desired amount of arguments is the only
- // way to set the length property of a function.
- // In environments where Content Security Policies enabled (Chrome extensions,
- // for ex.) all use of eval or Function costructor throws an exception.
- // However in all of these environments Function.prototype.bind exists
- // and so this code will never be executed.
- var bound = Function("binder", "return function (" + boundArgs.join(",") + "){return binder.apply(this,arguments)}")(binder);
-
- if (target.prototype) {
- Empty.prototype = target.prototype;
- bound.prototype = new Empty();
- // Clean up dangling references.
- Empty.prototype = null;
- }
-
- // TODO
- // 18. Set the [[Extensible]] internal property of F to true.
-
- // TODO
- // 19. Let thrower be the [[ThrowTypeError]] function Object (13.2.3).
- // 20. Call the [[DefineOwnProperty]] internal method of F with
- // arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]:
- // thrower, [[Enumerable]]: false, [[Configurable]]: false}, and
- // false.
- // 21. Call the [[DefineOwnProperty]] internal method of F with
- // arguments "arguments", PropertyDescriptor {[[Get]]: thrower,
- // [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false},
- // and false.
-
- // TODO
- // NOTE Function objects created using Function.prototype.bind do not
- // have a prototype property or the [[Code]], [[FormalParameters]], and
- // [[Scope]] internal properties.
- // XXX can't delete prototype in pure-js.
-
- // 22. Return F.
- return bound;
- }
-});
-
-// _Please note: Shortcuts are defined after `Function.prototype.bind` as we
-// us it in defining shortcuts.
-var owns = call.bind(ObjectPrototype.hasOwnProperty);
-
-// If JS engine supports accessors creating shortcuts.
-var defineGetter;
-var defineSetter;
-var lookupGetter;
-var lookupSetter;
-var supportsAccessors;
-if ((supportsAccessors = owns(ObjectPrototype, "__defineGetter__"))) {
- defineGetter = call.bind(ObjectPrototype.__defineGetter__);
- defineSetter = call.bind(ObjectPrototype.__defineSetter__);
- lookupGetter = call.bind(ObjectPrototype.__lookupGetter__);
- lookupSetter = call.bind(ObjectPrototype.__lookupSetter__);
-}
-
-//
-// Array
-// =====
-//
-
-// ES5 15.4.4.12
-// http://es5.github.com/#x15.4.4.12
-var spliceNoopReturnsEmptyArray = (function () {
- var a = [1, 2];
- var result = a.splice();
- return a.length === 2 && isArray(result) && result.length === 0;
-}());
-defineProperties(ArrayPrototype, {
- // Safari 5.0 bug where .splice() returns undefined
- splice: function splice(start, deleteCount) {
- if (arguments.length === 0) {
- return [];
- } else {
- return array_splice.apply(this, arguments);
- }
- }
-}, spliceNoopReturnsEmptyArray);
-
-var spliceWorksWithEmptyObject = (function () {
- var obj = {};
- ArrayPrototype.splice.call(obj, 0, 0, 1);
- return obj.length === 1;
-}());
-var omittingSecondSpliceArgIsNoop = [1].splice(0).length === 0;
-defineProperties(ArrayPrototype, {
- splice: function splice(start, deleteCount) {
- if (arguments.length === 0) { return []; }
- var args = arguments;
- this.length = Math.max(toInteger(this.length), 0);
- if (arguments.length > 0 && typeof deleteCount !== 'number') {
- args = _Array_slice_.call(arguments);
- if (args.length < 2) {
- args.push(toInteger(deleteCount));
- } else {
- args[1] = toInteger(deleteCount);
- }
- }
- return array_splice.apply(this, args);
- }
-}, !omittingSecondSpliceArgIsNoop || !spliceWorksWithEmptyObject);
-
-// ES5 15.4.4.12
-// http://es5.github.com/#x15.4.4.13
-// Return len+argCount.
-// [bugfix, ielt8]
-// IE < 8 bug: [].unshift(0) === undefined but should be "1"
-var hasUnshiftReturnValueBug = [].unshift(0) !== 1;
-defineProperties(ArrayPrototype, {
- unshift: function () {
- array_unshift.apply(this, arguments);
- return this.length;
- }
-}, hasUnshiftReturnValueBug);
-
-// ES5 15.4.3.2
-// http://es5.github.com/#x15.4.3.2
-// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray
-defineProperties(Array, { isArray: isArray });
-
-// The IsCallable() check in the Array functions
-// has been replaced with a strict check on the
-// internal class of the object to trap cases where
-// the provided function was actually a regular
-// expression literal, which in V8 and
-// JavaScriptCore is a typeof "function". Only in
-// V8 are regular expression literals permitted as
-// reduce parameters, so it is desirable in the
-// general case for the shim to match the more
-// strict and common behavior of rejecting regular
-// expressions.
-
-// ES5 15.4.4.18
-// http://es5.github.com/#x15.4.4.18
-// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/forEach
-
-// Check failure of by-index access of string characters (IE < 9)
-// and failure of `0 in boxedString` (Rhino)
-var boxedString = Object("a");
-var splitString = boxedString[0] !== "a" || !(0 in boxedString);
-
-var properlyBoxesContext = function properlyBoxed(method) {
- // Check node 0.6.21 bug where third parameter is not boxed
- var properlyBoxesNonStrict = true;
- var properlyBoxesStrict = true;
- if (method) {
- method.call('foo', function (_, __, context) {
- if (typeof context !== 'object') { properlyBoxesNonStrict = false; }
- });
-
- method.call([1], function () {
- 'use strict';
- properlyBoxesStrict = typeof this === 'string';
- }, 'x');
- }
- return !!method && properlyBoxesNonStrict && properlyBoxesStrict;
-};
-
-defineProperties(ArrayPrototype, {
- forEach: function forEach(fun /*, thisp*/) {
- var object = toObject(this),
- self = splitString && isString(this) ? this.split('') : object,
- thisp = arguments[1],
- i = -1,
- length = self.length >>> 0;
-
- // If no callback function or if callback is not a callable function
- if (!isFunction(fun)) {
- throw new TypeError(); // TODO message
- }
-
- while (++i < length) {
- if (i in self) {
- // Invoke the callback function with call, passing arguments:
- // context, property value, property key, thisArg object
- // context
- fun.call(thisp, self[i], i, object);
- }
- }
- }
-}, !properlyBoxesContext(ArrayPrototype.forEach));
-
-// ES5 15.4.4.19
-// http://es5.github.com/#x15.4.4.19
-// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map
-defineProperties(ArrayPrototype, {
- map: function map(fun /*, thisp*/) {
- var object = toObject(this),
- self = splitString && isString(this) ? this.split('') : object,
- length = self.length >>> 0,
- result = Array(length),
- thisp = arguments[1];
-
- // If no callback function or if callback is not a callable function
- if (!isFunction(fun)) {
- throw new TypeError(fun + " is not a function");
- }
-
- for (var i = 0; i < length; i++) {
- if (i in self) {
- result[i] = fun.call(thisp, self[i], i, object);
- }
- }
- return result;
- }
-}, !properlyBoxesContext(ArrayPrototype.map));
-
-// ES5 15.4.4.20
-// http://es5.github.com/#x15.4.4.20
-// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter
-defineProperties(ArrayPrototype, {
- filter: function filter(fun /*, thisp */) {
- var object = toObject(this),
- self = splitString && isString(this) ? this.split('') : object,
- length = self.length >>> 0,
- result = [],
- value,
- thisp = arguments[1];
-
- // If no callback function or if callback is not a callable function
- if (!isFunction(fun)) {
- throw new TypeError(fun + " is not a function");
- }
-
- for (var i = 0; i < length; i++) {
- if (i in self) {
- value = self[i];
- if (fun.call(thisp, value, i, object)) {
- result.push(value);
- }
- }
- }
- return result;
- }
-}, !properlyBoxesContext(ArrayPrototype.filter));
-
-// ES5 15.4.4.16
-// http://es5.github.com/#x15.4.4.16
-// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every
-defineProperties(ArrayPrototype, {
- every: function every(fun /*, thisp */) {
- var object = toObject(this),
- self = splitString && isString(this) ? this.split('') : object,
- length = self.length >>> 0,
- thisp = arguments[1];
-
- // If no callback function or if callback is not a callable function
- if (!isFunction(fun)) {
- throw new TypeError(fun + " is not a function");
- }
-
- for (var i = 0; i < length; i++) {
- if (i in self && !fun.call(thisp, self[i], i, object)) {
- return false;
- }
- }
- return true;
- }
-}, !properlyBoxesContext(ArrayPrototype.every));
-
-// ES5 15.4.4.17
-// http://es5.github.com/#x15.4.4.17
-// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some
-defineProperties(ArrayPrototype, {
- some: function some(fun /*, thisp */) {
- var object = toObject(this),
- self = splitString && isString(this) ? this.split('') : object,
- length = self.length >>> 0,
- thisp = arguments[1];
-
- // If no callback function or if callback is not a callable function
- if (!isFunction(fun)) {
- throw new TypeError(fun + " is not a function");
- }
-
- for (var i = 0; i < length; i++) {
- if (i in self && fun.call(thisp, self[i], i, object)) {
- return true;
- }
- }
- return false;
- }
-}, !properlyBoxesContext(ArrayPrototype.some));
-
-// ES5 15.4.4.21
-// http://es5.github.com/#x15.4.4.21
-// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce
-var reduceCoercesToObject = false;
-if (ArrayPrototype.reduce) {
- reduceCoercesToObject = typeof ArrayPrototype.reduce.call('es5', function (_, __, ___, list) { return list; }) === 'object';
-}
-defineProperties(ArrayPrototype, {
- reduce: function reduce(fun /*, initial*/) {
- var object = toObject(this),
- self = splitString && isString(this) ? this.split('') : object,
- length = self.length >>> 0;
-
- // If no callback function or if callback is not a callable function
- if (!isFunction(fun)) {
- throw new TypeError(fun + " is not a function");
- }
-
- // no value to return if no initial value and an empty array
- if (!length && arguments.length === 1) {
- throw new TypeError("reduce of empty array with no initial value");
- }
-
- var i = 0;
- var result;
- if (arguments.length >= 2) {
- result = arguments[1];
- } else {
- do {
- if (i in self) {
- result = self[i++];
- break;
- }
-
- // if array contains no values, no initial value to return
- if (++i >= length) {
- throw new TypeError("reduce of empty array with no initial value");
- }
- } while (true);
- }
-
- for (; i < length; i++) {
- if (i in self) {
- result = fun.call(void 0, result, self[i], i, object);
- }
- }
-
- return result;
- }
-}, !reduceCoercesToObject);
-
-// ES5 15.4.4.22
-// http://es5.github.com/#x15.4.4.22
-// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight
-var reduceRightCoercesToObject = false;
-if (ArrayPrototype.reduceRight) {
- reduceRightCoercesToObject = typeof ArrayPrototype.reduceRight.call('es5', function (_, __, ___, list) { return list; }) === 'object';
-}
-defineProperties(ArrayPrototype, {
- reduceRight: function reduceRight(fun /*, initial*/) {
- var object = toObject(this),
- self = splitString && isString(this) ? this.split('') : object,
- length = self.length >>> 0;
-
- // If no callback function or if callback is not a callable function
- if (!isFunction(fun)) {
- throw new TypeError(fun + " is not a function");
- }
-
- // no value to return if no initial value, empty array
- if (!length && arguments.length === 1) {
- throw new TypeError("reduceRight of empty array with no initial value");
- }
-
- var result, i = length - 1;
- if (arguments.length >= 2) {
- result = arguments[1];
- } else {
- do {
- if (i in self) {
- result = self[i--];
- break;
- }
-
- // if array contains no values, no initial value to return
- if (--i < 0) {
- throw new TypeError("reduceRight of empty array with no initial value");
- }
- } while (true);
- }
-
- if (i < 0) {
- return result;
- }
-
- do {
- if (i in self) {
- result = fun.call(void 0, result, self[i], i, object);
- }
- } while (i--);
-
- return result;
- }
-}, !reduceRightCoercesToObject);
-
-// ES5 15.4.4.14
-// http://es5.github.com/#x15.4.4.14
-// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf
-var hasFirefox2IndexOfBug = Array.prototype.indexOf && [0, 1].indexOf(1, 2) !== -1;
-defineProperties(ArrayPrototype, {
- indexOf: function indexOf(sought /*, fromIndex */ ) {
- var self = splitString && isString(this) ? this.split('') : toObject(this),
- length = self.length >>> 0;
-
- if (!length) {
- return -1;
- }
-
- var i = 0;
- if (arguments.length > 1) {
- i = toInteger(arguments[1]);
- }
-
- // handle negative indices
- i = i >= 0 ? i : Math.max(0, length + i);
- for (; i < length; i++) {
- if (i in self && self[i] === sought) {
- return i;
- }
- }
- return -1;
- }
-}, hasFirefox2IndexOfBug);
-
-// ES5 15.4.4.15
-// http://es5.github.com/#x15.4.4.15
-// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/lastIndexOf
-var hasFirefox2LastIndexOfBug = Array.prototype.lastIndexOf && [0, 1].lastIndexOf(0, -3) !== -1;
-defineProperties(ArrayPrototype, {
- lastIndexOf: function lastIndexOf(sought /*, fromIndex */) {
- var self = splitString && isString(this) ? this.split('') : toObject(this),
- length = self.length >>> 0;
-
- if (!length) {
- return -1;
- }
- var i = length - 1;
- if (arguments.length > 1) {
- i = Math.min(i, toInteger(arguments[1]));
- }
- // handle negative indices
- i = i >= 0 ? i : length - Math.abs(i);
- for (; i >= 0; i--) {
- if (i in self && sought === self[i]) {
- return i;
- }
- }
- return -1;
- }
-}, hasFirefox2LastIndexOfBug);
-
-//
-// Object
-// ======
-//
-
-// ES5 15.2.3.14
-// http://es5.github.com/#x15.2.3.14
-
-// http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation
-var hasDontEnumBug = !({'toString': null}).propertyIsEnumerable('toString'),
- hasProtoEnumBug = (function () {}).propertyIsEnumerable('prototype'),
- dontEnums = [
- "toString",
- "toLocaleString",
- "valueOf",
- "hasOwnProperty",
- "isPrototypeOf",
- "propertyIsEnumerable",
- "constructor"
- ],
- dontEnumsLength = dontEnums.length;
-
-defineProperties(Object, {
- keys: function keys(object) {
- var isFn = isFunction(object),
- isArgs = isArguments(object),
- isObject = object !== null && typeof object === 'object',
- isStr = isObject && isString(object);
-
- if (!isObject && !isFn && !isArgs) {
- throw new TypeError("Object.keys called on a non-object");
- }
-
- var theKeys = [];
- var skipProto = hasProtoEnumBug && isFn;
- if (isStr || isArgs) {
- for (var i = 0; i < object.length; ++i) {
- theKeys.push(String(i));
- }
- } else {
- for (var name in object) {
- if (!(skipProto && name === 'prototype') && owns(object, name)) {
- theKeys.push(String(name));
- }
- }
- }
-
- if (hasDontEnumBug) {
- var ctor = object.constructor,
- skipConstructor = ctor && ctor.prototype === object;
- for (var j = 0; j < dontEnumsLength; j++) {
- var dontEnum = dontEnums[j];
- if (!(skipConstructor && dontEnum === 'constructor') && owns(object, dontEnum)) {
- theKeys.push(dontEnum);
- }
- }
- }
- return theKeys;
- }
-});
-
-var keysWorksWithArguments = Object.keys && (function () {
- // Safari 5.0 bug
- return Object.keys(arguments).length === 2;
-}(1, 2));
-var originalKeys = Object.keys;
-defineProperties(Object, {
- keys: function keys(object) {
- if (isArguments(object)) {
- return originalKeys(ArrayPrototype.slice.call(object));
- } else {
- return originalKeys(object);
- }
- }
-}, !keysWorksWithArguments);
-
-//
-// Date
-// ====
-//
-
-// ES5 15.9.5.43
-// http://es5.github.com/#x15.9.5.43
-// This function returns a String value represent the instance in time
-// represented by this Date object. The format of the String is the Date Time
-// string format defined in 15.9.1.15. All fields are present in the String.
-// The time zone is always UTC, denoted by the suffix Z. If the time value of
-// this object is not a finite Number a RangeError exception is thrown.
-var negativeDate = -62198755200000;
-var negativeYearString = "-000001";
-var hasNegativeDateBug = Date.prototype.toISOString && new Date(negativeDate).toISOString().indexOf(negativeYearString) === -1;
-
-defineProperties(Date.prototype, {
- toISOString: function toISOString() {
- var result, length, value, year, month;
- if (!isFinite(this)) {
- throw new RangeError("Date.prototype.toISOString called on non-finite value.");
- }
-
- year = this.getUTCFullYear();
-
- month = this.getUTCMonth();
- // see https://github.com/es-shims/es5-shim/issues/111
- year += Math.floor(month / 12);
- month = (month % 12 + 12) % 12;
-
- // the date time string format is specified in 15.9.1.15.
- result = [month + 1, this.getUTCDate(), this.getUTCHours(), this.getUTCMinutes(), this.getUTCSeconds()];
- year = (
- (year < 0 ? "-" : (year > 9999 ? "+" : "")) +
- ("00000" + Math.abs(year)).slice(0 <= year && year <= 9999 ? -4 : -6)
- );
-
- length = result.length;
- while (length--) {
- value = result[length];
- // pad months, days, hours, minutes, and seconds to have two
- // digits.
- if (value < 10) {
- result[length] = "0" + value;
- }
- }
- // pad milliseconds to have three digits.
- return (
- year + "-" + result.slice(0, 2).join("-") +
- "T" + result.slice(2).join(":") + "." +
- ("000" + this.getUTCMilliseconds()).slice(-3) + "Z"
- );
- }
-}, hasNegativeDateBug);
-
-
-// ES5 15.9.5.44
-// http://es5.github.com/#x15.9.5.44
-// This function provides a String representation of a Date object for use by
-// JSON.stringify (15.12.3).
-var dateToJSONIsSupported = false;
-try {
- dateToJSONIsSupported = (
- Date.prototype.toJSON &&
- new Date(NaN).toJSON() === null &&
- new Date(negativeDate).toJSON().indexOf(negativeYearString) !== -1 &&
- Date.prototype.toJSON.call({ // generic
- toISOString: function () {
- return true;
- }
- })
- );
-} catch (e) {
-}
-if (!dateToJSONIsSupported) {
- Date.prototype.toJSON = function toJSON(key) {
- // When the toJSON method is called with argument key, the following
- // steps are taken:
-
- // 1. Let O be the result of calling ToObject, giving it the this
- // value as its argument.
- // 2. Let tv be toPrimitive(O, hint Number).
- var o = Object(this),
- tv = toPrimitive(o),
- toISO;
- // 3. If tv is a Number and is not finite, return null.
- if (typeof tv === "number" && !isFinite(tv)) {
- return null;
- }
- // 4. Let toISO be the result of calling the [[Get]] internal method of
- // O with argument "toISOString".
- toISO = o.toISOString;
- // 5. If IsCallable(toISO) is false, throw a TypeError exception.
- if (typeof toISO !== "function") {
- throw new TypeError("toISOString property is not callable");
- }
- // 6. Return the result of calling the [[Call]] internal method of
- // toISO with O as the this value and an empty argument list.
- return toISO.call(o);
-
- // NOTE 1 The argument is ignored.
-
- // NOTE 2 The toJSON function is intentionally generic; it does not
- // require that its this value be a Date object. Therefore, it can be
- // transferred to other kinds of objects for use as a method. However,
- // it does require that any such object have a toISOString method. An
- // object is free to use the argument key to filter its
- // stringification.
- };
-}
-
-// ES5 15.9.4.2
-// http://es5.github.com/#x15.9.4.2
-// based on work shared by Daniel Friesen (dantman)
-// http://gist.github.com/303249
-var supportsExtendedYears = Date.parse('+033658-09-27T01:46:40.000Z') === 1e15;
-var acceptsInvalidDates = !isNaN(Date.parse('2012-04-04T24:00:00.500Z')) || !isNaN(Date.parse('2012-11-31T23:59:59.000Z'));
-var doesNotParseY2KNewYear = isNaN(Date.parse("2000-01-01T00:00:00.000Z"));
-if (!Date.parse || doesNotParseY2KNewYear || acceptsInvalidDates || !supportsExtendedYears) {
- // XXX global assignment won't work in embeddings that use
- // an alternate object for the context.
- Date = (function (NativeDate) {
-
- // Date.length === 7
- function Date(Y, M, D, h, m, s, ms) {
- var length = arguments.length;
- if (this instanceof NativeDate) {
- var date = length === 1 && String(Y) === Y ? // isString(Y)
- // We explicitly pass it through parse:
- new NativeDate(Date.parse(Y)) :
- // We have to manually make calls depending on argument
- // length here
- length >= 7 ? new NativeDate(Y, M, D, h, m, s, ms) :
- length >= 6 ? new NativeDate(Y, M, D, h, m, s) :
- length >= 5 ? new NativeDate(Y, M, D, h, m) :
- length >= 4 ? new NativeDate(Y, M, D, h) :
- length >= 3 ? new NativeDate(Y, M, D) :
- length >= 2 ? new NativeDate(Y, M) :
- length >= 1 ? new NativeDate(Y) :
- new NativeDate();
- // Prevent mixups with unfixed Date object
- date.constructor = Date;
- return date;
- }
- return NativeDate.apply(this, arguments);
- }
-
- // 15.9.1.15 Date Time String Format.
- var isoDateExpression = new RegExp("^" +
- "(\\d{4}|[\+\-]\\d{6})" + // four-digit year capture or sign +
- // 6-digit extended year
- "(?:-(\\d{2})" + // optional month capture
- "(?:-(\\d{2})" + // optional day capture
- "(?:" + // capture hours:minutes:seconds.milliseconds
- "T(\\d{2})" + // hours capture
- ":(\\d{2})" + // minutes capture
- "(?:" + // optional :seconds.milliseconds
- ":(\\d{2})" + // seconds capture
- "(?:(\\.\\d{1,}))?" + // milliseconds capture
- ")?" +
- "(" + // capture UTC offset component
- "Z|" + // UTC capture
- "(?:" + // offset specifier +/-hours:minutes
- "([-+])" + // sign capture
- "(\\d{2})" + // hours offset capture
- ":(\\d{2})" + // minutes offset capture
- ")" +
- ")?)?)?)?" +
- "$");
-
- var months = [
- 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
- ];
-
- function dayFromMonth(year, month) {
- var t = month > 1 ? 1 : 0;
- return (
- months[month] +
- Math.floor((year - 1969 + t) / 4) -
- Math.floor((year - 1901 + t) / 100) +
- Math.floor((year - 1601 + t) / 400) +
- 365 * (year - 1970)
- );
- }
-
- function toUTC(t) {
- return Number(new NativeDate(1970, 0, 1, 0, 0, 0, t));
- }
-
- // Copy any custom methods a 3rd party library may have added
- for (var key in NativeDate) {
- Date[key] = NativeDate[key];
- }
-
- // Copy "native" methods explicitly; they may be non-enumerable
- Date.now = NativeDate.now;
- Date.UTC = NativeDate.UTC;
- Date.prototype = NativeDate.prototype;
- Date.prototype.constructor = Date;
-
- // Upgrade Date.parse to handle simplified ISO 8601 strings
- Date.parse = function parse(string) {
- var match = isoDateExpression.exec(string);
- if (match) {
- // parse months, days, hours, minutes, seconds, and milliseconds
- // provide default values if necessary
- // parse the UTC offset component
- var year = Number(match[1]),
- month = Number(match[2] || 1) - 1,
- day = Number(match[3] || 1) - 1,
- hour = Number(match[4] || 0),
- minute = Number(match[5] || 0),
- second = Number(match[6] || 0),
- millisecond = Math.floor(Number(match[7] || 0) * 1000),
- // When time zone is missed, local offset should be used
- // (ES 5.1 bug)
- // see https://bugs.ecmascript.org/show_bug.cgi?id=112
- isLocalTime = Boolean(match[4] && !match[8]),
- signOffset = match[9] === "-" ? 1 : -1,
- hourOffset = Number(match[10] || 0),
- minuteOffset = Number(match[11] || 0),
- result;
- if (
- hour < (
- minute > 0 || second > 0 || millisecond > 0 ?
- 24 : 25
- ) &&
- minute < 60 && second < 60 && millisecond < 1000 &&
- month > -1 && month < 12 && hourOffset < 24 &&
- minuteOffset < 60 && // detect invalid offsets
- day > -1 &&
- day < (
- dayFromMonth(year, month + 1) -
- dayFromMonth(year, month)
- )
- ) {
- result = (
- (dayFromMonth(year, month) + day) * 24 +
- hour +
- hourOffset * signOffset
- ) * 60;
- result = (
- (result + minute + minuteOffset * signOffset) * 60 +
- second
- ) * 1000 + millisecond;
- if (isLocalTime) {
- result = toUTC(result);
- }
- if (-8.64e15 <= result && result <= 8.64e15) {
- return result;
- }
- }
- return NaN;
- }
- return NativeDate.parse.apply(this, arguments);
- };
-
- return Date;
- })(Date);
-}
-
-// ES5 15.9.4.4
-// http://es5.github.com/#x15.9.4.4
-if (!Date.now) {
- Date.now = function now() {
- return new Date().getTime();
- };
-}
-
-
-//
-// Number
-// ======
-//
-
-// ES5.1 15.7.4.5
-// http://es5.github.com/#x15.7.4.5
-var hasToFixedBugs = NumberPrototype.toFixed && (
- (0.00008).toFixed(3) !== '0.000'
- || (0.9).toFixed(0) === '0'
- || (1.255).toFixed(2) !== '1.25'
- || (1000000000000000128).toFixed(0) !== "1000000000000000128"
-);
-
-var toFixedHelpers = {
- base: 1e7,
- size: 6,
- data: [0, 0, 0, 0, 0, 0],
- multiply: function multiply(n, c) {
- var i = -1;
- while (++i < toFixedHelpers.size) {
- c += n * toFixedHelpers.data[i];
- toFixedHelpers.data[i] = c % toFixedHelpers.base;
- c = Math.floor(c / toFixedHelpers.base);
- }
- },
- divide: function divide(n) {
- var i = toFixedHelpers.size, c = 0;
- while (--i >= 0) {
- c += toFixedHelpers.data[i];
- toFixedHelpers.data[i] = Math.floor(c / n);
- c = (c % n) * toFixedHelpers.base;
- }
- },
- numToString: function numToString() {
- var i = toFixedHelpers.size;
- var s = '';
- while (--i >= 0) {
- if (s !== '' || i === 0 || toFixedHelpers.data[i] !== 0) {
- var t = String(toFixedHelpers.data[i]);
- if (s === '') {
- s = t;
- } else {
- s += '0000000'.slice(0, 7 - t.length) + t;
- }
- }
- }
- return s;
- },
- pow: function pow(x, n, acc) {
- return (n === 0 ? acc : (n % 2 === 1 ? pow(x, n - 1, acc * x) : pow(x * x, n / 2, acc)));
- },
- log: function log(x) {
- var n = 0;
- while (x >= 4096) {
- n += 12;
- x /= 4096;
- }
- while (x >= 2) {
- n += 1;
- x /= 2;
- }
- return n;
- }
-};
-
-defineProperties(NumberPrototype, {
- toFixed: function toFixed(fractionDigits) {
- var f, x, s, m, e, z, j, k;
-
- // Test for NaN and round fractionDigits down
- f = Number(fractionDigits);
- f = f !== f ? 0 : Math.floor(f);
-
- if (f < 0 || f > 20) {
- throw new RangeError("Number.toFixed called with invalid number of decimals");
- }
-
- x = Number(this);
-
- // Test for NaN
- if (x !== x) {
- return "NaN";
- }
-
- // If it is too big or small, return the string value of the number
- if (x <= -1e21 || x >= 1e21) {
- return String(x);
- }
-
- s = "";
-
- if (x < 0) {
- s = "-";
- x = -x;
- }
-
- m = "0";
-
- if (x > 1e-21) {
- // 1e-21 < x < 1e21
- // -70 < log2(x) < 70
- e = toFixedHelpers.log(x * toFixedHelpers.pow(2, 69, 1)) - 69;
- z = (e < 0 ? x * toFixedHelpers.pow(2, -e, 1) : x / toFixedHelpers.pow(2, e, 1));
- z *= 0x10000000000000; // Math.pow(2, 52);
- e = 52 - e;
-
- // -18 < e < 122
- // x = z / 2 ^ e
- if (e > 0) {
- toFixedHelpers.multiply(0, z);
- j = f;
-
- while (j >= 7) {
- toFixedHelpers.multiply(1e7, 0);
- j -= 7;
- }
-
- toFixedHelpers.multiply(toFixedHelpers.pow(10, j, 1), 0);
- j = e - 1;
-
- while (j >= 23) {
- toFixedHelpers.divide(1 << 23);
- j -= 23;
- }
-
- toFixedHelpers.divide(1 << j);
- toFixedHelpers.multiply(1, 1);
- toFixedHelpers.divide(2);
- m = toFixedHelpers.numToString();
- } else {
- toFixedHelpers.multiply(0, z);
- toFixedHelpers.multiply(1 << (-e), 0);
- m = toFixedHelpers.numToString() + '0.00000000000000000000'.slice(2, 2 + f);
- }
- }
-
- if (f > 0) {
- k = m.length;
-
- if (k <= f) {
- m = s + '0.0000000000000000000'.slice(0, f - k + 2) + m;
- } else {
- m = s + m.slice(0, k - f) + '.' + m.slice(k - f);
- }
- } else {
- m = s + m;
- }
-
- return m;
- }
-}, hasToFixedBugs);
-
-
-//
-// String
-// ======
-//
-
-// ES5 15.5.4.14
-// http://es5.github.com/#x15.5.4.14
-
-// [bugfix, IE lt 9, firefox 4, Konqueror, Opera, obscure browsers]
-// Many browsers do not split properly with regular expressions or they
-// do not perform the split correctly under obscure conditions.
-// See http://blog.stevenlevithan.com/archives/cross-browser-split
-// I've tested in many browsers and this seems to cover the deviant ones:
-// 'ab'.split(/(?:ab)*/) should be ["", ""], not [""]
-// '.'.split(/(.?)(.?)/) should be ["", ".", "", ""], not ["", ""]
-// 'tesst'.split(/(s)*/) should be ["t", undefined, "e", "s", "t"], not
-// [undefined, "t", undefined, "e", ...]
-// ''.split(/.?/) should be [], not [""]
-// '.'.split(/()()/) should be ["."], not ["", "", "."]
-
-var string_split = StringPrototype.split;
-if (
- 'ab'.split(/(?:ab)*/).length !== 2 ||
- '.'.split(/(.?)(.?)/).length !== 4 ||
- 'tesst'.split(/(s)*/)[1] === "t" ||
- 'test'.split(/(?:)/, -1).length !== 4 ||
- ''.split(/.?/).length ||
- '.'.split(/()()/).length > 1
-) {
- (function () {
- var compliantExecNpcg = /()??/.exec("")[1] === void 0; // NPCG: nonparticipating capturing group
-
- StringPrototype.split = function (separator, limit) {
- var string = this;
- if (separator === void 0 && limit === 0) {
- return [];
- }
-
- // If `separator` is not a regex, use native split
- if (_toString.call(separator) !== "[object RegExp]") {
- return string_split.call(this, separator, limit);
- }
-
- var output = [],
- flags = (separator.ignoreCase ? "i" : "") +
- (separator.multiline ? "m" : "") +
- (separator.extended ? "x" : "") + // Proposed for ES6
- (separator.sticky ? "y" : ""), // Firefox 3+
- lastLastIndex = 0,
- // Make `global` and avoid `lastIndex` issues by working with a copy
- separator2, match, lastIndex, lastLength;
- separator = new RegExp(separator.source, flags + "g");
- string += ""; // Type-convert
- if (!compliantExecNpcg) {
- // Doesn't need flags gy, but they don't hurt
- separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
- }
- /* Values for `limit`, per the spec:
- * If undefined: 4294967295 // Math.pow(2, 32) - 1
- * If 0, Infinity, or NaN: 0
- * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
- * If negative number: 4294967296 - Math.floor(Math.abs(limit))
- * If other: Type-convert, then use the above rules
- */
- limit = limit === void 0 ?
- -1 >>> 0 : // Math.pow(2, 32) - 1
- ToUint32(limit);
- while (match = separator.exec(string)) {
- // `separator.lastIndex` is not reliable cross-browser
- lastIndex = match.index + match[0].length;
- if (lastIndex > lastLastIndex) {
- output.push(string.slice(lastLastIndex, match.index));
- // Fix browsers whose `exec` methods don't consistently return `undefined` for
- // nonparticipating capturing groups
- if (!compliantExecNpcg && match.length > 1) {
- match[0].replace(separator2, function () {
- for (var i = 1; i < arguments.length - 2; i++) {
- if (arguments[i] === void 0) {
- match[i] = void 0;
- }
- }
- });
- }
- if (match.length > 1 && match.index < string.length) {
- ArrayPrototype.push.apply(output, match.slice(1));
- }
- lastLength = match[0].length;
- lastLastIndex = lastIndex;
- if (output.length >= limit) {
- break;
- }
- }
- if (separator.lastIndex === match.index) {
- separator.lastIndex++; // Avoid an infinite loop
- }
- }
- if (lastLastIndex === string.length) {
- if (lastLength || !separator.test("")) {
- output.push("");
- }
- } else {
- output.push(string.slice(lastLastIndex));
- }
- return output.length > limit ? output.slice(0, limit) : output;
- };
- }());
-
-// [bugfix, chrome]
-// If separator is undefined, then the result array contains just one String,
-// which is the this value (converted to a String). If limit is not undefined,
-// then the output array is truncated so that it contains no more than limit
-// elements.
-// "0".split(undefined, 0) -> []
-} else if ("0".split(void 0, 0).length) {
- StringPrototype.split = function split(separator, limit) {
- if (separator === void 0 && limit === 0) { return []; }
- return string_split.call(this, separator, limit);
- };
-}
-
-var str_replace = StringPrototype.replace;
-var replaceReportsGroupsCorrectly = (function () {
- var groups = [];
- 'x'.replace(/x(.)?/g, function (match, group) {
- groups.push(group);
- });
- return groups.length === 1 && typeof groups[0] === 'undefined';
-}());
-
-if (!replaceReportsGroupsCorrectly) {
- StringPrototype.replace = function replace(searchValue, replaceValue) {
- var isFn = isFunction(replaceValue);
- var hasCapturingGroups = isRegex(searchValue) && (/\)[*?]/).test(searchValue.source);
- if (!isFn || !hasCapturingGroups) {
- return str_replace.call(this, searchValue, replaceValue);
- } else {
- var wrappedReplaceValue = function (match) {
- var length = arguments.length;
- var originalLastIndex = searchValue.lastIndex;
- searchValue.lastIndex = 0;
- var args = searchValue.exec(match);
- searchValue.lastIndex = originalLastIndex;
- args.push(arguments[length - 2], arguments[length - 1]);
- return replaceValue.apply(this, args);
- };
- return str_replace.call(this, searchValue, wrappedReplaceValue);
- }
- };
-}
-
-// ECMA-262, 3rd B.2.3
-// Not an ECMAScript standard, although ECMAScript 3rd Edition has a
-// non-normative section suggesting uniform semantics and it should be
-// normalized across all browsers
-// [bugfix, IE lt 9] IE < 9 substr() with negative value not working in IE
-var string_substr = StringPrototype.substr;
-var hasNegativeSubstrBug = "".substr && "0b".substr(-1) !== "b";
-defineProperties(StringPrototype, {
- substr: function substr(start, length) {
- return string_substr.call(
- this,
- start < 0 ? ((start = this.length + start) < 0 ? 0 : start) : start,
- length
- );
- }
-}, hasNegativeSubstrBug);
-
-// ES5 15.5.4.20
-// whitespace from: http://es5.github.io/#x15.5.4.20
-var ws = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003" +
- "\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028" +
- "\u2029\uFEFF";
-var zeroWidth = '\u200b';
-var wsRegexChars = "[" + ws + "]";
-var trimBeginRegexp = new RegExp("^" + wsRegexChars + wsRegexChars + "*");
-var trimEndRegexp = new RegExp(wsRegexChars + wsRegexChars + "*$");
-var hasTrimWhitespaceBug = StringPrototype.trim && (ws.trim() || !zeroWidth.trim());
-defineProperties(StringPrototype, {
- // http://blog.stevenlevithan.com/archives/faster-trim-javascript
- // http://perfectionkills.com/whitespace-deviations/
- trim: function trim() {
- if (this === void 0 || this === null) {
- throw new TypeError("can't convert " + this + " to object");
- }
- return String(this).replace(trimBeginRegexp, "").replace(trimEndRegexp, "");
- }
-}, hasTrimWhitespaceBug);
-
-// ES-5 15.1.2.2
-if (parseInt(ws + '08') !== 8 || parseInt(ws + '0x16') !== 22) {
- parseInt = (function (origParseInt) {
- var hexRegex = /^0[xX]/;
- return function parseIntES5(str, radix) {
- str = String(str).trim();
- if (!Number(radix)) {
- radix = hexRegex.test(str) ? 16 : 10;
- }
- return origParseInt(str, radix);
- };
- }(parseInt));
-}
-
-}));
\ No newline at end of file
diff --git a/tests/reactfire.spec.js b/tests/reactfire.spec.js
new file mode 100644
index 0000000..3e3accf
--- /dev/null
+++ b/tests/reactfire.spec.js
@@ -0,0 +1,432 @@
+'use strict';
+
+// Mocha / Chai
+var chai = require('chai');
+var expect = chai.expect;
+
+// React
+var React = require('react/addons');
+var ReactTestUtils = React.addons.TestUtils;
+
+// ReactFire
+var Firebase = require('firebase');
+var ReactFireMixin = require('../src/reactfire.js');
+
+// JSDom
+var jsdom = require('jsdom');
+global.document = jsdom.jsdom(); // Needed for ReactTestUtils shallow renderer
+document.createElement = null; // Needed for Firebase
+
+// Test helpers
+var TH = require('./helpers.js');
+
+// Get a reference to a random demo Firebase
+var demoFirebaseUrl = 'https://' + TH.generateRandomString() + '.firebaseio-demo.com';
+
+
+describe('ReactFire', function() {
+ var firebaseRef;
+ var shallowRenderer;
+
+ beforeEach(function(done) {
+ shallowRenderer = ReactTestUtils.createRenderer();
+
+ firebaseRef = new Firebase(demoFirebaseUrl);
+ firebaseRef.remove(function(error) {
+ if (error) {
+ done(error);
+ } else {
+ firebaseRef = firebaseRef.child(TH.generateRandomString());
+ done();
+ }
+ });
+ });
+
+
+ describe('bindAsArray()', function() {
+ it('throws error given invalid Firebase reference', function() {
+ var TestComponent = React.createClass({
+ mixins: [ReactFireMixin],
+
+ componentWillMount: function() {
+ var _this = this;
+
+ TH.invalidFirebaseRefs.forEach(function(invalidFirebaseRef) {
+ expect(function() {
+ _this.bindAsArray(invalidFirebaseRef, 'items');
+ }).to.throw('ReactFire: firebaseRef must be an instance of Firebase');
+ });
+ },
+
+ render: function() {
+ return React.DOM.div(null);
+ }
+ });
+
+ shallowRenderer.render(React.createElement(TestComponent));
+ });
+
+ it('throws error given invalid bind variable', function() {
+ var TestComponent = React.createClass({
+ mixins: [ReactFireMixin],
+
+ componentWillMount: function() {
+ var _this = this;
+
+ TH.invalidBindVars.forEach(function(invalidBindVar) {
+ expect(function() {
+ _this.bindAsArray(firebaseRef, invalidBindVar);
+ }).to.throw(/bindVar/);
+ });
+ },
+
+ render: function() {
+ return React.DOM.div(null);
+ }
+ });
+
+ shallowRenderer.render(React.createElement(TestComponent));
+ });
+
+ it('binds array items which are objects', function(done) {
+ var TestComponent = React.createClass({
+ mixins: [ReactFireMixin],
+
+ componentWillMount: function() {
+ this.bindAsArray(firebaseRef, 'items');
+
+ firebaseRef.set({
+ first: { index: 0 },
+ second: { index: 1 },
+ third: { index: 2 }
+ }, function() {
+ expect(this.state.items).to.deep.equal([
+ { '$key': 'first', index: 0 },
+ { '$key': 'second', index: 1 },
+ { '$key': 'third', index: 2 }
+ ]);
+
+ done();
+ }.bind(this));
+ },
+
+ render: function() {
+ return React.DOM.div(null);
+ }
+ });
+
+ shallowRenderer.render(React.createElement(TestComponent));
+ });
+
+ it('binds array items which are primitives', function(done) {
+ var TestComponent = React.createClass({
+ mixins: [ReactFireMixin],
+
+ componentWillMount: function() {
+ this.bindAsArray(firebaseRef, 'items');
+
+ firebaseRef.set(['first', 'second', 'third'], function() {
+ expect(this.state.items).to.deep.equal([
+ { '$key': 0, '$value': 'first' },
+ { '$key': 1, '$value': 'second' },
+ { '$key': 2, '$value': 'third' }
+ ]);
+
+ done();
+ }.bind(this));
+ },
+
+ render: function() {
+ return React.DOM.div(null);
+ }
+ });
+
+ shallowRenderer.render(React.createElement(TestComponent));
+ });
+
+ it('binds sparse arrays', function(done) {
+ var TestComponent = React.createClass({
+ mixins: [ReactFireMixin],
+
+ componentWillMount: function() {
+ this.bindAsArray(firebaseRef, 'items');
+
+ firebaseRef.set({ 0: 'a', 2: 'b', 5: 'c' }, function() {
+ expect(this.state).to.deep.equal({
+ items: [
+ { $key: 0, $value: 'a' },
+ { $key: 2, $value: 'b' },
+ { $key: 5, $value: 'c' }
+ ]
+ });
+
+ done();
+ }.bind(this));
+ },
+
+ render: function() {
+ return React.DOM.div(null);
+ }
+ });
+
+ shallowRenderer.render(React.createElement(TestComponent));
+ });
+
+ it('binds with limit queries', function(done) {
+ var TestComponent = React.createClass({
+ mixins: [ReactFireMixin],
+
+ componentWillMount: function() {
+ this.bindAsArray(firebaseRef.limitToLast(2), 'items');
+
+ firebaseRef.set({ a: 1, b: 2, c: 3 }, function() {
+ expect(this.state).to.deep.equal({
+ items: [
+ { $key: 'b', $value: 2 },
+ { $key: 'c', $value: 3 }
+ ]
+ });
+
+ done();
+ }.bind(this));
+ },
+
+ render: function() {
+ return React.DOM.div(null);
+ }
+ });
+
+ shallowRenderer.render(React.createElement(TestComponent));
+ });
+ });
+
+
+ describe('bindAsObject()', function() {
+ it('throws error given invalid Firebase reference', function() {
+ var TestComponent = React.createClass({
+ mixins: [ReactFireMixin],
+
+ componentWillMount: function() {
+ var _this = this;
+
+ TH.invalidFirebaseRefs.forEach(function(invalidFirebaseRef) {
+ expect(function() {
+ _this.bindAsObject(invalidFirebaseRef, 'items');
+ }).to.throw('ReactFire: firebaseRef must be an instance of Firebase');
+ });
+ },
+
+ render: function() {
+ return React.DOM.div(null);
+ }
+ });
+
+ shallowRenderer.render(React.createElement(TestComponent));
+ });
+
+ it('throws error given invalid bind variable', function() {
+ var TestComponent = React.createClass({
+ mixins: [ReactFireMixin],
+
+ componentWillMount: function() {
+ var _this = this;
+
+ TH.invalidBindVars.forEach(function(invalidBindVar) {
+ expect(function() {
+ _this.bindAsObject(firebaseRef, invalidBindVar);
+ }).to.throw(/bindVar/);
+ });
+ },
+
+ render: function() {
+ return React.DOM.div(null);
+ }
+ });
+
+ shallowRenderer.render(React.createElement(TestComponent));
+ });
+
+ it('binds objects', function(done) {
+ var TestComponent = React.createClass({
+ mixins: [ReactFireMixin],
+
+ componentWillMount: function() {
+ this.bindAsObject(firebaseRef, 'items');
+
+ var obj = {
+ first: { index: 0 },
+ second: { index: 1 },
+ third: { index: 2 }
+ };
+
+ firebaseRef.set(obj, function() {
+ expect(this.state.items).to.deep.equal(obj);
+
+ done();
+ }.bind(this));
+ },
+
+ render: function() {
+ return React.DOM.div(null);
+ }
+ });
+
+ shallowRenderer.render(React.createElement(TestComponent));
+ });
+
+ it('binds with limit queries', function(done) {
+ var TestComponent = React.createClass({
+ mixins: [ReactFireMixin],
+
+ componentWillMount: function() {
+ this.bindAsObject(firebaseRef.limitToLast(2), 'items');
+
+ firebaseRef.set({
+ first: { index: 0 },
+ second: { index: 1 },
+ third: { index: 2 }
+ }, function() {
+ expect(this.state.items).to.deep.equal({
+ second: { index: 1 },
+ third: { index: 2 }
+ });
+
+ done();
+ }.bind(this));
+ },
+
+ render: function() {
+ return React.DOM.div(null);
+ }
+ });
+
+ shallowRenderer.render(React.createElement(TestComponent));
+ });
+ });
+
+
+ describe('unbind()', function() {
+ it('throws error given invalid bind variable', function() {
+ var TestComponent = React.createClass({
+ mixins: [ReactFireMixin],
+
+ componentWillMount: function() {
+ var _this = this;
+
+ TH.invalidBindVars.forEach(function(invalidBindVar) {
+ expect(function() {
+ _this.unbind(invalidBindVar);
+ }).to.throw(/bindVar/);
+ });
+ },
+
+ render: function() {
+ return React.DOM.div(null);
+ }
+ });
+
+ shallowRenderer.render(React.createElement(TestComponent));
+ });
+
+ it('throws error given unbound bind variable', function() {
+ var TestComponent = React.createClass({
+ mixins: [ReactFireMixin],
+
+ componentWillMount: function() {
+ var _this = this;
+
+ expect(function() {
+ _this.unbind('items');
+ }).to.throw(/bindVar/);
+ },
+
+ render: function() {
+ return React.DOM.div(null);
+ }
+ });
+
+ shallowRenderer.render(React.createElement(TestComponent));
+ });
+
+ it('unbinds the state bound to Firebase as an array', function(done) {
+ var TestComponent = React.createClass({
+ mixins: [ReactFireMixin],
+
+ componentWillMount: function() {
+ this.bindAsArray(firebaseRef, 'items');
+
+ firebaseRef.set({
+ first: { index: 0 },
+ second: { index: 1 },
+ third: { index: 2 }
+ }, function() {
+ this.unbind('items', function() {
+ expect(this.state.items).to.be.undefined;
+ done();
+ });
+ }.bind(this));
+ },
+
+ render: function() {
+ return React.DOM.div(null);
+ }
+ });
+
+ shallowRenderer.render(React.createElement(TestComponent));
+ });
+
+ it('unbinds the state bound to Firebase as an object', function(done) {
+ var TestComponent = React.createClass({
+ mixins: [ReactFireMixin],
+
+ componentWillMount: function() {
+ this.bindAsObject(firebaseRef, 'items');
+
+ firebaseRef.set({
+ first: { index: 0 },
+ second: { index: 1 },
+ third: { index: 2 }
+ }, function() {
+ this.unbind('items', function() {
+ expect(this.state.items).to.be.undefined;
+ done();
+ });
+ }.bind(this));
+ },
+
+ render: function() {
+ return React.DOM.div(null);
+ }
+ });
+
+ shallowRenderer.render(React.createElement(TestComponent));
+ });
+
+ it('unbinds the state bound to Firebase limit query', function(done) {
+ var TestComponent = React.createClass({
+ mixins: [ReactFireMixin],
+
+ componentWillMount: function() {
+ this.bindAsObject(firebaseRef.limitToLast(2), 'items');
+
+ firebaseRef.set({
+ first: { index: 0 },
+ second: { index: 1 },
+ third: { index: 2 }
+ }, function() {
+ this.unbind('items', function() {
+ expect(this.state.items).to.be.undefined;
+ done();
+ });
+ }.bind(this));
+ },
+
+ render: function() {
+ return React.DOM.div(null);
+ }
+ });
+
+ shallowRenderer.render(React.createElement(TestComponent));
+ });
+ });
+});
diff --git a/tests/specs/common.spec.js b/tests/specs/common.spec.js
deleted file mode 100644
index 9c20d61..0000000
--- a/tests/specs/common.spec.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/*************/
-/* GLOBALS */
-/*************/
-// Override the default timeout interval for Jasmine
-jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
-
-// Get a reference to a random demo Firebase
-var demoFirebaseUrl = "https://" + generateRandomString() + ".firebaseio-demo.com";
-
-// React test addon
-var ReactTestUtils = React.addons.TestUtils;
-
-// Define examples of valid and invalid parameters
-var invalidFirebaseRefs = [null, undefined, true, false, [], 0, 5, "", "a", ["hi", 1]];
-var validBindVars = ["a", "testing", "(e@Xi:4t>*E2)hc<5oa:1s6{B0d?u", Array(743).join("a")];
-var invalidBindVars = ["", 1, true, false, [], {}, [1, 2], {a: 1}, null, undefined, "te.st", "te$st", "te[st", "te]st", "te#st", "te/st", "a#i]$da[s", "te/nst", "te/rst", "te/u0000st", "te/u0015st", "te/007Fst", Array(800).join("a")];
-
-/**********************/
-/* HELPER FUNCTIONS */
-/**********************/
-/* Helper function which runs before each Jasmine test has started */
-function beforeEachHelper(done) {
- // Create a new firebase ref with a new context
- firebaseRef = new Firebase(demoFirebaseUrl, Firebase.Context());
-
- // Reset the Firebase
- firebaseRef.remove(function() {
- // Create a new firebase ref at a random node
- firebaseRef = firebaseRef.child(generateRandomString());
-
- done();
- });
-}
-
-/* Helper function which runs after each Jasmine test has completed */
-function afterEachHelper(done) {
- React.unmountComponentAtNode(document.body);
- done();
-}
-
-/* Returns a random alphabetic string of variable length */
-function generateRandomString() {
- var possibleCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
- var numPossibleCharacters = possibleCharacters.length;
-
- var text = "";
- for (var i = 0; i < 10; i++) {
- text += possibleCharacters.charAt(Math.floor(Math.random() * numPossibleCharacters));
- }
-
- return text;
-}
-
-/* Returns the current data in the Firebase */
-function getFirebaseData() {
- return new RSVP.Promise(function(resolve, reject) {
- firebaseRef.once("value", function(dataSnapshot) {
- resolve(dataSnapshot.val());
- });
- });
-};
diff --git a/tests/specs/reactfire.spec.js b/tests/specs/reactfire.spec.js
deleted file mode 100644
index 0ceca4c..0000000
--- a/tests/specs/reactfire.spec.js
+++ /dev/null
@@ -1,568 +0,0 @@
-describe("ReactFireMixin Tests:", function() {
- beforeEach(function(done) {
- beforeEachHelper(done);
- });
-
- afterEach(function(done) {
- afterEachHelper(done);
- });
-
- describe("bindAsArray():", function() {
- it("bindAsArray() throws errors given invalid Firebase refs", function() {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- var _this = this;
-
- invalidFirebaseRefs.forEach(function(invalidFirebaseRef) {
- try {
- _this.bindAsArray(invalidFirebaseRef, "items");
- expect("Function should throw error given parameter: " + invalidFirebaseRef).toBeFalsy();
- } catch (error) {
- expect(error.code).toEqual("INVALID_FIREBASE_REF");
- }
- });
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
-
- it("bindAsArray() throws errors given invalid bind variables", function() {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- var _this = this;
-
- invalidBindVars.forEach(function(invalidBindVar) {
- try {
- _this.bindAsArray(firebaseRef, invalidBindVar);
- expect("Function should throw error given parameter: " + invalidBindVar).toBeFalsy();
- } catch (error) {
- expect(error.code).toEqual("INVALID_BIND_VARIABLE");
- }
- });
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
-
- it("bindAsArray() does not throw errors given valid inputs", function() {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- var _this = this;
-
- validBindVars.forEach(function(validBindVar) {
- expect(function() { _this.bindAsArray(firebaseRef, validBindVar); }).not.toThrow();
- });
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
-
- it("bindAsArray() does not throw an error given a limit query", function() {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- var _this = this;
-
- expect(function() { _this.bindAsArray(firebaseRef.limitToLast(10), "items"); }).not.toThrow();
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
-
- it("bindAsArray() binds to remote Firebase data as an array", function(done) {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- this.bindAsArray(firebaseRef, "items");
- },
-
- componentDidMount: function() {
- firebaseRef.set({ a: 1, b: 2, c: 3 });
- },
-
- componentDidUpdate: function(prevProps, prevState) {
- expect(this.state).toEqual({
- items: [
- { $key: "a", $value: 1 },
- { $key: "b", $value: 2 },
- { $key: "c", $value: 3 }
- ]
- });
- done();
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
-
- it("bindAsArray() binds to remote Firebase data as an array (limit query)", function(done) {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- this.bindAsArray(firebaseRef.limitToLast(2), "items");
- },
-
- componentDidMount: function() {
- firebaseRef.set({ a: 1, b: 2, c: 3 });
- },
-
- componentDidUpdate: function(prevProps, prevState) {
- expect(this.state).toEqual({
- items: [
- { $key: "b", $value: 2 },
- { $key: "c", $value: 3 }
- ]
- });
- done();
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
-
- it("bindAsArray() makes $key available on array items when they are objects", function(done) {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
- componentWillMount: function() {
- this.bindAsArray(firebaseRef, "items");
- },
- componentDidMount: function() {
- firebaseRef.set({ first: { index: 1 }, second: { index: 2 }, third: { index: 3 } });
- },
- componentDidUpdate: function(prevProps, prevState) {
- expect(this.state.items.map(function(item) { return item.$key; })).toEqual(["first", "second", "third"]);
- done();
- },
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
- React.render(new TestComponent(), document.body);
- });
-
- it("bindAsArray() makes $key available on array items when they are primitives", function(done) {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
- componentWillMount: function() {
- this.bindAsArray(firebaseRef, "items");
- },
- componentDidMount: function() {
- firebaseRef.set(["first", "second", "third"]);
- },
- componentDidUpdate: function(prevProps, prevState) {
- expect(this.state.items.map(function(item) { return item.$key; })).toEqual([0, 1, 2]);
- expect(this.state.items.map(function(item) { return item.$value; })).toEqual(["first", "second", "third"]);
- done();
- },
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
- React.render(new TestComponent(), document.body);
- });
-
- it("bindAsArray() properly binds to sparse arrays", function(done) {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- this.bindAsArray(firebaseRef, "items");
- },
-
- componentDidMount: function() {
- firebaseRef.set({ 0: 'a', 2: 'b', 5: 'c' });
- },
-
- componentDidUpdate: function(prevProps, prevState) {
- expect(this.state).toEqual({
- items: [
- { $key: 0, $value: 'a' },
- { $key: 2, $value: 'b' },
- { $key: 5, $value: 'c' }
- ]
- });
- done();
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
- });
-
- describe("bindAsObject():", function() {
- it("bindAsObject() throws errors given invalid Firebase refs", function() {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- var _this = this;
-
- invalidFirebaseRefs.forEach(function(invalidFirebaseRef) {
- try {
- _this.bindAsObject(invalidFirebaseRef, "items");
- expect("Function should throw error given parameter: " + invalidFirebaseRef).toBeFalsy();
- } catch (error) {
- expect(error.code).toEqual("INVALID_FIREBASE_REF");
- }
- });
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
-
- it("bindAsObject() throws errors given invalid bind variables", function() {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- var _this = this;
-
- invalidBindVars.forEach(function(invalidBindVar) {
- try {
- _this.bindAsObject(firebaseRef, invalidBindVar);
- expect("Function should throw error given parameter: " + invalidBindVar).toBeFalsy();
- } catch (error) {
- expect(error.code).toEqual("INVALID_BIND_VARIABLE");
- }
- });
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
-
- it("bindAsObject() does not throw errors given valid inputs", function() {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- var _this = this;
-
- validBindVars.forEach(function(validBindVar) {
- expect(function() { _this.bindAsObject(firebaseRef, validBindVar); }).not.toThrow();
- });
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
-
- it("bindAsObject() does not throw an error given a limit query", function() {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- var _this = this;
-
- expect(function() { _this.bindAsObject(firebaseRef.limitToLast(10), "items"); }).not.toThrow();
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
-
- it("bindAsObject() binds to remote Firebase data as an object", function(done) {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- this.bindAsObject(firebaseRef, "items");
- },
-
- componentDidMount: function() {
- firebaseRef.set({ a: 1, b: 2, c: 3 });
- },
-
- componentDidUpdate: function(prevProps, prevState) {
- expect(this.state).toEqual({ items: { a: 1, b: 2, c: 3 } });
- done();
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
-
- it("bindAsObject() binds to remote Firebase data as an object (limit query)", function(done) {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- this.bindAsObject(firebaseRef.limitToLast(2), "items");
- },
-
- componentDidMount: function() {
- firebaseRef.set({ a: 1, b: 2, c: 3 });
- },
-
- componentDidUpdate: function(prevProps, prevState) {
- expect(this.state).toEqual({ items: { b: 2, c: 3 } });
- done();
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
- });
-
- describe("unbind():", function() {
- it("unbind() throws errors given invalid bind variables", function() {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- var _this = this;
-
- invalidBindVars.forEach(function(invalidBindVar) {
- try {
- _this.unbind(invalidBindVar);
- expect("Function should throw error given parameter: " + invalidBindVar).toBeFalsy();
- } catch (error) {
- expect(error.code).toEqual("INVALID_BIND_VARIABLE");
- }
- });
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
-
- it("unbind() throws errors given unbound bind variable", function() {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- var _this = this;
-
- validBindVars.forEach(function(validBindVar) {
- try {
- _this.unbind(validBindVar);
- expect("Function should throw error given parameter: " + validBindVar).toBeFalsy();
- } catch (error) {
- expect(error.code).toEqual("UNBOUND_BIND_VARIABLE");
- }
- });
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
-
- it("unbind() does not throw errors given valid bind variables", function() {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- var _this = this;
-
- validBindVars.forEach(function(validBindVar) {
- _this.bindAsArray(firebaseRef, validBindVar);
- expect(function() { _this.unbind(validBindVar); }).not.toThrow();
- });
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
-
- it("unbind() does not throw an error given a limit query", function() {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- var _this = this;
-
- validBindVars.forEach(function(validBindVar) {
- _this.bindAsArray(firebaseRef.limitToLast(10), validBindVar);
- expect(function() { _this.unbind(validBindVar); }).not.toThrow();
- });
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
-
- it("unbind() unbinds the state bound to Firebase as an array", function(done) {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- this.bindAsArray(firebaseRef, "items");
- this.unbind("items");
- },
-
- componentDidMount: function() {
- firebaseRef.set({ a: 1, b: 2, c: 3 }, function() {
- this.setTimeout(done, 250);
- });
- },
-
- componentDidUpdate: function(prevProps, prevState) {
- expect("Should not be here").toBeFalsy();
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
-
- it("unbind() unbinds the state bound to Firebase as an object", function(done) {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- this.bindAsObject(firebaseRef, "items");
- this.unbind("items");
- },
-
- componentDidMount: function() {
- firebaseRef.set({ a: 1, b: 2, c: 3 }, function() {
- this.setTimeout(done, 250);
- });
- },
-
- componentDidUpdate: function(prevProps, prevState) {
- expect("Should not be here").toBeFalsy();
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
- });
-
- describe("_bind():", function() {
- it("_bind() throws errors given invalid third input parameter", function() {
- var nonBooleanParams = [null, undefined, [], {}, 0, 5, "", "a", {a : 1}, ["hi", 1]];
-
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- var _this = this;
-
- nonBooleanParams.forEach(function(nonBooleanParam) {
- try {
- _this._bind(firebaseRef, "items", nonBooleanParam);
- expect("Function should throw error given parameter: " + nonBooleanParam).toBeFalsy();
- } catch (error) {
- expect(error.code).toEqual("INVALID_BIND_AS_ARRAY");
- }
- });
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
-
- it("_bind() does not throw error given valid inputs", function() {
- var TestComponent = React.createClass({
- mixins: [ReactFireMixin],
-
- componentWillMount: function() {
- var _this = this;
-
- expect(function() { _this._bind(firebaseRef, "items", function() {}, true); }).not.toThrow();
- expect(function() { _this._bind(firebaseRef, "items", function() {}, false); }).not.toThrow();
- },
-
- render: function() {
- return React.DOM.div(null, "Testing");
- }
- });
-
- React.render(new TestComponent(), document.body);
- });
- });
-});