diff --git a/README.md b/README.md
index 09708afb6..b24234ded 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,16 @@ There are 4 available container profiles:
reuse this instance to run the tests.
Useful for development to avoid the server start up cost per sample.
+Each of the containers allow you to override the version used
+
+* `-Dorg.wildfly=8.1.0.Final`
+
+ This will change the version from 8.0.0 to 8.1.0.Final for WildFly.
+
+* `-Dglassfish.version=4.1`
+
+ This will change the version from 4.0 to 4.1 for GlassFish testing purposes.
+
Similarly, there are 6 profiles to choose a browser to test on:
* ``browser-firefox``
@@ -99,5 +109,5 @@ That's it! Welcome in the community!
## CI Job ##
-* [WildFly](https://arungupta.ci.cloudbees.com/job/javaee7-samples-on-wildfly-cb/)
-* [GlassFish](https://arungupta.ci.cloudbees.com/job/Java%20EE%207%20Samples%20on%20GlassFish-cb/)
+* [WildFly](https://javaee-support.ci.cloudbees.com/job/javaee7-pull-request-builder/)
+* [GlassFish](https://javaee-support.ci.cloudbees.com/job/javaee7-pull-request-builder-glassfish/)
diff --git a/batch/pom.xml b/batch/pom.xml
index bee6ff9d2..d8442991f 100644
--- a/batch/pom.xml
+++ b/batch/pom.xml
@@ -31,4 +31,11 @@
chunk-simple-nobeans
scheduling
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/cdi/alternatives-priority/src/test/resources/arquillian.xml b/cdi/alternatives-priority/src/test/resources/arquillian.xml
deleted file mode 100644
index 7ff92fbce..000000000
--- a/cdi/alternatives-priority/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/cdi/alternatives/src/test/resources/arquillian.xml b/cdi/alternatives/src/test/resources/arquillian.xml
deleted file mode 100644
index de1737b5c..000000000
--- a/cdi/alternatives/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/cdi/bean-discovery-all/src/test/resources/arquillian.xml b/cdi/bean-discovery-all/src/test/resources/arquillian.xml
deleted file mode 100644
index de1737b5c..000000000
--- a/cdi/bean-discovery-all/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/cdi/bean-discovery-annotated/src/test/resources/arquillian.xml b/cdi/bean-discovery-annotated/src/test/resources/arquillian.xml
deleted file mode 100644
index de1737b5c..000000000
--- a/cdi/bean-discovery-annotated/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/cdi/bean-discovery-none/src/test/resources/arquillian.xml b/cdi/bean-discovery-none/src/test/resources/arquillian.xml
deleted file mode 100644
index de1737b5c..000000000
--- a/cdi/bean-discovery-none/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/cdi/beansxml-noversion/src/test/resources/arquillian.xml b/cdi/beansxml-noversion/src/test/resources/arquillian.xml
deleted file mode 100644
index de1737b5c..000000000
--- a/cdi/beansxml-noversion/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/cdi/events/src/test/resources/arquillian.xml b/cdi/events/src/test/resources/arquillian.xml
deleted file mode 100644
index 3be0ed583..000000000
--- a/cdi/events/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/cdi/interceptors-priority/src/test/resources/arquillian.xml b/cdi/interceptors-priority/src/test/resources/arquillian.xml
deleted file mode 100644
index 7ff92fbce..000000000
--- a/cdi/interceptors-priority/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/cdi/interceptors/src/test/resources/arquillian.xml b/cdi/interceptors/src/test/resources/arquillian.xml
deleted file mode 100644
index 7ff92fbce..000000000
--- a/cdi/interceptors/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/cdi/nobeans-xml/src/test/resources/arquillian.xml b/cdi/nobeans-xml/src/test/resources/arquillian.xml
deleted file mode 100644
index de1737b5c..000000000
--- a/cdi/nobeans-xml/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/cdi/pom.xml b/cdi/pom.xml
index b6ee7feee..6246be0d6 100644
--- a/cdi/pom.xml
+++ b/cdi/pom.xml
@@ -36,4 +36,11 @@
nobeans-el-injection-flowscoped
events
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/concurrency/managedexecutor/src/test/resources/arquillian.xml b/concurrency/managedexecutor/src/test/resources/arquillian.xml
deleted file mode 100644
index de1737b5c..000000000
--- a/concurrency/managedexecutor/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/concurrency/pom.xml b/concurrency/pom.xml
index 002160a70..10a9802cd 100644
--- a/concurrency/pom.xml
+++ b/concurrency/pom.xml
@@ -21,5 +21,11 @@
managedthreadfactory
dynamicproxy
-
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/ejb/pom.xml b/ejb/pom.xml
index 7e3756d29..7000ccbbf 100644
--- a/ejb/pom.xml
+++ b/ejb/pom.xml
@@ -23,4 +23,11 @@
timer
async-ejb
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/el/pom.xml b/el/pom.xml
index 8ec44494e..4093f8eb9 100644
--- a/el/pom.xml
+++ b/el/pom.xml
@@ -17,4 +17,11 @@
standalone
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/el/standalone/src/test/resources/arquillian.xml b/el/standalone/src/test/resources/arquillian.xml
deleted file mode 100644
index de1737b5c..000000000
--- a/el/standalone/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/extra/nosql/hibernate-ogm/src/test/resources/arquillian.xml b/extra/nosql/hibernate-ogm/src/test/resources/arquillian.xml
deleted file mode 100644
index de1737b5c..000000000
--- a/extra/nosql/hibernate-ogm/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/extra/pom.xml b/extra/pom.xml
index 4f34511ca..32ba89c1b 100644
--- a/extra/pom.xml
+++ b/extra/pom.xml
@@ -21,4 +21,11 @@
-->
nosql
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/interceptor/around-construct/src/test/resources/arquillian.xml b/interceptor/around-construct/src/test/resources/arquillian.xml
deleted file mode 100644
index 2650e410b..000000000
--- a/interceptor/around-construct/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/interceptor/pom.xml b/interceptor/pom.xml
index 10baeedf2..9cbc7e15d 100644
--- a/interceptor/pom.xml
+++ b/interceptor/pom.xml
@@ -17,4 +17,11 @@
around-construct
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/jacc/pom.xml b/jacc/pom.xml
index eb78e053a..3794211f6 100644
--- a/jacc/pom.xml
+++ b/jacc/pom.xml
@@ -19,5 +19,11 @@
contexts
-
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/jaspic/pom.xml b/jaspic/pom.xml
index f57a325eb..b11bc3ea3 100644
--- a/jaspic/pom.xml
+++ b/jaspic/pom.xml
@@ -42,5 +42,11 @@
this indeed happens. -->
wrapping
-
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/javamail/pom.xml b/javamail/pom.xml
index 42b4750fb..ff3eb4544 100644
--- a/javamail/pom.xml
+++ b/javamail/pom.xml
@@ -17,5 +17,11 @@
definition
-
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/jaxrs/angularjs/src/test/resources/arquillian.xml b/jaxrs/angularjs/src/test/resources/arquillian.xml
deleted file mode 100644
index 50b792441..000000000
--- a/jaxrs/angularjs/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
- ALTER SEQUENCE hibernate_sequence RESTART WITH 1000;
-
-
-
- phantomjs
-
-
-
diff --git a/jaxrs/jaxrs-endpoint/src/test/resources/arquillian.xml b/jaxrs/jaxrs-endpoint/src/test/resources/arquillian.xml
deleted file mode 100644
index de1737b5c..000000000
--- a/jaxrs/jaxrs-endpoint/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index 0651f3411..e84869882 100644
--- a/jaxrs/pom.xml
+++ b/jaxrs/pom.xml
@@ -46,4 +46,11 @@
jaxrs-security-declarative
db-access
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/jaxws/pom.xml b/jaxws/pom.xml
index 8adfb0553..a07067356 100644
--- a/jaxws/pom.xml
+++ b/jaxws/pom.xml
@@ -18,4 +18,11 @@
jaxws-endpoint
jaxws-client
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/jca/pom.xml b/jca/pom.xml
index 31c1fd6f5..6be5bc47c 100644
--- a/jca/pom.xml
+++ b/jca/pom.xml
@@ -17,4 +17,11 @@
connector-simple
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/jms/jms-batch/src/test/resources/arquillian.xml b/jms/jms-batch/src/test/resources/arquillian.xml
deleted file mode 100644
index de1737b5c..000000000
--- a/jms/jms-batch/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/jms/pom.xml b/jms/pom.xml
index d7a055809..fe7bce5be 100644
--- a/jms/pom.xml
+++ b/jms/pom.xml
@@ -21,5 +21,11 @@
temp-destination
jms-batch
-
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/jms/send-receive/src/test/resources/arquillian.xml b/jms/send-receive/src/test/resources/arquillian.xml
deleted file mode 100644
index de1737b5c..000000000
--- a/jms/send-receive/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/jms/temp-destination/src/test/resources/arquillian.xml b/jms/temp-destination/src/test/resources/arquillian.xml
deleted file mode 100644
index 76ede8688..000000000
--- a/jms/temp-destination/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
- -Xrunjdwp:transport=dt_socket,address=55407,server=y,suspend=y -Xmx512m -XX:MaxPermSize=128m
-
-
-
-
diff --git a/jpa/datasourcedefinition/src/test/resources/arquillian.xml b/jpa/datasourcedefinition/src/test/resources/arquillian.xml
deleted file mode 100644
index de1737b5c..000000000
--- a/jpa/datasourcedefinition/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/jpa/jpa-converter/src/test/resources-glassfish-embedded/arquillian.xml b/jpa/jpa-converter/src/test/resources-glassfish-embedded/arquillian.xml
deleted file mode 100644
index cd534e61c..000000000
--- a/jpa/jpa-converter/src/test/resources-glassfish-embedded/arquillian.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
- target/deployment
-
-
-
-
- src/test/resources-glassfish-embedded/glassfish-resources.xml
- 9999
-
-
-
-
- true
-
-
-
- NEW_LINE
- reset-sequence.sql
-
-
-
- SEQUENCE
- SEQUENCE
-
-
-
diff --git a/jpa/jpa-converter/src/test/resources-glassfish-remote/arquillian.xml b/jpa/jpa-converter/src/test/resources-glassfish-remote/arquillian.xml
deleted file mode 100644
index cf747bf6f..000000000
--- a/jpa/jpa-converter/src/test/resources-glassfish-remote/arquillian.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
- target/deployment
-
-
-
- true
-
-
-
- NEW_LINE
- reset-sequence.sql
-
-
-
- SEQUENCE
- SEQUENCE
-
-
-
diff --git a/jpa/jpa-converter/src/test/resources-jbosseap-remote/arquillian.xml b/jpa/jpa-converter/src/test/resources-jbosseap-remote/arquillian.xml
deleted file mode 100644
index eb05674d8..000000000
--- a/jpa/jpa-converter/src/test/resources-jbosseap-remote/arquillian.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
- java:jboss/UserTransaction
-
-
-
- SET REFERENTIAL_INTEGRITY FALSE;
- SET REFERENTIAL_INTEGRITY FALSE;
- NEW_LINE
-
-
-
- org.dbunit.ext.h2.H2DataTypeFactory
-
-
-
diff --git a/jpa/jpa-converter/src/test/resources-wildfly-managed/arquillian.xml b/jpa/jpa-converter/src/test/resources-wildfly-managed/arquillian.xml
deleted file mode 100644
index ae16ba4f8..000000000
--- a/jpa/jpa-converter/src/test/resources-wildfly-managed/arquillian.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
- java:jboss/UserTransaction
-
-
-
- SET REFERENTIAL_INTEGRITY FALSE;
- SET REFERENTIAL_INTEGRITY FALSE;
- NEW_LINE
-
-
-
- org.dbunit.ext.h2.H2DataTypeFactory
-
-
-
diff --git a/jpa/jpa-converter/src/test/resources-wildfly-remote/arquillian.xml b/jpa/jpa-converter/src/test/resources-wildfly-remote/arquillian.xml
deleted file mode 100644
index ae16ba4f8..000000000
--- a/jpa/jpa-converter/src/test/resources-wildfly-remote/arquillian.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
- java:jboss/UserTransaction
-
-
-
- SET REFERENTIAL_INTEGRITY FALSE;
- SET REFERENTIAL_INTEGRITY FALSE;
- NEW_LINE
-
-
-
- org.dbunit.ext.h2.H2DataTypeFactory
-
-
-
diff --git a/jpa/pom.xml b/jpa/pom.xml
index b5f48b112..e42fa65d9 100644
--- a/jpa/pom.xml
+++ b/jpa/pom.xml
@@ -42,5 +42,11 @@
jpa-converter
default-datasource
-
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/jsf/pom.xml b/jsf/pom.xml
index 3b9e810a8..ebef2e9a1 100644
--- a/jsf/pom.xml
+++ b/jsf/pom.xml
@@ -44,6 +44,11 @@
pom
test
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
diff --git a/jsf/simple-facelet/src/test/resources/arquillian.xml b/jsf/simple-facelet/src/test/resources/arquillian.xml
deleted file mode 100644
index 933a31063..000000000
--- a/jsf/simple-facelet/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
- target/
-
-
-
- ${browser}
-
-
-
diff --git a/json/object-builder/src/test/resources/arquillian.xml b/json/object-builder/src/test/resources/arquillian.xml
deleted file mode 100644
index de1737b5c..000000000
--- a/json/object-builder/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/json/pom.xml b/json/pom.xml
index f26dceda3..3b89b8463 100644
--- a/json/pom.xml
+++ b/json/pom.xml
@@ -20,5 +20,11 @@
streaming-generate
streaming-parser
-
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/jta/pom.xml b/jta/pom.xml
index 710f01ecd..828008f30 100644
--- a/jta/pom.xml
+++ b/jta/pom.xml
@@ -19,4 +19,11 @@
tx-exception
user-transaction
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/jta/transactional/src/test/resources/arquillian.xml b/jta/transactional/src/test/resources/arquillian.xml
deleted file mode 100644
index de1737b5c..000000000
--- a/jta/transactional/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/jta/tx-exception/src/test/resources/arquillian.xml b/jta/tx-exception/src/test/resources/arquillian.xml
deleted file mode 100644
index de1737b5c..000000000
--- a/jta/tx-exception/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/jta/user-transaction/src/test/resources/arquillian.xml b/jta/user-transaction/src/test/resources/arquillian.xml
deleted file mode 100644
index de1737b5c..000000000
--- a/jta/user-transaction/src/test/resources/arquillian.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- ${serverRoot:target/wildfly-8.0.0.Final}
- ${serverProfile:standalone-full.xml}
-
-
-
-
diff --git a/pom.xml b/pom.xml
index fe66b4d10..ca0af4a7b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -25,6 +25,7 @@
1.3
0.7-groovy-2.0
2.1.5
+ 4.0
@@ -32,6 +33,7 @@
+ test-utils
batch
cdi
concurrency
@@ -665,7 +667,7 @@
org.glassfish.main.extras
glassfish-embedded-all
- 4.0
+ ${glassfish.version}
test
diff --git a/servlet/pom.xml b/servlet/pom.xml
index b45ab6979..b2115481b 100644
--- a/servlet/pom.xml
+++ b/servlet/pom.xml
@@ -35,4 +35,11 @@
security-basicauth-omission
programmatic-registration
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/test-utils/pom.xml b/test-utils/pom.xml
new file mode 100644
index 000000000..37dc36c09
--- /dev/null
+++ b/test-utils/pom.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ javaee7-samples
+ org.javaee7
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ test-utils
+
+
+
\ No newline at end of file
diff --git a/jms/jms-xa/src/test/resources/arquillian.xml b/test-utils/src/main/resources/arquillian.xml
similarity index 80%
rename from jms/jms-xa/src/test/resources/arquillian.xml
rename to test-utils/src/main/resources/arquillian.xml
index 1a72e7a1f..caecf3bab 100644
--- a/jms/jms-xa/src/test/resources/arquillian.xml
+++ b/test-utils/src/main/resources/arquillian.xml
@@ -1,12 +1,13 @@
+
-
- target/deployment
-
+
+
+
diff --git a/validation/pom.xml b/validation/pom.xml
index b60a89801..daf300064 100644
--- a/validation/pom.xml
+++ b/validation/pom.xml
@@ -19,5 +19,12 @@
methods
custom-constraint
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+
diff --git a/websocket/atmosphere-chat/overlays/org.atmosphere.client.javascript-2.0.7.info b/websocket/atmosphere-chat/overlays/org.atmosphere.client.javascript-2.0.7.info
new file mode 100644
index 000000000..8302559f6
--- /dev/null
+++ b/websocket/atmosphere-chat/overlays/org.atmosphere.client.javascript-2.0.7.info
@@ -0,0 +1,3 @@
+1412104997000
+(?:[^/]+/)*?[^/]*?
+META-INF(?:$|/.+)
\ No newline at end of file
diff --git a/websocket/atmosphere-chat/overlays/org.atmosphere.client.javascript-2.0.7/WEB-INF/web.xml b/websocket/atmosphere-chat/overlays/org.atmosphere.client.javascript-2.0.7/WEB-INF/web.xml
new file mode 100644
index 000000000..c8f1294b9
--- /dev/null
+++ b/websocket/atmosphere-chat/overlays/org.atmosphere.client.javascript-2.0.7/WEB-INF/web.xml
@@ -0,0 +1 @@
+DUMMY web.xml
diff --git a/websocket/atmosphere-chat/overlays/org.atmosphere.client.javascript-2.0.7/javascript/atmosphere-min.js b/websocket/atmosphere-chat/overlays/org.atmosphere.client.javascript-2.0.7/javascript/atmosphere-min.js
new file mode 100644
index 000000000..4d175fc7c
--- /dev/null
+++ b/websocket/atmosphere-chat/overlays/org.atmosphere.client.javascript-2.0.7/javascript/atmosphere-min.js
@@ -0,0 +1,1012 @@
+(function(){var c="2.0.7-javascript",a={},d,g=[],f=[],e=0,b=Object.prototype.hasOwnProperty;
+a={onError:function(h){},onClose:function(h){},onOpen:function(h){},onReopen:function(h){},onMessage:function(h){},onReconnect:function(i,h){},onMessagePublished:function(h){},onTransportFailure:function(i,h){},onLocalMessage:function(h){},onFailureToReconnect:function(i,h){},onClientTimeout:function(h){},AtmosphereRequest:function(M){var O={timeout:300000,method:"GET",headers:{},contentType:"",callback:null,url:"",data:"",suspend:true,maxRequest:-1,reconnect:true,maxStreamingLength:10000000,lastIndex:0,logLevel:"info",requestCount:0,fallbackMethod:"GET",fallbackTransport:"streaming",transport:"long-polling",webSocketImpl:null,webSocketBinaryType:null,dispatchUrl:null,webSocketPathDelimiter:"@@",enableXDR:false,rewriteURL:false,attachHeadersAsQueryString:true,executeCallbackBeforeReconnect:false,readyState:0,lastTimestamp:0,withCredentials:false,trackMessageLength:false,messageDelimiter:"|",connectTimeout:-1,reconnectInterval:0,dropAtmosphereHeaders:true,uuid:0,async:true,shared:false,readResponsesHeaders:false,maxReconnectOnClose:5,enableProtocol:true,onError:function(aA){},onClose:function(aA){},onOpen:function(aA){},onMessage:function(aA){},onReopen:function(aB,aA){},onReconnect:function(aB,aA){},onMessagePublished:function(aA){},onTransportFailure:function(aB,aA){},onLocalMessage:function(aA){},onFailureToReconnect:function(aB,aA){},onClientTimeout:function(aA){}};
+var W={status:200,reasonPhrase:"OK",responseBody:"",messages:[],headers:[],state:"messageReceived",transport:"polling",error:null,request:null,partialMessage:"",errorHandled:false,closedByClientTimeout:false};
+var Z=null;
+var o=null;
+var v=null;
+var E=null;
+var G=null;
+var ak=true;
+var l=0;
+var aw=false;
+var aa=null;
+var aq;
+var q=null;
+var J=a.util.now();
+var K;
+var az;
+ay(M);
+function ar(){ak=true;
+aw=false;
+l=0;
+Z=null;
+o=null;
+v=null;
+E=null
+}function A(){am();
+ar()
+}function L(aB,aA){if(W.partialMessage===""&&(aA.transport==="streaming")&&(aB.responseText.length>aA.maxStreamingLength)){W.messages=[];
+ai(true);
+D();
+am();
+R(aB,aA,0)
+}}function D(){if(O.enableProtocol&&!O.firstMessage){var aC="X-Atmosphere-Transport=close&X-Atmosphere-tracking-id="+O.uuid;
+a.util.each(O.headers,function(aE,aG){var aF=a.util.isFunction(aG)?aG.call(this,O,O,W):aG;
+if(aF!=null){aC+="&"+encodeURIComponent(aE)+"="+encodeURIComponent(aF)
+}});
+var aA=O.url.replace(/([?&])_=[^&]*/,aC);
+aA=aA+(aA===O.url?(/\?/.test(O.url)?"&":"?")+aC:"");
+var aB={connected:false,};
+var aD=new a.AtmosphereRequest(aB);
+aD.attachHeadersAsQueryString=false;
+aD.dropAtmosphereHeaders=true;
+aD.url=aA;
+aD.contentType="text/plain";
+aD.transport="polling";
+n("",aD)
+}}function an(){if(O.reconnectId){clearTimeout(O.reconnectId)
+}O.reconnect=false;
+aw=true;
+W.request=O;
+W.state="unsubscribe";
+W.responseBody="";
+W.status=408;
+W.partialMessage="";
+C();
+D();
+am()
+}function am(){W.partialMessage="";
+if(O.id){clearTimeout(O.id)
+}if(E!=null){E.close();
+E=null
+}if(G!=null){G.abort();
+G=null
+}if(v!=null){v.abort();
+v=null
+}if(Z!=null){if(Z.webSocketOpened){Z.close()
+}Z=null
+}if(o!=null){o.close();
+o=null
+}at()
+}function at(){if(aq!=null){clearInterval(K);
+document.cookie=az+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
+aq.signal("close",{reason:"",heir:!aw?J:(aq.get("children")||[])[0]});
+aq.close()
+}if(q!=null){q.close()
+}}function ay(aA){A();
+O=a.util.extend(O,aA);
+O.mrequest=O.reconnect;
+if(!O.reconnect){O.reconnect=true
+}}function p(){return O.webSocketImpl!=null||window.WebSocket||window.MozWebSocket
+}function S(){return window.EventSource
+}function t(){if(O.shared){q=ah(O);
+if(q!=null){if(O.logLevel==="debug"){a.util.debug("Storage service available. All communication will be local")
+}if(q.open(O)){return
+}}if(O.logLevel==="debug"){a.util.debug("No Storage service available.")
+}q=null
+}O.firstMessage=e==0?true:false;
+O.isOpen=false;
+O.ctime=a.util.now();
+O.uuid=e;
+W.closedByClientTimeout=false;
+if(O.transport!=="websocket"&&O.transport!=="sse"){s(O)
+}else{if(O.transport==="websocket"){if(!p()){Q("Websocket is not supported, using request.fallbackTransport ("+O.fallbackTransport+")")
+}else{aj(false)
+}}else{if(O.transport==="sse"){if(!S()){Q("Server Side Events(SSE) is not supported, using request.fallbackTransport ("+O.fallbackTransport+")")
+}else{I(false)
+}}}}}function ah(aE){var aF,aD,aI,aA="atmosphere-"+aE.url,aB={storage:function(){function aJ(aN){if(aN.key===aA&&aN.newValue){aC(aN.newValue)
+}}if(!a.util.storage){return
+}var aM=window.localStorage,aK=function(aN){return a.util.parseJSON(aM.getItem(aA+"-"+aN))
+},aL=function(aN,aO){aM.setItem(aA+"-"+aN,a.util.stringifyJSON(aO))
+};
+return{init:function(){aL("children",aK("children").concat([J]));
+a.util.on(window,"storage",aJ);
+return aK("opened")
+},signal:function(aN,aO){aM.setItem(aA,a.util.stringifyJSON({target:"p",type:aN,data:aO}))
+},close:function(){var aN=aK("children");
+a.util.off(window,"storage",aJ);
+if(aN){if(aG(aN,aE.id)){aL("children",aN)
+}}}}
+},windowref:function(){var aJ=window.open("",aA.replace(/\W/g,""));
+if(!aJ||aJ.closed||!aJ.callbacks){return
+}return{init:function(){aJ.callbacks.push(aC);
+aJ.children.push(J);
+return aJ.opened
+},signal:function(aK,aL){if(!aJ.closed&&aJ.fire){aJ.fire(a.util.stringifyJSON({target:"p",type:aK,data:aL}))
+}},close:function(){if(!aI){aG(aJ.callbacks,aC);
+aG(aJ.children,J)
+}}}
+}};
+function aG(aM,aL){var aJ,aK=aM.length;
+for(aJ=0;
+aJ1000){return
+}aD=aB.storage()||aB.windowref();
+if(!aD){return
+}return{open:function(){var aJ;
+K=setInterval(function(){var aK=aF;
+aF=aH();
+if(!aF||aK.ts===aF.ts){aC(a.util.stringifyJSON({target:"c",type:"close",data:{reason:"error",heir:aK.heir}}))
+}},1000);
+aJ=aD.init();
+if(aJ){setTimeout(function(){N("opening","local",aE)
+},50)
+}return aJ
+},send:function(aJ){aD.signal("send",aJ)
+},localSend:function(aJ){aD.signal("localSend",a.util.stringifyJSON({id:J,event:aJ}))
+},close:function(){if(!aw){clearInterval(K);
+aD.signal("close");
+aD.close()
+}}}
+}function ad(){var aB,aA="atmosphere-"+O.url,aF={storage:function(){function aG(aI){if(aI.key===aA&&aI.newValue){aC(aI.newValue)
+}}if(!a.util.storage){return
+}var aH=window.localStorage;
+return{init:function(){a.util.on(window,"storage",aG)
+},signal:function(aI,aJ){aH.setItem(aA,a.util.stringifyJSON({target:"c",type:aI,data:aJ}))
+},get:function(aI){return a.util.parseJSON(aH.getItem(aA+"-"+aI))
+},set:function(aI,aJ){aH.setItem(aA+"-"+aI,a.util.stringifyJSON(aJ))
+},close:function(){a.util.off(window,"storage",aG);
+aH.removeItem(aA);
+aH.removeItem(aA+"-opened");
+aH.removeItem(aA+"-children")
+}}
+},windowref:function(){var aH=aA.replace(/\W/g,""),aG=document.getElementById(aH),aI;
+if(!aG){aG=document.createElement("div");
+aG.id=aH;
+aG.style.display="none";
+aG.innerHTML='';
+document.body.appendChild(aG)
+}aI=aG.firstChild.contentWindow;
+return{init:function(){aI.callbacks=[aC];
+aI.fire=function(aJ){var aK;
+for(aK=0;
+aK0&&aC==="re-connecting"){aB.isReopen=true;
+ae(W)
+}else{if(W.error==null){W.request=aB;
+var aD=W.state;
+W.state=aC;
+var aA=W.transport;
+W.transport=aF;
+var aE=W.responseBody;
+C();
+W.responseBody=aE;
+W.state=aD;
+W.transport=aA
+}}}function z(aC){aC.transport="jsonp";
+var aB=O,aA;
+if((aC!=null)&&(typeof(aC)!=="undefined")){aB=aC
+}G={open:function(){var aE="atmosphere"+(++J);
+function aD(){var aF=aB.url;
+if(aB.dispatchUrl!=null){aF+=aB.dispatchUrl
+}var aH=aB.data;
+if(aB.attachHeadersAsQueryString){aF=X(aB);
+if(aH!==""){aF+="&X-Atmosphere-Post-Body="+encodeURIComponent(aH)
+}aH=""
+}var aG=document.head||document.getElementsByTagName("head")[0]||document.documentElement;
+aA=document.createElement("script");
+aA.src=aF+"&jsonpTransport="+aE;
+aA.clean=function(){aA.clean=aA.onerror=aA.onload=aA.onreadystatechange=null;
+if(aA.parentNode){aA.parentNode.removeChild(aA)
+}};
+aA.onload=aA.onreadystatechange=function(){if(!aA.readyState||/loaded|complete/.test(aA.readyState)){aA.clean()
+}};
+aA.onerror=function(){aA.clean();
+aB.lastIndex=0;
+if(aB.openId){clearTimeout(aB.openId)
+}if(aB.reconnect&&l++0){O.id=setTimeout(function(){if(!aB){am()
+}},O.connectTimeout)
+}o.onopen=function(aE){y(O);
+if(O.logLevel==="debug"){a.util.debug("SSE successfully opened")
+}if(!O.enableProtocol){if(!aB){N("opening","sse",O)
+}else{N("re-opening","sse",O)
+}}aB=true;
+if(O.method==="POST"){W.state="messageReceived";
+o.send(O.data)
+}};
+o.onmessage=function(aF){y(O);
+if(!O.enableXDR&&aF.origin&&aF.origin!==window.location.protocol+"//"+window.location.host){a.util.log(O.logLevel,["Origin was not "+window.location.protocol+"//"+window.location.host]);
+return
+}W.state="messageReceived";
+W.status=200;
+aF=aF.data;
+var aE=x(aF,O,W);
+if(!aE){C();
+W.responseBody="";
+W.messages=[]
+}};
+o.onerror=function(aE){clearTimeout(O.id);
+if(W.closedByClientTimeout){return
+}ai(aB);
+am();
+if(aw){a.util.log(O.logLevel,["SSE closed normally"])
+}else{if(!aB){Q("SSE failed. Downgrading to fallback transport and resending")
+}else{if(O.reconnect&&(W.transport==="sse")){if(l++0){O.reconnectId=setTimeout(function(){I(true)
+},O.reconnectInterval)
+}else{I(true)
+}W.responseBody="";
+W.messages=[]
+}else{a.util.log(O.logLevel,["SSE reconnect maximum try reached "+l]);
+af(0,"maxReconnectOnClose reached")
+}}}}}
+}function aj(aB){W.transport="websocket";
+if(O.enableProtocol&&aB){var aC=a.util.now()-O.ctime;
+O.lastTimestamp=Number(O.stime)+Number(aC)
+}var aA=k(O.url);
+if(O.logLevel==="debug"){a.util.debug("Invoking executeWebSocket");
+a.util.debug("Using URL: "+aA)
+}if(aB&&!O.reconnect){if(Z!=null){am()
+}return
+}Z=j(aA);
+if(O.webSocketBinaryType!=null){Z.binaryType=O.webSocketBinaryType
+}if(O.connectTimeout>0){O.id=setTimeout(function(){if(!aB){var aD={code:1002,reason:"",wasClean:false};
+Z.onclose(aD);
+try{am()
+}catch(aE){}return
+}},O.connectTimeout)
+}Z.onopen=function(aE){y(O);
+if(O.logLevel==="debug"){a.util.debug("Websocket successfully opened")
+}var aD=aB;
+aB=true;
+if(Z!=null){Z.webSocketOpened=aB
+}if(!O.enableProtocol){if(aD){N("re-opening","websocket",O)
+}else{N("opening","websocket",O)
+}}if(Z!=null){if(O.method==="POST"){W.state="messageReceived";
+Z.send(O.data)
+}}};
+Z.onmessage=function(aF){y(O);
+W.state="messageReceived";
+W.status=200;
+aF=aF.data;
+var aD=typeof(aF)==="string";
+if(aD){var aE=x(aF,O,W);
+if(!aE){C();
+W.responseBody="";
+W.messages=[]
+}}else{if(!u(O,aF)){return
+}W.responseBody=aF;
+C();
+W.responseBody=null
+}};
+Z.onerror=function(aD){clearTimeout(O.id)
+};
+Z.onclose=function(aD){clearTimeout(O.id);
+if(W.state==="closed"){return
+}var aE=aD.reason;
+if(aE===""){switch(aD.code){case 1000:aE="Normal closure; the connection successfully completed whatever purpose for which it was created.";
+break;
+case 1001:aE="The endpoint is going away, either because of a server failure or because the browser is navigating away from the page that opened the connection.";
+break;
+case 1002:aE="The endpoint is terminating the connection due to a protocol error.";
+break;
+case 1003:aE="The connection is being terminated because the endpoint received data of a type it cannot accept (for example, a text-only endpoint received binary data).";
+break;
+case 1004:aE="The endpoint is terminating the connection because a data frame was received that is too large.";
+break;
+case 1005:aE="Unknown: no status code was provided even though one was expected.";
+break;
+case 1006:aE="Connection was closed abnormally (that is, with no close frame being sent).";
+break
+}}if(O.logLevel==="warn"){a.util.warn("Websocket closed, reason: "+aE);
+a.util.warn("Websocket closed, wasClean: "+aD.wasClean)
+}if(W.closedByClientTimeout){return
+}ai(aB);
+W.state="closed";
+if(aw){a.util.log(O.logLevel,["Websocket closed normally"])
+}else{if(!aB){Q("Websocket failed. Downgrading to Comet and resending")
+}else{if(O.reconnect&&W.transport==="websocket"){am();
+if(l++0){O.reconnectId=setTimeout(function(){W.responseBody="";
+W.messages=[];
+aj(true)
+},O.reconnectInterval)
+}else{W.responseBody="";
+W.messages=[];
+aj(true)
+}}else{a.util.log(O.logLevel,["Websocket reconnect maximum try reached "+O.requestCount]);
+if(O.logLevel==="warn"){a.util.warn("Websocket error, reason: "+aD.reason)
+}af(0,"maxReconnectOnClose reached")
+}}}}};
+if(Z.url===undefined){Z.onclose({reason:"Android 4.1 does not support websockets.",wasClean:false})
+}}function u(aD,aC){var aA=true;
+if(aD.transport==="polling"){return aA
+}if(a.util.trim(aC).length!==0&&aD.enableProtocol&&aD.firstMessage){aD.firstMessage=false;
+var aB=aC.split(aD.messageDelimiter);
+var aE=aB.length===2?0:1;
+aD.uuid=a.util.trim(aB[aE]);
+aD.stime=a.util.trim(aB[aE+1]);
+aA=false;
+if(aD.transport!=="long-polling"){ao(aD)
+}e=aD.uuid
+}else{if(aD.enableProtocol&&aD.firstMessage){aA=false
+}else{ao(aD)
+}}return aA
+}function y(aA){clearTimeout(aA.id);
+if(aA.timeout>0&&aA.transport!=="polling"){aA.id=setTimeout(function(){r(aA);
+D();
+am()
+},aA.timeout)
+}}function r(aA){W.closedByClientTimeout=true;
+W.state="closedByClient";
+W.responseBody="";
+W.status=408;
+W.messages=[];
+C()
+}function af(aA,aB){am();
+clearTimeout(O.id);
+W.state="error";
+W.reasonPhrase=aB;
+W.responseBody="";
+W.status=aA;
+W.messages=[];
+C()
+}function x(aE,aD,aA){if(!u(aD,aE)){return true
+}if(aE.length===0){return true
+}if(aD.trackMessageLength){aE=aA.partialMessage+aE;
+var aC=[];
+var aB=aE.indexOf(aD.messageDelimiter);
+while(aB!==-1){var aG=a.util.trim(aE.substring(0,aB));
+var aF=+aG;
+if(isNaN(aF)){throw new Error('message length "'+aG+'" is not a number')
+}aB+=aD.messageDelimiter.length;
+if(aB+aF>aE.length){aB=-1
+}else{aC.push(aE.substring(aB,aB+aF));
+aE=aE.substring(aB+aF,aE.length);
+aB=aE.indexOf(aD.messageDelimiter)
+}}aA.partialMessage=aE;
+if(aC.length!==0){aA.responseBody=aC.join(aD.messageDelimiter);
+aA.messages=aC;
+return false
+}else{aA.responseBody="";
+aA.messages=[];
+return true
+}}else{aA.responseBody=aE
+}return false
+}function Q(aA){a.util.log(O.logLevel,[aA]);
+if(typeof(O.onTransportFailure)!=="undefined"){O.onTransportFailure(aA,O)
+}else{if(typeof(a.util.onTransportFailure)!=="undefined"){a.util.onTransportFailure(aA,O)
+}}O.transport=O.fallbackTransport;
+var aB=O.connectTimeout===-1?0:O.connectTimeout;
+if(O.reconnect&&O.transport!=="none"||O.transport==null){O.method=O.fallbackMethod;
+W.transport=O.fallbackTransport;
+O.fallbackTransport="none";
+if(aB>0){O.reconnectId=setTimeout(function(){t()
+},aB)
+}else{t()
+}}else{af(500,"Unable to reconnect with fallback transport")
+}}function X(aC,aA){var aB=O;
+if((aC!=null)&&(typeof(aC)!=="undefined")){aB=aC
+}if(aA==null){aA=aB.url
+}if(!aB.attachHeadersAsQueryString){return aA
+}if(aA.indexOf("X-Atmosphere-Framework")!==-1){return aA
+}aA+=(aA.indexOf("?")!==-1)?"&":"?";
+aA+="X-Atmosphere-tracking-id="+aB.uuid;
+aA+="&X-Atmosphere-Framework="+c;
+aA+="&X-Atmosphere-Transport="+aB.transport;
+if(aB.trackMessageLength){aA+="&X-Atmosphere-TrackMessageSize=true"
+}if(aB.lastTimestamp!=null){aA+="&X-Cache-Date="+aB.lastTimestamp
+}else{aA+="&X-Cache-Date="+0
+}if(aB.contentType!==""){aA+="&Content-Type="+aB.contentType
+}if(aB.enableProtocol){aA+="&X-atmo-protocol=true"
+}a.util.each(aB.headers,function(aD,aF){var aE=a.util.isFunction(aF)?aF.call(this,aB,aC,W):aF;
+if(aE!=null){aA+="&"+encodeURIComponent(aD)+"="+encodeURIComponent(aE)
+}});
+return aA
+}function ao(aA){if(!aA.isOpen){aA.isOpen=true;
+N("opening",aA.transport,aA)
+}else{if(aA.isReopen){aA.isReopen=false;
+N("re-opening",aA.transport,aA)
+}}}function s(aC){var aA=O;
+if((aC!=null)||(typeof(aC)!=="undefined")){aA=aC
+}aA.lastIndex=0;
+aA.readyState=0;
+if((aA.transport==="jsonp")||((aA.enableXDR)&&(a.util.checkCORSSupport()))){z(aA);
+return
+}if(a.util.browser.msie&&a.util.browser.version<10){if((aA.transport==="streaming")){if(aA.enableXDR&&window.XDomainRequest){P(aA)
+}else{av(aA)
+}return
+}if((aA.enableXDR)&&(window.XDomainRequest)){P(aA);
+return
+}}var aD=function(){aA.lastIndex=0;
+if(aA.reconnect&&l++2&&aB.readyState===4){am();
+aD();
+return
+}aA.readyState=aB.readyState;
+if(aA.transport==="streaming"&&aB.readyState>=3){aL=true
+}else{if(aA.transport==="long-polling"&&aB.readyState===4){aL=true
+}}y(O);
+if(aA.transport!=="polling"){var aF=200;
+if(aB.readyState===4){aF=aB.status>1000?0:aB.status
+}if(aF>=300||aF===0){W.errorHandled=true;
+am();
+aD();
+return
+}if((!aA.enableProtocol||!aC.firstMessage)&&aB.readyState===2){ao(aA)
+}}else{if(aB.readyState===4){aL=true
+}}if(aL){var aJ=aB.responseText;
+if(a.util.trim(aJ).length===0&&aA.transport==="long-polling"){if(!aB.hasData){aD()
+}else{aB.hasData=false
+}return
+}aB.hasData=true;
+ag(aB,O);
+if(aA.transport==="streaming"){if(!a.util.browser.opera){var aI=aJ.substring(aA.lastIndex,aJ.length);
+aG=x(aI,aA,W);
+aA.lastIndex=aJ.length;
+if(aG){return
+}}else{a.util.iterate(function(){if(W.status!==500&&aB.responseText.length>aA.lastIndex){try{W.status=aB.status;
+W.headers=a.util.parseHeaders(aB.getAllResponseHeaders());
+ag(aB,O)
+}catch(aN){W.status=404
+}y(O);
+W.state="messageReceived";
+var aM=aB.responseText.substring(aA.lastIndex);
+aA.lastIndex=aB.responseText.length;
+aG=x(aM,aA,W);
+if(!aG){C()
+}L(aB,aA)
+}else{if(W.status>400){aA.lastIndex=aB.responseText.length;
+return false
+}}},0)
+}}else{aG=x(aJ,aA,W)
+}try{W.status=aB.status;
+W.headers=a.util.parseHeaders(aB.getAllResponseHeaders());
+ag(aB,aA)
+}catch(aK){W.status=404
+}if(aA.suspend){W.state=W.status===0?"closed":"messageReceived"
+}else{W.state="messagePublished"
+}var aH=aC.transport!=="streaming"&&aC.transport!=="polling";
+if(aH&&!aA.executeCallbackBeforeReconnect){R(aB,aA,0)
+}if(W.responseBody.length!==0&&!aG){C()
+}if(aH&&aA.executeCallbackBeforeReconnect){R(aB,aA,0)
+}L(aB,aA)
+}};
+try{aB.send(aA.data);
+ak=true
+}catch(aE){a.util.log(aA.logLevel,["Unable to connect to "+aA.url])
+}}else{if(aA.logLevel==="debug"){a.util.log(aA.logLevel,["Max re-connection reached."])
+}af(0,"maxRequest reached")
+}}function h(aC,aD,aB){var aA=aD.url;
+if(aD.dispatchUrl!=null&&aD.method==="POST"){aA+=aD.dispatchUrl
+}aA=X(aD,aA);
+aA=a.util.prepareURL(aA);
+if(aB){aC.open(aD.method,aA,aD.async);
+if(aD.connectTimeout>0){aD.id=setTimeout(function(){if(aD.requestCount===0){am();
+F("Connect timeout","closed",200,aD.transport)
+}},aD.connectTimeout)
+}}if(O.withCredentials){if("withCredentials" in aC){aC.withCredentials=true
+}}if(!O.dropAtmosphereHeaders){aC.setRequestHeader("X-Atmosphere-Framework",a.util.version);
+aC.setRequestHeader("X-Atmosphere-Transport",aD.transport);
+if(aD.lastTimestamp!=null){aC.setRequestHeader("X-Cache-Date",aD.lastTimestamp)
+}else{aC.setRequestHeader("X-Cache-Date",0)
+}if(aD.trackMessageLength){aC.setRequestHeader("X-Atmosphere-TrackMessageSize","true")
+}aC.setRequestHeader("X-Atmosphere-tracking-id",aD.uuid)
+}if(aD.contentType!==""){aC.setRequestHeader("Content-Type",aD.contentType)
+}a.util.each(aD.headers,function(aE,aG){var aF=a.util.isFunction(aG)?aG.call(this,aC,aD,aB,W):aG;
+if(aF!=null){aC.setRequestHeader(aE,aF)
+}})
+}function R(aB,aC,aD){if(aC.reconnect||(aC.suspend&&ak)){var aA=0;
+if(aB&&aB.readyState!==0){aA=aB.status>1000?0:aB.status
+}W.status=aA===0?204:aA;
+W.reason=aA===0?"Server resumed the connection or down.":"OK";
+clearTimeout(aC.id);
+if(aC.reconnectId){clearTimeout(aC.reconnectId)
+}if(aD>0){O.reconnectId=setTimeout(function(){s(aC)
+},aD)
+}else{s(aC)
+}}}function ae(aA){aA.state="re-connecting";
+ab(aA)
+}function P(aA){if(aA.transport!=="polling"){E=V(aA);
+E.open()
+}else{V(aA).open()
+}}function V(aC){var aB=O;
+if((aC!=null)&&(typeof(aC)!=="undefined")){aB=aC
+}var aH=aB.transport;
+var aG=0;
+var aA=new window.XDomainRequest();
+var aE=function(){if(aB.transport==="long-polling"&&(aB.reconnect&&(aB.maxRequest===-1||aB.requestCount++0){aB.reconnectId=setTimeout(function(){N("re-connecting",aC.transport,aC);
+P(aB)
+},aB.reconnectInterval)
+}else{N("re-connecting",aC.transport,aC);
+P(aB)
+}}else{af(0,"maxReconnectOnClose reached")
+}}};
+aA.onload=function(){};
+var aD=function(aI){clearTimeout(aB.id);
+var aK=aI.responseText;
+aK=aK.substring(aG);
+aG+=aK.length;
+if(aH!=="polling"){y(aB);
+var aJ=x(aK,aB,W);
+if(aH==="long-polling"&&a.util.trim(aK).length===0){return
+}if(aB.executeCallbackBeforeReconnect){aE()
+}if(!aJ){F(W.responseBody,"messageReceived",200,aH)
+}if(!aB.executeCallbackBeforeReconnect){aE()
+}}};
+return{open:function(){var aI=aB.url;
+if(aB.dispatchUrl!=null){aI+=aB.dispatchUrl
+}aI=X(aB,aI);
+aA.open(aB.method,aF(aI));
+if(aB.method==="GET"){aA.send()
+}else{aA.send(aB.data)
+}if(aB.connectTimeout>0){aB.id=setTimeout(function(){if(aB.requestCount===0){am();
+F("Connect timeout","closed",200,aB.transport)
+}},aB.connectTimeout)
+}},close:function(){aA.abort()
+}}
+}function av(aA){E=w(aA);
+E.open()
+}function w(aD){var aC=O;
+if((aD!=null)&&(typeof(aD)!=="undefined")){aC=aD
+}var aB;
+var aE=new window.ActiveXObject("htmlfile");
+aE.open();
+aE.close();
+var aA=aC.url;
+if(aC.dispatchUrl!=null){aA+=aC.dispatchUrl
+}if(aC.transport!=="polling"){W.transport=aC.transport
+}return{open:function(){var aF=aE.createElement("iframe");
+aA=X(aC);
+if(aC.data!==""){aA+="&X-Atmosphere-Post-Body="+encodeURIComponent(aC.data)
+}aA=a.util.prepareURL(aA);
+aF.src=aA;
+aE.body.appendChild(aF);
+var aG=aF.contentDocument||aF.contentWindow.document;
+aB=a.util.iterate(function(){try{if(!aG.firstChild){return
+}var aJ=aG.body?aG.body.lastChild:aG;
+var aL=function(){var aN=aJ.cloneNode(true);
+aN.appendChild(aG.createTextNode("."));
+var aM=aN.innerText;
+aM=aM.substring(0,aM.length-1);
+return aM
+};
+if(!aG.body||!aG.body.firstChild||aG.body.firstChild.nodeName.toLowerCase()!=="pre"){var aI=aG.head||aG.getElementsByTagName("head")[0]||aG.documentElement||aG;
+var aH=aG.createElement("script");
+aH.text="document.write('')";
+aI.insertBefore(aH,aI.firstChild);
+aI.removeChild(aH);
+aJ=aG.body.lastChild
+}if(aC.closed){aC.isReopen=true
+}aB=a.util.iterate(function(){var aN=aL();
+if(aN.length>aC.lastIndex){y(O);
+W.status=200;
+W.error=null;
+aJ.innerText="";
+var aM=x(aN,aC,W);
+if(aM){return""
+}F(W.responseBody,"messageReceived",200,aC.transport)
+}aC.lastIndex=0;
+if(aG.readyState==="complete"){ai(true);
+N("re-connecting",aC.transport,aC);
+if(aC.reconnectInterval>0){aC.reconnectId=setTimeout(function(){av(aC)
+},aC.reconnectInterval)
+}else{av(aC)
+}return false
+}},null);
+return false
+}catch(aK){W.error=true;
+N("re-connecting",aC.transport,aC);
+if(l++0){aC.reconnectId=setTimeout(function(){av(aC)
+},aC.reconnectInterval)
+}else{av(aC)
+}}else{af(0,"maxReconnectOnClose reached")
+}aE.execCommand("Stop");
+aE.close();
+return false
+}})
+},close:function(){if(aB){aB()
+}aE.execCommand("Stop");
+ai(true)
+}}
+}function al(aA){if(q!=null){m(aA)
+}else{if(v!=null||o!=null){i(aA)
+}else{if(E!=null){Y(aA)
+}else{if(G!=null){U(aA)
+}else{if(Z!=null){H(aA)
+}else{af(0,"No suspended connection available");
+a.util.error("No suspended connection available. Make sure atmosphere.subscribe has been called and request.onOpen invoked before invoking this method")
+}}}}}}function n(aB,aA){if(!aA){aA=ap(aB)
+}aA.transport="polling";
+aA.method="GET";
+aA.async=false;
+aA.withCredentials=false;
+aA.reconnect=false;
+aA.force=true;
+aA.suspend=false;
+aA.timeout=1000;
+s(aA)
+}function m(aA){q.send(aA)
+}function B(aB){if(aB.length===0){return
+}try{if(q){q.localSend(aB)
+}else{if(aq){aq.signal("localMessage",a.util.stringifyJSON({id:J,event:aB}))
+}}}catch(aA){a.util.error(aA)
+}}function i(aB){var aA=ap(aB);
+s(aA)
+}function Y(aB){if(O.enableXDR&&a.util.checkCORSSupport()){var aA=ap(aB);
+aA.reconnect=false;
+z(aA)
+}else{i(aB)
+}}function U(aA){i(aA)
+}function T(aA){var aB=aA;
+if(typeof(aB)==="object"){aB=aA.data
+}return aB
+}function ap(aB){var aC=T(aB);
+var aA={connected:false,timeout:60000,method:"POST",url:O.url,contentType:O.contentType,headers:O.headers,reconnect:true,callback:null,data:aC,suspend:false,maxRequest:-1,logLevel:"info",requestCount:0,withCredentials:O.withCredentials,async:O.async,transport:"polling",isOpen:true,attachHeadersAsQueryString:true,enableXDR:O.enableXDR,uuid:O.uuid,dispatchUrl:O.dispatchUrl,enableProtocol:false,messageDelimiter:"|",maxReconnectOnClose:O.maxReconnectOnClose};
+if(typeof(aB)==="object"){aA=a.util.extend(aA,aB)
+}return aA
+}function H(aA){var aD=a.util.isBinary(aA)?aA:T(aA);
+var aB;
+try{if(O.dispatchUrl!=null){aB=O.webSocketPathDelimiter+O.dispatchUrl+O.webSocketPathDelimiter+aD
+}else{aB=aD
+}if(!Z.webSocketOpened){a.util.error("WebSocket not connected.");
+return
+}Z.send(aB)
+}catch(aC){Z.onclose=function(aE){};
+am();
+Q("Websocket failed. Downgrading to Comet and resending "+aA);
+i(aA)
+}}function ac(aB){var aA=a.util.parseJSON(aB);
+if(aA.id!==J){if(typeof(O.onLocalMessage)!=="undefined"){O.onLocalMessage(aA.event)
+}else{if(typeof(a.util.onLocalMessage)!=="undefined"){a.util.onLocalMessage(aA.event)
+}}}}function F(aD,aA,aB,aC){W.responseBody=aD;
+W.transport=aC;
+W.status=aB;
+W.state=aA;
+C()
+}function ag(aA,aD){if(!aD.readResponsesHeaders&&!aD.enableProtocol){aD.lastTimestamp=a.util.now();
+aD.uuid=J;
+return
+}try{var aC=aA.getResponseHeader("X-Cache-Date");
+if(aC&&aC!=null&&aC.length>0){aD.lastTimestamp=aC.split(" ").pop()
+}var aB=aA.getResponseHeader("X-Atmosphere-tracking-id");
+if(aB&&aB!=null){aD.uuid=aB.split(" ").pop()
+}if(aD.headers){a.util.each(O.headers,function(aG){var aF=aA.getResponseHeader(aG);
+if(aF){W.headers[aG]=aF
+}})
+}}catch(aE){}}function ab(aA){au(aA,O);
+au(aA,a.util)
+}function au(aB,aC){switch(aB.state){case"messageReceived":l=0;
+if(typeof(aC.onMessage)!=="undefined"){aC.onMessage(aB)
+}break;
+case"error":if(typeof(aC.onError)!=="undefined"){aC.onError(aB)
+}break;
+case"opening":delete O.closed;
+if(typeof(aC.onOpen)!=="undefined"){aC.onOpen(aB)
+}break;
+case"messagePublished":if(typeof(aC.onMessagePublished)!=="undefined"){aC.onMessagePublished(aB)
+}break;
+case"re-connecting":if(typeof(aC.onReconnect)!=="undefined"){aC.onReconnect(O,aB)
+}break;
+case"closedByClient":if(typeof(aC.onClientTimeout)!=="undefined"){aC.onClientTimeout(O)
+}break;
+case"re-opening":delete O.closed;
+if(typeof(aC.onReopen)!=="undefined"){aC.onReopen(O,aB)
+}break;
+case"fail-to-reconnect":if(typeof(aC.onFailureToReconnect)!=="undefined"){aC.onFailureToReconnect(O,aB)
+}break;
+case"unsubscribe":case"closed":var aA=typeof(O.closed)!=="undefined"?O.closed:false;
+if(typeof(aC.onClose)!=="undefined"&&!aA){aC.onClose(aB)
+}O.closed=true;
+break
+}}function ai(aA){if(W.state!=="closed"){W.state="closed";
+W.responseBody="";
+W.messages=[];
+W.status=!aA?501:200;
+C()
+}}function C(){var aC=function(aF,aG){aG(W)
+};
+if(q==null&&aa!=null){aa(W.responseBody)
+}O.reconnect=O.mrequest;
+var aA=typeof(W.responseBody)==="string";
+var aD=(aA&&O.trackMessageLength)?(W.messages.length>0?W.messages:[""]):new Array(W.responseBody);
+for(var aB=0;
+aB1&&aD[aB].length===0){continue
+}W.responseBody=(aA)?a.util.trim(aD[aB]):aD[aB];
+if(q==null&&aa!=null){aa(W.responseBody)
+}if(W.responseBody.length===0&&W.state==="messageReceived"){continue
+}ab(W);
+if(f.length>0){if(O.logLevel==="debug"){a.util.debug("Invoking "+f.length+" global callbacks: "+W.state)
+}try{a.util.each(f,aC)
+}catch(aE){a.util.log(O.logLevel,["Callback exception"+aE])
+}}if(typeof(O.callback)==="function"){if(O.logLevel==="debug"){a.util.debug("Invoking request callbacks")
+}try{O.callback(W)
+}catch(aE){a.util.log(O.logLevel,["Callback exception"+aE])
+}}}}this.subscribe=function(aA){ay(aA);
+t()
+};
+this.execute=function(){t()
+};
+this.close=function(){an()
+};
+this.disconnect=function(){D()
+};
+this.getUrl=function(){return O.url
+};
+this.push=function(aC,aB){if(aB!=null){var aA=O.dispatchUrl;
+O.dispatchUrl=aB;
+al(aC);
+O.dispatchUrl=aA
+}else{al(aC)
+}};
+this.getUUID=function(){return O.uuid
+};
+this.pushLocal=function(aA){B(aA)
+};
+this.enableProtocol=function(aA){return O.enableProtocol
+};
+this.request=O;
+this.response=W
+}};
+a.subscribe=function(h,k,j){if(typeof(k)==="function"){a.addCallback(k)
+}if(typeof(h)!=="string"){j=h
+}else{j.url=h
+}var i=new a.AtmosphereRequest(j);
+i.execute();
+g[g.length]=i;
+return i
+};
+a.unsubscribe=function(){if(g.length>0){var h=[].concat(g);
+for(var k=0;
+k0){for(var l=0;
+l=0){g.splice(h,1)
+}};
+a.addCallback=function(h){if(a.util.inArray(h,f)===-1){f.push(h)
+}};
+a.removeCallback=function(i){var h=a.util.inArray(i,f);
+if(h!==-1){f.splice(h,1)
+}};
+a.util={browser:{},parseHeaders:function(i){var h,k=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,j={};
+while(h=k.exec(i)){j[h[1]]=h[2]
+}return j
+},now:function(){return new Date().getTime()
+},isArray:function(h){return Object.prototype.toString.call(h)==="[object Array]"
+},inArray:function(k,l){if(!Array.prototype.indexOf){var h=l.length;
+for(var j=0;
+j';
+return encodeURI(decodeURI(i.firstChild.href))
+},prepareURL:function(i){var j=a.util.now();
+var h=i.replace(/([?&])_=[^&]*/,"$1_="+j);
+return h+(h===i?(/\?/.test(i)?"&":"?")+"_="+j:"")
+},trim:function(h){if(!String.prototype.trim){return h.toString().replace(/(?:(?:^|\n)\s+|\s+(?:$|\n))/g,"").replace(/\s+/g," ")
+}else{return h.toString().trim()
+}},param:function(l){var j,h=[];
+function k(m,n){n=a.util.isFunction(n)?n():(n==null?"":n);
+h.push(encodeURIComponent(m)+"="+encodeURIComponent(n))
+}function i(n,o){var m;
+if(a.util.isArray(o)){a.util.each(o,function(q,p){if(/\[\]$/.test(n)){k(n,p)
+}else{i(n+"["+(typeof p==="object"?q:"")+"]",p)
+}})
+}else{if(Object.prototype.toString.call(o)==="[object Object]"){for(m in o){i(n+"["+m+"]",o[m])
+}}else{k(n,o)
+}}}for(j in l){i(j,l[j])
+}return h.join("&").replace(/%20/g,"+")
+},storage:!!(window.localStorage&&window.StorageEvent),iterate:function(j,i){var k;
+i=i||0;
+(function h(){k=setTimeout(function(){if(j()===false){return
+}h()
+},i)
+})();
+return function(){clearTimeout(k)
+}
+},each:function(n,o,j){if(!n){return
+}var m,k=0,l=n.length,h=a.util.isArray(n);
+if(j){if(h){for(;
+k-1;
+if(i){return true
+}return false
+}};
+d=a.util.now();
+(function(){var i=navigator.userAgent.toLowerCase(),h=/(chrome)[ \/]([\w.]+)/.exec(i)||/(webkit)[ \/]([\w.]+)/.exec(i)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(i)||/(msie) ([\w.]+)/.exec(i)||i.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(i)||[];
+a.util.browser[h[1]||""]=true;
+a.util.browser.version=h[2]||"0";
+if(a.util.browser.msie||(a.util.browser.mozilla&&a.util.browser.version.split(".")[0]==="1")){a.util.storage=false
+}})();
+a.util.on(window,"unload",function(h){a.unsubscribe()
+});
+a.util.on(window,"keypress",function(h){if(h.which===27){h.preventDefault()
+}});
+a.util.on(window,"offline",function(){a.unsubscribe()
+});
+window.atmosphere=a
+})();
\ No newline at end of file
diff --git a/websocket/atmosphere-chat/overlays/org.atmosphere.client.javascript-2.0.7/javascript/atmosphere.js b/websocket/atmosphere-chat/overlays/org.atmosphere.client.javascript-2.0.7/javascript/atmosphere.js
new file mode 100644
index 000000000..de170f218
--- /dev/null
+++ b/websocket/atmosphere-chat/overlays/org.atmosphere.client.javascript-2.0.7/javascript/atmosphere.js
@@ -0,0 +1,3031 @@
+/**
+ * Copyright 2013 Jeanfrancois Arcand
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Highly inspired by Portal v1.0
+ * http://github.com/flowersinthesand/portal
+ *
+ * Copyright 2011-2013, Donghwan Kim
+ * Licensed under the Apache License, Version 2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ */
+/**
+ * Official documentation of this library: https://github.com/Atmosphere/atmosphere/wiki/jQuery.atmosphere.js-API
+ */
+(function () {
+
+ "use strict";
+
+ var version = "2.0.7-javascript",
+ atmosphere = {},
+ guid,
+ requests = [],
+ callbacks = [],
+ uuid = 0,
+ hasOwn = Object.prototype.hasOwnProperty;
+
+ atmosphere = {
+
+ onError: function (response) {
+ },
+ onClose: function (response) {
+ },
+ onOpen: function (response) {
+ },
+ onReopen: function (response) {
+ },
+ onMessage: function (response) {
+ },
+ onReconnect: function (request, response) {
+ },
+ onMessagePublished: function (response) {
+ },
+ onTransportFailure: function (errorMessage, _request) {
+ },
+ onLocalMessage: function (response) {
+ },
+ onFailureToReconnect: function (request, response) {
+ },
+ onClientTimeout: function(request){
+ },
+
+ AtmosphereRequest: function (options) {
+
+ /**
+ * {Object} Request parameters.
+ *
+ * @private
+ */
+ var _request = {
+ timeout: 300000,
+ method: 'GET',
+ headers: {},
+ contentType: '',
+ callback: null,
+ url: '',
+ data: '',
+ suspend: true,
+ maxRequest: -1,
+ reconnect: true,
+ maxStreamingLength: 10000000,
+ lastIndex: 0,
+ logLevel: 'info',
+ requestCount: 0,
+ fallbackMethod: 'GET',
+ fallbackTransport: 'streaming',
+ transport: 'long-polling',
+ webSocketImpl: null,
+ webSocketBinaryType: null,
+ dispatchUrl: null,
+ webSocketPathDelimiter: "@@",
+ enableXDR: false,
+ rewriteURL: false,
+ attachHeadersAsQueryString: true,
+ executeCallbackBeforeReconnect: false,
+ readyState: 0,
+ lastTimestamp: 0,
+ withCredentials: false,
+ trackMessageLength: false,
+ messageDelimiter: '|',
+ connectTimeout: -1,
+ reconnectInterval: 0,
+ dropAtmosphereHeaders: true,
+ uuid: 0,
+ async: true,
+ shared: false,
+ readResponsesHeaders: false,
+ maxReconnectOnClose: 5,
+ enableProtocol: true,
+ onError: function (response) {
+ },
+ onClose: function (response) {
+ },
+ onOpen: function (response) {
+ },
+ onMessage: function (response) {
+ },
+ onReopen: function (request, response) {
+ },
+ onReconnect: function (request, response) {
+ },
+ onMessagePublished: function (response) {
+ },
+ onTransportFailure: function (reason, request) {
+ },
+ onLocalMessage: function (request) {
+ },
+ onFailureToReconnect: function (request, response) {
+ },
+ onClientTimeout: function(request){
+ }
+ };
+
+ /**
+ * {Object} Request's last response.
+ *
+ * @private
+ */
+ var _response = {
+ status: 200,
+ reasonPhrase: "OK",
+ responseBody: '',
+ messages: [],
+ headers: [],
+ state: "messageReceived",
+ transport: "polling",
+ error: null,
+ request: null,
+ partialMessage: "",
+ errorHandled: false,
+ closedByClientTimeout: false
+ };
+
+ /**
+ * {websocket} Opened web socket.
+ *
+ * @private
+ */
+ var _websocket = null;
+
+ /**
+ * {SSE} Opened SSE.
+ *
+ * @private
+ */
+ var _sse = null;
+
+ /**
+ * {XMLHttpRequest, ActiveXObject} Opened ajax request (in case of http-streaming or long-polling)
+ *
+ * @private
+ */
+ var _activeRequest = null;
+
+ /**
+ * {Object} Object use for streaming with IE.
+ *
+ * @private
+ */
+ var _ieStream = null;
+
+ /**
+ * {Object} Object use for jsonp transport.
+ *
+ * @private
+ */
+ var _jqxhr = null;
+
+ /**
+ * {boolean} If request has been subscribed or not.
+ *
+ * @private
+ */
+ var _subscribed = true;
+
+ /**
+ * {number} Number of test reconnection.
+ *
+ * @private
+ */
+ var _requestCount = 0;
+
+ /**
+ * {boolean} If request is currently aborded.
+ *
+ * @private
+ */
+ var _abordingConnection = false;
+
+ /**
+ * A local "channel' of communication.
+ *
+ * @private
+ */
+ var _localSocketF = null;
+
+ /**
+ * The storage used.
+ *
+ * @private
+ */
+ var _storageService;
+
+ /**
+ * Local communication
+ *
+ * @private
+ */
+ var _localStorageService = null;
+
+ /**
+ * A Unique ID
+ *
+ * @private
+ */
+ var guid = atmosphere.util.now();
+
+ /** Trace time */
+ var _traceTimer;
+
+ /** Key for connection sharing */
+ var _sharingKey;
+
+ // Automatic call to subscribe
+ _subscribe(options);
+
+ /**
+ * Initialize atmosphere request object.
+ *
+ * @private
+ */
+ function _init() {
+ _subscribed = true;
+ _abordingConnection = false;
+ _requestCount = 0;
+
+ _websocket = null;
+ _sse = null;
+ _activeRequest = null;
+ _ieStream = null;
+ }
+
+ /**
+ * Re-initialize atmosphere object.
+ *
+ * @private
+ */
+ function _reinit() {
+ _clearState();
+ _init();
+ }
+
+ /**
+ *
+ * @private
+ */
+ function _verifyStreamingLength(ajaxRequest, rq) {
+ // Wait to be sure we have the full message before closing.
+ if (_response.partialMessage === "" && (rq.transport === 'streaming') && (ajaxRequest.responseText.length > rq.maxStreamingLength)) {
+ _response.messages = [];
+ _invokeClose(true);
+ _disconnect();
+ _clearState();
+ _reconnect(ajaxRequest, rq, 0);
+ }
+ }
+
+ /**
+ * Disconnect
+ *
+ * @private
+ */
+ function _disconnect() {
+ if (_request.enableProtocol && !_request.firstMessage) {
+ var query = "X-Atmosphere-Transport=close&X-Atmosphere-tracking-id=" + _request.uuid;
+
+ atmosphere.util.each(_request.headers, function (name, value) {
+ var h = atmosphere.util.isFunction(value) ? value.call(this, _request, _request, _response) : value;
+ if (h != null) {
+ query += "&" + encodeURIComponent(name) + "=" + encodeURIComponent(h);
+ }
+ });
+
+ var url = _request.url.replace(/([?&])_=[^&]*/, query);
+ url = url + (url === _request.url ? (/\?/.test(_request.url) ? "&" : "?") + query : "");
+
+
+ var rq = {
+ connected: false,
+ };
+ var closeR = new atmosphere.AtmosphereRequest(rq);
+ closeR.attachHeadersAsQueryString = false;
+ closeR.dropAtmosphereHeaders = true;
+ closeR.url = url;
+ closeR.contentType = "text/plain";
+ closeR.transport = 'polling';
+ _pushOnClose("", closeR);
+ }
+ }
+
+ /**
+ * Close request.
+ *
+ * @private
+ */
+ function _close() {
+ if (_request.reconnectId) {
+ clearTimeout(_request.reconnectId);
+ }
+ _request.reconnect = false;
+ _abordingConnection = true;
+ _response.request = _request;
+ _response.state = 'unsubscribe';
+ _response.responseBody = "";
+ _response.status = 408;
+ _response.partialMessage = "";
+ _invokeCallback();
+ _disconnect();
+ _clearState();
+ }
+
+ function _clearState() {
+ _response.partialMessage = "";
+ if (_request.id) {
+ clearTimeout(_request.id);
+ }
+
+ if (_ieStream != null) {
+ _ieStream.close();
+ _ieStream = null;
+ }
+ if (_jqxhr != null) {
+ _jqxhr.abort();
+ _jqxhr = null;
+ }
+ if (_activeRequest != null) {
+ _activeRequest.abort();
+ _activeRequest = null;
+ }
+ if (_websocket != null) {
+ if (_websocket.webSocketOpened) {
+ _websocket.close();
+ }
+ _websocket = null;
+ }
+ if (_sse != null) {
+ _sse.close();
+ _sse = null;
+ }
+ _clearStorage();
+ }
+
+ function _clearStorage() {
+ // Stop sharing a connection
+ if (_storageService != null) {
+ // Clears trace timer
+ clearInterval(_traceTimer);
+ // Removes the trace
+ document.cookie = _sharingKey + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
+ // The heir is the parent unless unloading
+ _storageService.signal("close", {
+ reason: "",
+ heir: !_abordingConnection ? guid : (_storageService.get("children") || [])[0]
+ });
+ _storageService.close();
+ }
+ if (_localStorageService != null) {
+ _localStorageService.close();
+ }
+ }
+
+ /**
+ * Subscribe request using request transport.
+ * If request is currently opened, this one will be closed.
+ *
+ * @param {Object} Request parameters.
+ * @private
+ */
+ function _subscribe(options) {
+ _reinit();
+
+ _request = atmosphere.util.extend(_request, options);
+ // Allow at least 1 request
+ _request.mrequest = _request.reconnect;
+ if (!_request.reconnect) {
+ _request.reconnect = true;
+ }
+ }
+
+ /**
+ * Check if web socket is supported (check for custom implementation provided by request object or browser implementation).
+ *
+ * @returns {boolean} True if web socket is supported, false otherwise.
+ * @private
+ */
+ function _supportWebsocket() {
+ return _request.webSocketImpl != null || window.WebSocket || window.MozWebSocket;
+ }
+
+ /**
+ * Check if server side events (SSE) is supported (check for custom implementation provided by request object or browser implementation).
+ *
+ * @returns {boolean} True if web socket is supported, false otherwise.
+ * @private
+ */
+ function _supportSSE() {
+ return window.EventSource;
+ }
+
+ /**
+ * Open request using request transport.
+ * If request transport is 'websocket' but websocket can't be opened, request will automatically reconnect using fallback transport.
+ *
+ * @private
+ */
+ function _execute() {
+ // Shared across multiple tabs/windows.
+ if (_request.shared) {
+ _localStorageService = _local(_request);
+ if (_localStorageService != null) {
+ if (_request.logLevel === 'debug') {
+ atmosphere.util.debug("Storage service available. All communication will be local");
+ }
+
+ if (_localStorageService.open(_request)) {
+ // Local connection.
+ return;
+ }
+ }
+
+ if (_request.logLevel === 'debug') {
+ atmosphere.util.debug("No Storage service available.");
+ }
+ // Wasn't local or an error occurred
+ _localStorageService = null;
+ }
+
+ // Protocol
+ _request.firstMessage = uuid == 0 ? true : false;
+ _request.isOpen = false;
+ _request.ctime = atmosphere.util.now();
+ _request.uuid = uuid;
+ _response.closedByClientTimeout = false;
+
+ if (_request.transport !== 'websocket' && _request.transport !== 'sse') {
+ _executeRequest(_request);
+
+ } else if (_request.transport === 'websocket') {
+ if (!_supportWebsocket()) {
+ _reconnectWithFallbackTransport("Websocket is not supported, using request.fallbackTransport (" + _request.fallbackTransport
+ + ")");
+ } else {
+ _executeWebSocket(false);
+ }
+ } else if (_request.transport === 'sse') {
+ if (!_supportSSE()) {
+ _reconnectWithFallbackTransport("Server Side Events(SSE) is not supported, using request.fallbackTransport ("
+ + _request.fallbackTransport + ")");
+ } else {
+ _executeSSE(false);
+ }
+ }
+ }
+
+ function _local(request) {
+ var trace, connector, orphan, name = "atmosphere-" + request.url, connectors = {
+ storage: function () {
+ function onstorage(event) {
+ if (event.key === name && event.newValue) {
+ listener(event.newValue);
+ }
+ }
+
+ if (!atmosphere.util.storage) {
+ return;
+ }
+
+ var storage = window.localStorage,
+ get = function (key) {
+ return atmosphere.util.parseJSON(storage.getItem(name + "-" + key));
+ },
+ set = function (key, value) {
+ storage.setItem(name + "-" + key, atmosphere.util.stringifyJSON(value));
+ };
+
+ return {
+ init: function () {
+ set("children", get("children").concat([guid]));
+ atmosphere.util.on(window, "storage", onstorage);
+ return get("opened");
+ },
+ signal: function (type, data) {
+ storage.setItem(name, atmosphere.util.stringifyJSON({
+ target: "p",
+ type: type,
+ data: data
+ }));
+ },
+ close: function () {
+ var children = get("children");
+
+ atmosphere.util.off(window, "storage", onstorage);
+ if (children) {
+ if (removeFromArray(children, request.id)) {
+ set("children", children);
+ }
+ }
+ }
+ };
+ },
+ windowref: function () {
+ var win = window.open("", name.replace(/\W/g, ""));
+
+ if (!win || win.closed || !win.callbacks) {
+ return;
+ }
+
+ return {
+ init: function () {
+ win.callbacks.push(listener);
+ win.children.push(guid);
+ return win.opened;
+ },
+ signal: function (type, data) {
+ if (!win.closed && win.fire) {
+ win.fire(atmosphere.util.stringifyJSON({
+ target: "p",
+ type: type,
+ data: data
+ }));
+ }
+ },
+ close: function () {
+ // Removes traces only if the parent is alive
+ if (!orphan) {
+ removeFromArray(win.callbacks, listener);
+ removeFromArray(win.children, guid);
+ }
+ }
+
+ };
+ }
+ };
+
+ function removeFromArray(array, val) {
+ var i, length = array.length;
+
+ for (i = 0; i < length; i++) {
+ if (array[i] === val) {
+ array.splice(i, 1);
+ }
+ }
+
+ return length !== array.length;
+ }
+
+ // Receives open, close and message command from the parent
+ function listener(string) {
+ var command = atmosphere.util.parseJSON(string), data = command.data;
+
+ if (command.target === "c") {
+ switch (command.type) {
+ case "open":
+ _open("opening", 'local', _request);
+ break;
+ case "close":
+ if (!orphan) {
+ orphan = true;
+ if (data.reason === "aborted") {
+ _close();
+ } else {
+ // Gives the heir some time to reconnect
+ if (data.heir === guid) {
+ _execute();
+ } else {
+ setTimeout(function () {
+ _execute();
+ }, 100);
+ }
+ }
+ }
+ break;
+ case "message":
+ _prepareCallback(data, "messageReceived", 200, request.transport);
+ break;
+ case "localMessage":
+ _localMessage(data);
+ break;
+ }
+ }
+ }
+
+ function findTrace() {
+ var matcher = new RegExp("(?:^|; )(" + encodeURIComponent(name) + ")=([^;]*)").exec(document.cookie);
+ if (matcher) {
+ return atmosphere.util.parseJSON(decodeURIComponent(matcher[2]));
+ }
+ }
+
+ // Finds and validates the parent socket's trace from the cookie
+ trace = findTrace();
+ if (!trace || atmosphere.util.now() - trace.ts > 1000) {
+ return;
+ }
+
+ // Chooses a connector
+ connector = connectors.storage() || connectors.windowref();
+ if (!connector) {
+ return;
+ }
+
+ return {
+ open: function () {
+ var parentOpened;
+
+ // Checks the shared one is alive
+ _traceTimer = setInterval(function () {
+ var oldTrace = trace;
+ trace = findTrace();
+ if (!trace || oldTrace.ts === trace.ts) {
+ // Simulates a close signal
+ listener(atmosphere.util.stringifyJSON({
+ target: "c",
+ type: "close",
+ data: {
+ reason: "error",
+ heir: oldTrace.heir
+ }
+ }));
+ }
+ }, 1000);
+
+ parentOpened = connector.init();
+ if (parentOpened) {
+ // Firing the open event without delay robs the user of the opportunity to bind connecting event handlers
+ setTimeout(function () {
+ _open("opening", 'local', request);
+ }, 50);
+ }
+ return parentOpened;
+ },
+ send: function (event) {
+ connector.signal("send", event);
+ },
+ localSend: function (event) {
+ connector.signal("localSend", atmosphere.util.stringifyJSON({
+ id: guid,
+ event: event
+ }));
+ },
+ close: function () {
+ // Do not signal the parent if this method is executed by the unload event handler
+ if (!_abordingConnection) {
+ clearInterval(_traceTimer);
+ connector.signal("close");
+ connector.close();
+ }
+ }
+ };
+ }
+
+ function share() {
+ var storageService, name = "atmosphere-" + _request.url, servers = {
+ // Powered by the storage event and the localStorage
+ // http://www.w3.org/TR/webstorage/#event-storage
+ storage: function () {
+ function onstorage(event) {
+ // When a deletion, newValue initialized to null
+ if (event.key === name && event.newValue) {
+ listener(event.newValue);
+ }
+ }
+
+ if (!atmosphere.util.storage) {
+ return;
+ }
+
+ var storage = window.localStorage;
+
+ return {
+ init: function () {
+ // Handles the storage event
+ atmosphere.util.on(window, "storage", onstorage);
+ },
+ signal: function (type, data) {
+ storage.setItem(name, atmosphere.util.stringifyJSON({
+ target: "c",
+ type: type,
+ data: data
+ }));
+ },
+ get: function (key) {
+ return atmosphere.util.parseJSON(storage.getItem(name + "-" + key));
+ },
+ set: function (key, value) {
+ storage.setItem(name + "-" + key, atmosphere.util.stringifyJSON(value));
+ },
+ close: function () {
+ atmosphere.util.off(window, "storage", onstorage);
+ storage.removeItem(name);
+ storage.removeItem(name + "-opened");
+ storage.removeItem(name + "-children");
+ }
+
+ };
+ },
+ // Powered by the window.open method
+ // https://developer.mozilla.org/en/DOM/window.open
+ windowref: function () {
+ // Internet Explorer raises an invalid argument error
+ // when calling the window.open method with the name containing non-word characters
+ var neim = name.replace(/\W/g, ""), container = document.getElementById(neim), win;
+
+ if (!container) {
+ container = document.createElement("div");
+ container.id = neim;
+ container.style.display = "none";
+ container.innerHTML = '';
+ document.body.appendChild(container);
+ }
+
+ win = container.firstChild.contentWindow;
+
+ return {
+ init: function () {
+ // Callbacks from different windows
+ win.callbacks = [listener];
+ // In IE 8 and less, only string argument can be safely passed to the function in other window
+ win.fire = function (string) {
+ var i;
+
+ for (i = 0; i < win.callbacks.length; i++) {
+ win.callbacks[i](string);
+ }
+ };
+ },
+ signal: function (type, data) {
+ if (!win.closed && win.fire) {
+ win.fire(atmosphere.util.stringifyJSON({
+ target: "c",
+ type: type,
+ data: data
+ }));
+ }
+ },
+ get: function (key) {
+ return !win.closed ? win[key] : null;
+ },
+ set: function (key, value) {
+ if (!win.closed) {
+ win[key] = value;
+ }
+ },
+ close: function () {
+ }
+ };
+ }
+ };
+
+ // Receives send and close command from the children
+ function listener(string) {
+ var command = atmosphere.util.parseJSON(string), data = command.data;
+
+ if (command.target === "p") {
+ switch (command.type) {
+ case "send":
+ _push(data);
+ break;
+ case "localSend":
+ _localMessage(data);
+ break;
+ case "close":
+ _close();
+ break;
+ }
+ }
+ }
+
+ _localSocketF = function propagateMessageEvent(context) {
+ storageService.signal("message", context);
+ };
+
+ function leaveTrace() {
+ document.cookie = _sharingKey + "=" +
+ // Opera's JSON implementation ignores a number whose a last digit of 0 strangely
+ // but has no problem with a number whose a last digit of 9 + 1
+ encodeURIComponent(atmosphere.util.stringifyJSON({
+ ts: atmosphere.util.now() + 1,
+ heir: (storageService.get("children") || [])[0]
+ })) + "; path=/";
+ }
+
+ // Chooses a storageService
+ storageService = servers.storage() || servers.windowref();
+ storageService.init();
+
+ if (_request.logLevel === 'debug') {
+ atmosphere.util.debug("Installed StorageService " + storageService);
+ }
+
+ // List of children sockets
+ storageService.set("children", []);
+
+ if (storageService.get("opened") != null && !storageService.get("opened")) {
+ // Flag indicating the parent socket is opened
+ storageService.set("opened", false);
+ }
+ // Leaves traces
+ _sharingKey = encodeURIComponent(name);
+ leaveTrace();
+ _traceTimer = setInterval(leaveTrace, 1000);
+
+ _storageService = storageService;
+ }
+
+ /**
+ * @private
+ */
+ function _open(state, transport, request) {
+ if (_request.shared && transport !== 'local') {
+ share();
+ }
+
+ if (_storageService != null) {
+ _storageService.set("opened", true);
+ }
+
+ request.close = function () {
+ _close();
+ };
+
+ if (_requestCount > 0 && state === 're-connecting') {
+ request.isReopen = true;
+ _tryingToReconnect(_response);
+ } else if (_response.error == null) {
+ _response.request = request;
+ var prevState = _response.state;
+ _response.state = state;
+ var prevTransport = _response.transport;
+ _response.transport = transport;
+
+ var _body = _response.responseBody;
+ _invokeCallback();
+ _response.responseBody = _body;
+
+ _response.state = prevState;
+ _response.transport = prevTransport;
+ }
+ }
+
+ /**
+ * Execute request using jsonp transport.
+ *
+ * @param request {Object} request Request parameters, if undefined _request object will be used.
+ * @private
+ */
+ function _jsonp(request) {
+ // When CORS is enabled, make sure we force the proper transport.
+ request.transport = "jsonp";
+
+ var rq = _request, script;
+ if ((request != null) && (typeof (request) !== 'undefined')) {
+ rq = request;
+ }
+
+ _jqxhr = {
+ open: function () {
+ var callback = "atmosphere" + (++guid);
+
+ function poll() {
+ var url = rq.url;
+ if (rq.dispatchUrl != null) {
+ url += rq.dispatchUrl;
+ }
+
+ var data = rq.data;
+ if (rq.attachHeadersAsQueryString) {
+ url = _attachHeaders(rq);
+ if (data !== '') {
+ url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(data);
+ }
+ data = '';
+ }
+
+ var head = document.head || document.getElementsByTagName("head")[0] || document.documentElement;
+
+ script = document.createElement("script");
+ script.src = url + "&jsonpTransport=" + callback;
+ script.clean = function () {
+ script.clean = script.onerror = script.onload = script.onreadystatechange = null;
+ if (script.parentNode) {
+ script.parentNode.removeChild(script);
+ }
+ };
+ script.onload = script.onreadystatechange = function () {
+ if (!script.readyState || /loaded|complete/.test(script.readyState)) {
+ script.clean();
+ }
+ };
+ script.onerror = function () {
+ script.clean();
+ rq.lastIndex = 0;
+
+ if (rq.openId) {
+ clearTimeout(rq.openId);
+ }
+
+ if (rq.reconnect && _requestCount++ < rq.maxReconnectOnClose) {
+ _open('re-connecting', rq.transport, rq);
+ _reconnect(_jqxhr, rq, request.reconnectInterval);
+ rq.openId = setTimeout(function() {
+ _triggerOpen(rq);
+ }, rq.reconnectInterval + 1000);
+ } else {
+ _onError(0, "maxReconnectOnClose reached");
+ }
+ };
+
+ head.insertBefore(script, head.firstChild);
+ }
+
+ // Attaches callback
+ window[callback] = function (msg) {
+ if (rq.reconnect) {
+ if (rq.maxRequest === -1 || rq.requestCount++ < rq.maxRequest) {
+ // _readHeaders(_jqxhr, rq);
+
+ if (!rq.executeCallbackBeforeReconnect) {
+ _reconnect(_jqxhr, rq, 0);
+ }
+
+ if (msg != null && typeof msg !== 'string') {
+ try {
+ msg = msg.message;
+ } catch (err) {
+ // The message was partial
+ }
+ }
+
+ var skipCallbackInvocation = _trackMessageSize(msg, rq, _response);
+ if (!skipCallbackInvocation) {
+ _prepareCallback(_response.responseBody, "messageReceived", 200, rq.transport);
+ }
+
+ if (rq.executeCallbackBeforeReconnect) {
+ _reconnect(_jqxhr, rq, 0);
+ }
+ } else {
+ atmosphere.util.log(_request.logLevel, ["JSONP reconnect maximum try reached " + _request.requestCount]);
+ _onError(0, "maxRequest reached");
+ }
+ }
+ };
+ setTimeout(function () {
+ poll();
+ }, 50);
+ },
+ abort: function () {
+ if (script.clean) {
+ script.clean();
+ }
+ }
+ };
+
+ _jqxhr.open();
+ }
+
+ /**
+ * Build websocket object.
+ *
+ * @param location {string} Web socket url.
+ * @returns {websocket} Web socket object.
+ * @private
+ */
+ function _getWebSocket(location) {
+ if (_request.webSocketImpl != null) {
+ return _request.webSocketImpl;
+ } else {
+ if (window.WebSocket) {
+ return new WebSocket(location);
+ } else {
+ return new MozWebSocket(location);
+ }
+ }
+ }
+
+ /**
+ * Build web socket url from request url.
+ *
+ * @return {string} Web socket url (start with "ws" or "wss" for secure web socket).
+ * @private
+ */
+ function _buildWebSocketUrl() {
+ return atmosphere.util.getAbsoluteURL(_attachHeaders(_request)).replace(/^http/, "ws");
+ }
+
+ /**
+ * Build SSE url from request url.
+ *
+ * @return a url with Atmosphere's headers
+ * @private
+ */
+ function _buildSSEUrl() {
+ var url = _attachHeaders(_request);
+ return url;
+ }
+
+ /**
+ * Open SSE.
+ * Automatically use fallback transport if SSE can't be opened.
+ *
+ * @private
+ */
+ function _executeSSE(sseOpened) {
+
+ _response.transport = "sse";
+
+ var location = _buildSSEUrl();
+
+ if (_request.logLevel === 'debug') {
+ atmosphere.util.debug("Invoking executeSSE");
+ atmosphere.util.debug("Using URL: " + location);
+ }
+
+ if (_request.enableProtocol && sseOpened) {
+ var time = atmosphere.util.now() - _request.ctime;
+ _request.lastTimestamp = Number(_request.stime) + Number(time);
+ }
+
+ if (sseOpened && !_request.reconnect) {
+ if (_sse != null) {
+ _clearState();
+ }
+ return;
+ }
+
+ try {
+ _sse = new EventSource(location, {
+ withCredentials: _request.withCredentials
+ });
+ } catch (e) {
+ _onError(0, e);
+ _reconnectWithFallbackTransport("SSE failed. Downgrading to fallback transport and resending");
+ return;
+ }
+
+ if (_request.connectTimeout > 0) {
+ _request.id = setTimeout(function () {
+ if (!sseOpened) {
+ _clearState();
+ }
+ }, _request.connectTimeout);
+ }
+
+ _sse.onopen = function (event) {
+ _timeout(_request);
+ if (_request.logLevel === 'debug') {
+ atmosphere.util.debug("SSE successfully opened");
+ }
+
+ if (!_request.enableProtocol) {
+ if (!sseOpened) {
+ _open('opening', "sse", _request);
+ } else {
+ _open('re-opening', "sse", _request);
+ }
+ }
+ sseOpened = true;
+
+ if (_request.method === 'POST') {
+ _response.state = "messageReceived";
+ _sse.send(_request.data);
+ }
+ };
+
+ _sse.onmessage = function (message) {
+ _timeout(_request);
+
+ if (!_request.enableXDR && message.origin && message.origin !== window.location.protocol + "//" + window.location.host) {
+ atmosphere.util.log(_request.logLevel, ["Origin was not " + window.location.protocol + "//" + window.location.host]);
+ return;
+ }
+
+ _response.state = 'messageReceived';
+ _response.status = 200;
+
+ message = message.data;
+ var skipCallbackInvocation = _trackMessageSize(message, _request, _response);
+
+ // https://github.com/remy/polyfills/blob/master/EventSource.js
+ // Since we polling.
+ /* if (_sse.URL) {
+ _sse.interval = 100;
+ _sse.URL = _buildSSEUrl();
+ } */
+
+ if (!skipCallbackInvocation) {
+ _invokeCallback();
+ _response.responseBody = '';
+ _response.messages = [];
+ }
+ };
+
+ _sse.onerror = function (message) {
+ clearTimeout(_request.id);
+
+ if (_response.closedByClientTimeout) return;
+
+ _invokeClose(sseOpened);
+ _clearState();
+
+ if (_abordingConnection) {
+ atmosphere.util.log(_request.logLevel, ["SSE closed normally"]);
+ } else if (!sseOpened) {
+ _reconnectWithFallbackTransport("SSE failed. Downgrading to fallback transport and resending");
+ } else if (_request.reconnect && (_response.transport === 'sse')) {
+ if (_requestCount++ < _request.maxReconnectOnClose) {
+ _open('re-connecting', _request.transport, _request);
+ if (_request.reconnectInterval > 0) {
+ _request.reconnectId = setTimeout(function () {
+ _executeSSE(true);
+ }, _request.reconnectInterval);
+ } else {
+ _executeSSE(true);
+ }
+ _response.responseBody = "";
+ _response.messages = [];
+ } else {
+ atmosphere.util.log(_request.logLevel, ["SSE reconnect maximum try reached " + _requestCount]);
+ _onError(0, "maxReconnectOnClose reached");
+ }
+ }
+ };
+ }
+
+ /**
+ * Open web socket.
+ * Automatically use fallback transport if web socket can't be opened.
+ *
+ * @private
+ */
+ function _executeWebSocket(webSocketOpened) {
+
+ _response.transport = "websocket";
+
+ if (_request.enableProtocol && webSocketOpened) {
+ var time = atmosphere.util.now() - _request.ctime;
+ _request.lastTimestamp = Number(_request.stime) + Number(time);
+ }
+
+ var location = _buildWebSocketUrl(_request.url);
+ if (_request.logLevel === 'debug') {
+ atmosphere.util.debug("Invoking executeWebSocket");
+ atmosphere.util.debug("Using URL: " + location);
+ }
+
+ if (webSocketOpened && !_request.reconnect) {
+ if (_websocket != null) {
+ _clearState();
+ }
+ return;
+ }
+
+ _websocket = _getWebSocket(location);
+ if (_request.webSocketBinaryType != null) {
+ _websocket.binaryType = _request.webSocketBinaryType;
+ }
+
+ if (_request.connectTimeout > 0) {
+ _request.id = setTimeout(function () {
+ if (!webSocketOpened) {
+ var _message = {
+ code: 1002,
+ reason: "",
+ wasClean: false
+ };
+ _websocket.onclose(_message);
+ // Close it anyway
+ try {
+ _clearState();
+ } catch (e) {
+ }
+ return;
+ }
+
+ }, _request.connectTimeout);
+ }
+
+ _websocket.onopen = function (message) {
+ _timeout(_request);
+
+ if (_request.logLevel === 'debug') {
+ atmosphere.util.debug("Websocket successfully opened");
+ }
+
+ var reopening = webSocketOpened;
+
+ webSocketOpened = true;
+ if(_websocket != null) {
+ _websocket.webSocketOpened = webSocketOpened;
+ }
+
+ if (!_request.enableProtocol) {
+ if (reopening) {
+ _open('re-opening', "websocket", _request);
+ } else {
+ _open('opening', "websocket", _request);
+ }
+ }
+
+ if (_websocket != null) {
+ if (_request.method === 'POST') {
+ _response.state = "messageReceived";
+ _websocket.send(_request.data);
+ }
+ }
+ };
+
+ _websocket.onmessage = function (message) {
+ _timeout(_request);
+
+ _response.state = 'messageReceived';
+ _response.status = 200;
+
+ message = message.data;
+ var isString = typeof (message) === 'string';
+ if (isString) {
+ var skipCallbackInvocation = _trackMessageSize(message, _request, _response);
+ if (!skipCallbackInvocation) {
+ _invokeCallback();
+ _response.responseBody = '';
+ _response.messages = [];
+ }
+ } else {
+ if (!_handleProtocol(_request, message))
+ return;
+
+ _response.responseBody = message;
+ _invokeCallback();
+ _response.responseBody = null;
+ }
+ };
+
+ _websocket.onerror = function (message) {
+ clearTimeout(_request.id);
+ };
+
+ _websocket.onclose = function (message) {
+ clearTimeout(_request.id);
+ if (_response.state === 'closed')
+ return;
+
+ var reason = message.reason;
+ if (reason === "") {
+ switch (message.code) {
+ case 1000:
+ reason = "Normal closure; the connection successfully completed whatever purpose for which " + "it was created.";
+ break;
+ case 1001:
+ reason = "The endpoint is going away, either because of a server failure or because the "
+ + "browser is navigating away from the page that opened the connection.";
+ break;
+ case 1002:
+ reason = "The endpoint is terminating the connection due to a protocol error.";
+ break;
+ case 1003:
+ reason = "The connection is being terminated because the endpoint received data of a type it "
+ + "cannot accept (for example, a text-only endpoint received binary data).";
+ break;
+ case 1004:
+ reason = "The endpoint is terminating the connection because a data frame was received that " + "is too large.";
+ break;
+ case 1005:
+ reason = "Unknown: no status code was provided even though one was expected.";
+ break;
+ case 1006:
+ reason = "Connection was closed abnormally (that is, with no close frame being sent).";
+ break;
+ }
+ }
+
+ if (_request.logLevel === 'warn') {
+ atmosphere.util.warn("Websocket closed, reason: " + reason);
+ atmosphere.util.warn("Websocket closed, wasClean: " + message.wasClean);
+ }
+
+ if (_response.closedByClientTimeout) {
+ return;
+ }
+
+ _invokeClose(webSocketOpened);
+
+ _response.state = 'closed';
+
+ if (_abordingConnection) {
+ atmosphere.util.log(_request.logLevel, ["Websocket closed normally"]);
+ } else if (!webSocketOpened) {
+ _reconnectWithFallbackTransport("Websocket failed. Downgrading to Comet and resending");
+
+ } else if (_request.reconnect && _response.transport === 'websocket') {
+ _clearState();
+ if (_requestCount++ < _request.maxReconnectOnClose) {
+ _open('re-connecting', _request.transport, _request);
+ if (_request.reconnectInterval > 0) {
+ _request.reconnectId = setTimeout(function () {
+ _response.responseBody = "";
+ _response.messages = [];
+ _executeWebSocket(true);
+ }, _request.reconnectInterval);
+ } else {
+ _response.responseBody = "";
+ _response.messages = [];
+ _executeWebSocket(true);
+ }
+ } else {
+ atmosphere.util.log(_request.logLevel, ["Websocket reconnect maximum try reached " + _request.requestCount]);
+ if (_request.logLevel === 'warn') {
+ atmosphere.util.warn("Websocket error, reason: " + message.reason);
+ }
+ _onError(0, "maxReconnectOnClose reached");
+ }
+ }
+ };
+
+ if (_websocket.url === undefined) {
+ // Android 4.1 does not really support websockets and fails silently
+ _websocket.onclose({
+ reason: "Android 4.1 does not support websockets.",
+ wasClean: false
+ });
+ }
+ }
+
+ function _handleProtocol(request, message) {
+
+ // The first messages is always the uuid.
+ var b = true;
+
+ if (request.transport === 'polling') return b;
+
+ if (atmosphere.util.trim(message).length !== 0 && request.enableProtocol && request.firstMessage) {
+ request.firstMessage = false;
+ var messages = message.split(request.messageDelimiter);
+ var pos = messages.length === 2 ? 0 : 1;
+ request.uuid = atmosphere.util.trim(messages[pos]);
+ request.stime = atmosphere.util.trim(messages[pos + 1]);
+ b = false;
+ if (request.transport !== 'long-polling') {
+ _triggerOpen(request);
+ }
+ uuid = request.uuid;
+ } else if (request.enableProtocol && request.firstMessage) {
+ // In case we are getting some junk from IE
+ b = false;
+ } else {
+ _triggerOpen(request);
+ }
+ return b;
+ }
+
+ function _timeout(_request) {
+ clearTimeout(_request.id);
+ if (_request.timeout > 0 && _request.transport !== 'polling') {
+ _request.id = setTimeout(function () {
+ _onClientTimeout(_request);
+ _disconnect();
+ _clearState();
+ }, _request.timeout);
+ }
+ }
+
+ function _onClientTimeout(_request) {
+ _response.closedByClientTimeout = true;
+ _response.state = 'closedByClient';
+ _response.responseBody = "";
+ _response.status = 408;
+ _response.messages = [];
+ _invokeCallback();
+ }
+
+ function _onError(code, reason) {
+ _clearState();
+ clearTimeout(_request.id);
+ _response.state = 'error';
+ _response.reasonPhrase = reason;
+ _response.responseBody = "";
+ _response.status = code;
+ _response.messages = [];
+ _invokeCallback();
+ }
+
+ /**
+ * Track received message and make sure callbacks/functions are only invoked when the complete message has been received.
+ *
+ * @param message
+ * @param request
+ * @param response
+ */
+ function _trackMessageSize(message, request, response) {
+ if (!_handleProtocol(request, message))
+ return true;
+ if (message.length === 0)
+ return true;
+
+ if (request.trackMessageLength) {
+ // prepend partialMessage if any
+ message = response.partialMessage + message;
+
+ var messages = [];
+ var messageStart = message.indexOf(request.messageDelimiter);
+ while (messageStart !== -1) {
+ var str = atmosphere.util.trim(message.substring(0, messageStart));
+ var messageLength = +str;
+ if (isNaN(messageLength))
+ throw new Error('message length "' + str + '" is not a number');
+ messageStart += request.messageDelimiter.length;
+ if (messageStart + messageLength > message.length) {
+ // message not complete, so there is no trailing messageDelimiter
+ messageStart = -1;
+ } else {
+ // message complete, so add it
+ messages.push(message.substring(messageStart, messageStart + messageLength));
+ // remove consumed characters
+ message = message.substring(messageStart + messageLength, message.length);
+ messageStart = message.indexOf(request.messageDelimiter);
+ }
+ }
+
+ /* keep any remaining data */
+ response.partialMessage = message;
+
+ if (messages.length !== 0) {
+ response.responseBody = messages.join(request.messageDelimiter);
+ response.messages = messages;
+ return false;
+ } else {
+ response.responseBody = "";
+ response.messages = [];
+ return true;
+ }
+ } else {
+ response.responseBody = message;
+ }
+ return false;
+ }
+
+ /**
+ * Reconnect request with fallback transport.
+ * Used in case websocket can't be opened.
+ *
+ * @private
+ */
+ function _reconnectWithFallbackTransport(errorMessage) {
+ atmosphere.util.log(_request.logLevel, [errorMessage]);
+
+ if (typeof (_request.onTransportFailure) !== 'undefined') {
+ _request.onTransportFailure(errorMessage, _request);
+ } else if (typeof (atmosphere.util.onTransportFailure) !== 'undefined') {
+ atmosphere.util.onTransportFailure(errorMessage, _request);
+ }
+
+ _request.transport = _request.fallbackTransport;
+ var reconnectInterval = _request.connectTimeout === -1 ? 0 : _request.connectTimeout;
+ if (_request.reconnect && _request.transport !== 'none' || _request.transport == null) {
+ _request.method = _request.fallbackMethod;
+ _response.transport = _request.fallbackTransport;
+ _request.fallbackTransport = 'none';
+ if (reconnectInterval > 0) {
+ _request.reconnectId = setTimeout(function () {
+ _execute();
+ }, reconnectInterval);
+ } else {
+ _execute();
+ }
+ } else {
+ _onError(500, "Unable to reconnect with fallback transport");
+ }
+ }
+
+ /**
+ * Get url from request and attach headers to it.
+ *
+ * @param request {Object} request Request parameters, if undefined _request object will be used.
+ *
+ * @returns {Object} Request object, if undefined, _request object will be used.
+ * @private
+ */
+ function _attachHeaders(request, url) {
+ var rq = _request;
+ if ((request != null) && (typeof (request) !== 'undefined')) {
+ rq = request;
+ }
+
+ if (url == null) {
+ url = rq.url;
+ }
+
+ // If not enabled
+ if (!rq.attachHeadersAsQueryString)
+ return url;
+
+ // If already added
+ if (url.indexOf("X-Atmosphere-Framework") !== -1) {
+ return url;
+ }
+
+ url += (url.indexOf('?') !== -1) ? '&' : '?';
+ url += "X-Atmosphere-tracking-id=" + rq.uuid;
+ url += "&X-Atmosphere-Framework=" + version;
+ url += "&X-Atmosphere-Transport=" + rq.transport;
+
+ if (rq.trackMessageLength) {
+ url += "&X-Atmosphere-TrackMessageSize=" + "true";
+ }
+
+ if (rq.lastTimestamp != null) {
+ url += "&X-Cache-Date=" + rq.lastTimestamp;
+ } else {
+ url += "&X-Cache-Date=" + 0;
+ }
+
+ if (rq.contentType !== '') {
+ url += "&Content-Type=" + rq.contentType;
+ }
+
+ if (rq.enableProtocol) {
+ url += "&X-atmo-protocol=true";
+ }
+
+ atmosphere.util.each(rq.headers, function (name, value) {
+ var h = atmosphere.util.isFunction(value) ? value.call(this, rq, request, _response) : value;
+ if (h != null) {
+ url += "&" + encodeURIComponent(name) + "=" + encodeURIComponent(h);
+ }
+ });
+
+ return url;
+ }
+
+ function _triggerOpen(rq) {
+ if (!rq.isOpen) {
+ rq.isOpen = true;
+ _open('opening', rq.transport, rq);
+ } else if (rq.isReopen) {
+ rq.isReopen = false;
+ _open('re-opening', rq.transport, rq);
+ }
+ }
+
+ /**
+ * Execute ajax request.
+ *
+ * @param request {Object} request Request parameters, if undefined _request object will be used.
+ * @private
+ */
+ function _executeRequest(request) {
+ var rq = _request;
+ if ((request != null) || (typeof (request) !== 'undefined')) {
+ rq = request;
+ }
+
+ rq.lastIndex = 0;
+ rq.readyState = 0;
+
+ // CORS fake using JSONP
+ if ((rq.transport === 'jsonp') || ((rq.enableXDR) && (atmosphere.util.checkCORSSupport()))) {
+ _jsonp(rq);
+ return;
+ }
+
+ if (atmosphere.util.browser.msie && atmosphere.util.browser.version < 10) {
+ if ((rq.transport === 'streaming')) {
+ if (rq.enableXDR && window.XDomainRequest) {
+ _ieXDR(rq);
+ } else {
+ _ieStreaming(rq);
+ }
+ return;
+ }
+
+ if ((rq.enableXDR) && (window.XDomainRequest)) {
+ _ieXDR(rq);
+ return;
+ }
+ }
+
+ var reconnectF = function () {
+ rq.lastIndex = 0;
+ if (rq.reconnect && _requestCount++ < rq.maxReconnectOnClose) {
+ _open('re-connecting', request.transport, request);
+ _reconnect(ajaxRequest, rq, request.reconnectInterval);
+ } else {
+ _onError(0, "maxReconnectOnClose reached");
+ }
+ };
+
+ if (rq.force || (rq.reconnect && (rq.maxRequest === -1 || rq.requestCount++ < rq.maxRequest))) {
+ rq.force = false;
+
+ var ajaxRequest = atmosphere.util.xhr();
+ ajaxRequest.hasData = false;
+
+ _doRequest(ajaxRequest, rq, true);
+
+ if (rq.suspend) {
+ _activeRequest = ajaxRequest;
+ }
+
+ if (rq.transport !== 'polling') {
+ _response.transport = rq.transport;
+
+ ajaxRequest.onabort = function () {
+ _invokeClose(true);
+ };
+
+ ajaxRequest.onerror = function () {
+ _response.error = true;
+ try {
+ _response.status = XMLHttpRequest.status;
+ } catch (e) {
+ _response.status = 500;
+ }
+
+ if (!_response.status) {
+ _response.status = 500;
+ }
+ if (!_response.errorHandled) {
+ _clearState();
+ reconnectF();
+ }
+ };
+ }
+
+ ajaxRequest.onreadystatechange = function () {
+ if (_abordingConnection) {
+ return;
+ }
+
+ _response.error = null;
+ var skipCallbackInvocation = false;
+ var update = false;
+
+ if (rq.transport === 'streaming' && rq.readyState > 2 && ajaxRequest.readyState === 4) {
+ _clearState();
+ reconnectF();
+ return;
+ }
+
+ rq.readyState = ajaxRequest.readyState;
+
+ if (rq.transport === 'streaming' && ajaxRequest.readyState >= 3) {
+ update = true;
+ } else if (rq.transport === 'long-polling' && ajaxRequest.readyState === 4) {
+ update = true;
+ }
+ _timeout(_request);
+
+ if (rq.transport !== 'polling') {
+ // MSIE 9 and lower status can be higher than 1000, Chrome can be 0
+ var status = 200;
+ if (ajaxRequest.readyState === 4) {
+ status = ajaxRequest.status > 1000 ? 0 : ajaxRequest.status;
+ }
+
+ if (status >= 300 || status === 0) {
+ // Prevent onerror callback to be called
+ _response.errorHandled = true;
+ _clearState();
+ reconnectF();
+ return;
+ }
+
+ // Firefox incorrectly send statechange 0->2 when a reconnect attempt fails. The above checks ensure that onopen is not called for these
+ if ((!rq.enableProtocol || !request.firstMessage) && ajaxRequest.readyState === 2) {
+ _triggerOpen(rq);
+ }
+
+ } else if (ajaxRequest.readyState === 4) {
+ update = true;
+ }
+
+ if (update) {
+ var responseText = ajaxRequest.responseText;
+
+ if (atmosphere.util.trim(responseText).length === 0 && rq.transport === 'long-polling') {
+ // For browser that aren't support onabort
+ if (!ajaxRequest.hasData) {
+ reconnectF();
+ } else {
+ ajaxRequest.hasData = false;
+ }
+ return;
+ }
+ ajaxRequest.hasData = true;
+
+ _readHeaders(ajaxRequest, _request);
+
+ if (rq.transport === 'streaming') {
+ if (!atmosphere.util.browser.opera) {
+ var message = responseText.substring(rq.lastIndex, responseText.length);
+ skipCallbackInvocation = _trackMessageSize(message, rq, _response);
+
+ rq.lastIndex = responseText.length;
+ if (skipCallbackInvocation) {
+ return;
+ }
+ } else {
+ atmosphere.util.iterate(function () {
+ if (_response.status !== 500 && ajaxRequest.responseText.length > rq.lastIndex) {
+ try {
+ _response.status = ajaxRequest.status;
+ _response.headers = atmosphere.util.parseHeaders(ajaxRequest.getAllResponseHeaders());
+
+ _readHeaders(ajaxRequest, _request);
+
+ } catch (e) {
+ _response.status = 404;
+ }
+ _timeout(_request);
+
+ _response.state = "messageReceived";
+ var message = ajaxRequest.responseText.substring(rq.lastIndex);
+ rq.lastIndex = ajaxRequest.responseText.length;
+
+ skipCallbackInvocation = _trackMessageSize(message, rq, _response);
+ if (!skipCallbackInvocation) {
+ _invokeCallback();
+ }
+
+ _verifyStreamingLength(ajaxRequest, rq);
+ } else if (_response.status > 400) {
+ // Prevent replaying the last message.
+ rq.lastIndex = ajaxRequest.responseText.length;
+ return false;
+ }
+ }, 0);
+ }
+ } else {
+ skipCallbackInvocation = _trackMessageSize(responseText, rq, _response);
+ }
+
+ try {
+ _response.status = ajaxRequest.status;
+ _response.headers = atmosphere.util.parseHeaders(ajaxRequest.getAllResponseHeaders());
+
+ _readHeaders(ajaxRequest, rq);
+ } catch (e) {
+ _response.status = 404;
+ }
+
+ if (rq.suspend) {
+ _response.state = _response.status === 0 ? "closed" : "messageReceived";
+ } else {
+ _response.state = "messagePublished";
+ }
+
+ var isAllowedToReconnect = request.transport !== 'streaming' && request.transport !== 'polling';
+ if (isAllowedToReconnect && !rq.executeCallbackBeforeReconnect) {
+ _reconnect(ajaxRequest, rq, 0);
+ }
+
+ if (_response.responseBody.length !== 0 && !skipCallbackInvocation)
+ _invokeCallback();
+
+ if (isAllowedToReconnect && rq.executeCallbackBeforeReconnect) {
+ _reconnect(ajaxRequest, rq, 0);
+ }
+
+ _verifyStreamingLength(ajaxRequest, rq);
+ }
+ };
+
+ try {
+ ajaxRequest.send(rq.data);
+ _subscribed = true;
+ } catch (e) {
+ atmosphere.util.log(rq.logLevel, ["Unable to connect to " + rq.url]);
+ }
+
+ } else {
+ if (rq.logLevel === 'debug') {
+ atmosphere.util.log(rq.logLevel, ["Max re-connection reached."]);
+ }
+ _onError(0, "maxRequest reached");
+ }
+ }
+
+ /**
+ * Do ajax request.
+ *
+ * @param ajaxRequest Ajax request.
+ * @param request Request parameters.
+ * @param create If ajax request has to be open.
+ */
+ function _doRequest(ajaxRequest, request, create) {
+ // Prevent Android to cache request
+ var url = request.url;
+ if (request.dispatchUrl != null && request.method === 'POST') {
+ url += request.dispatchUrl;
+ }
+ url = _attachHeaders(request, url);
+ url = atmosphere.util.prepareURL(url);
+
+ if (create) {
+ ajaxRequest.open(request.method, url, request.async);
+ if (request.connectTimeout > 0) {
+ request.id = setTimeout(function () {
+ if (request.requestCount === 0) {
+ _clearState();
+ _prepareCallback("Connect timeout", "closed", 200, request.transport);
+ }
+ }, request.connectTimeout);
+ }
+ }
+
+ if (_request.withCredentials) {
+ if ("withCredentials" in ajaxRequest) {
+ ajaxRequest.withCredentials = true;
+ }
+ }
+
+ if (!_request.dropAtmosphereHeaders) {
+ ajaxRequest.setRequestHeader("X-Atmosphere-Framework", atmosphere.util.version);
+ ajaxRequest.setRequestHeader("X-Atmosphere-Transport", request.transport);
+ if (request.lastTimestamp != null) {
+ ajaxRequest.setRequestHeader("X-Cache-Date", request.lastTimestamp);
+ } else {
+ ajaxRequest.setRequestHeader("X-Cache-Date", 0);
+ }
+
+ if (request.trackMessageLength) {
+ ajaxRequest.setRequestHeader("X-Atmosphere-TrackMessageSize", "true");
+ }
+ ajaxRequest.setRequestHeader("X-Atmosphere-tracking-id", request.uuid);
+ }
+
+ if (request.contentType !== '') {
+ ajaxRequest.setRequestHeader("Content-Type", request.contentType);
+ }
+
+ atmosphere.util.each(request.headers, function (name, value) {
+ var h = atmosphere.util.isFunction(value) ? value.call(this, ajaxRequest, request, create, _response) : value;
+ if (h != null) {
+ ajaxRequest.setRequestHeader(name, h);
+ }
+ });
+ }
+
+ function _reconnect(ajaxRequest, request, reconnectInterval) {
+ if (request.reconnect || (request.suspend && _subscribed)) {
+ var status = 0;
+ if (ajaxRequest && ajaxRequest.readyState !== 0) {
+ status = ajaxRequest.status > 1000 ? 0 : ajaxRequest.status;
+ }
+ _response.status = status === 0 ? 204 : status;
+ _response.reason = status === 0 ? "Server resumed the connection or down." : "OK";
+
+ clearTimeout(request.id);
+ if (request.reconnectId) {
+ clearTimeout(request.reconnectId);
+ }
+
+ if (reconnectInterval > 0) {
+ // For whatever reason, never cancel a reconnect timeout as it is mandatory to reconnect.
+ _request.reconnectId = setTimeout(function () {
+ _executeRequest(request);
+ }, reconnectInterval);
+ } else {
+ _executeRequest(request);
+ }
+ }
+ }
+
+ function _tryingToReconnect(response) {
+ response.state = 're-connecting';
+ _invokeFunction(response);
+ }
+
+ function _ieXDR(request) {
+ if (request.transport !== "polling") {
+ _ieStream = _configureXDR(request);
+ _ieStream.open();
+ } else {
+ _configureXDR(request).open();
+ }
+ }
+
+ function _configureXDR(request) {
+ var rq = _request;
+ if ((request != null) && (typeof (request) !== 'undefined')) {
+ rq = request;
+ }
+
+ var transport = rq.transport;
+ var lastIndex = 0;
+ var xdr = new window.XDomainRequest();
+
+ var reconnect = function () {
+ if (rq.transport === "long-polling" && (rq.reconnect && (rq.maxRequest === -1 || rq.requestCount++ < rq.maxRequest))) {
+ xdr.status = 200;
+ _ieXDR(rq);
+ }
+ };
+
+ var rewriteURL = rq.rewriteURL || function (url) {
+ // Maintaining session by rewriting URL
+ // http://stackoverflow.com/questions/6453779/maintaining-session-by-rewriting-url
+ var match = /(?:^|;\s*)(JSESSIONID|PHPSESSID)=([^;]*)/.exec(document.cookie);
+
+ switch (match && match[1]) {
+ case "JSESSIONID":
+ return url.replace(/;jsessionid=[^\?]*|(\?)|$/, ";jsessionid=" + match[2] + "$1");
+ case "PHPSESSID":
+ return url.replace(/\?PHPSESSID=[^&]*&?|\?|$/, "?PHPSESSID=" + match[2] + "&").replace(/&$/, "");
+ }
+ return url;
+ };
+
+ // Handles open and message event
+ xdr.onprogress = function () {
+ handle(xdr);
+ };
+ // Handles error event
+ xdr.onerror = function () {
+ // If the server doesn't send anything back to XDR will fail with polling
+ if (rq.transport !== 'polling') {
+ _clearState();
+ if (_requestCount++ < rq.maxReconnectOnClose) {
+ if (rq.reconnectInterval > 0) {
+ rq.reconnectId = setTimeout(function () {
+ _open('re-connecting', request.transport, request);
+ _ieXDR(rq);
+ }, rq.reconnectInterval);
+ } else {
+ _open('re-connecting', request.transport, request);
+ _ieXDR(rq);
+ }
+ } else {
+ _onError(0, "maxReconnectOnClose reached");
+ }
+ }
+ };
+
+ // Handles close event
+ xdr.onload = function () {
+ };
+
+ var handle = function (xdr) {
+ clearTimeout(rq.id);
+ var message = xdr.responseText;
+
+ message = message.substring(lastIndex);
+ lastIndex += message.length;
+
+ if (transport !== 'polling') {
+ _timeout(rq);
+
+ var skipCallbackInvocation = _trackMessageSize(message, rq, _response);
+
+ if (transport === 'long-polling' && atmosphere.util.trim(message).length === 0)
+ return;
+
+ if (rq.executeCallbackBeforeReconnect) {
+ reconnect();
+ }
+
+ if (!skipCallbackInvocation) {
+ _prepareCallback(_response.responseBody, "messageReceived", 200, transport);
+ }
+
+ if (!rq.executeCallbackBeforeReconnect) {
+ reconnect();
+ }
+ }
+ };
+
+ return {
+ open: function () {
+ var url = rq.url;
+ if (rq.dispatchUrl != null) {
+ url += rq.dispatchUrl;
+ }
+ url = _attachHeaders(rq, url);
+ xdr.open(rq.method, rewriteURL(url));
+ if (rq.method === 'GET') {
+ xdr.send();
+ } else {
+ xdr.send(rq.data);
+ }
+
+ if (rq.connectTimeout > 0) {
+ rq.id = setTimeout(function () {
+ if (rq.requestCount === 0) {
+ _clearState();
+ _prepareCallback("Connect timeout", "closed", 200, rq.transport);
+ }
+ }, rq.connectTimeout);
+ }
+ },
+ close: function () {
+ xdr.abort();
+ }
+ };
+ }
+
+ function _ieStreaming(request) {
+ _ieStream = _configureIE(request);
+ _ieStream.open();
+ }
+
+ function _configureIE(request) {
+ var rq = _request;
+ if ((request != null) && (typeof (request) !== 'undefined')) {
+ rq = request;
+ }
+
+ var stop;
+ var doc = new window.ActiveXObject("htmlfile");
+
+ doc.open();
+ doc.close();
+
+ var url = rq.url;
+ if (rq.dispatchUrl != null) {
+ url += rq.dispatchUrl;
+ }
+
+ if (rq.transport !== 'polling') {
+ _response.transport = rq.transport;
+ }
+
+ return {
+ open: function () {
+ var iframe = doc.createElement("iframe");
+
+ url = _attachHeaders(rq);
+ if (rq.data !== '') {
+ url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(rq.data);
+ }
+
+ // Finally attach a timestamp to prevent Android and IE caching.
+ url = atmosphere.util.prepareURL(url);
+
+ iframe.src = url;
+ doc.body.appendChild(iframe);
+
+ // For the server to respond in a consistent format regardless of user agent, we polls response text
+ var cdoc = iframe.contentDocument || iframe.contentWindow.document;
+
+ stop = atmosphere.util.iterate(function () {
+ try {
+ if (!cdoc.firstChild) {
+ return;
+ }
+
+ var res = cdoc.body ? cdoc.body.lastChild : cdoc;
+ var readResponse = function () {
+ // Clones the element not to disturb the original one
+ var clone = res.cloneNode(true);
+
+ // If the last character is a carriage return or a line feed, IE ignores it in the innerText property
+ // therefore, we add another non-newline character to preserve it
+ clone.appendChild(cdoc.createTextNode("."));
+
+ var text = clone.innerText;
+
+ text = text.substring(0, text.length - 1);
+ return text;
+
+ };
+
+ // To support text/html content type
+ if (!cdoc.body || !cdoc.body.firstChild || cdoc.body.firstChild.nodeName.toLowerCase() !== "pre") {
+ // Injects a plaintext element which renders text without interpreting the HTML and cannot be stopped
+ // it is deprecated in HTML5, but still works
+ var head = cdoc.head || cdoc.getElementsByTagName("head")[0] || cdoc.documentElement || cdoc;
+ var script = cdoc.createElement("script");
+
+ script.text = "document.write('')";
+
+ head.insertBefore(script, head.firstChild);
+ head.removeChild(script);
+
+ // The plaintext element will be the response container
+ res = cdoc.body.lastChild;
+ }
+
+ if (rq.closed) {
+ rq.isReopen = true;
+ }
+
+ // Handles message and close event
+ stop = atmosphere.util.iterate(function () {
+ var text = readResponse();
+ if (text.length > rq.lastIndex) {
+ _timeout(_request);
+
+ _response.status = 200;
+ _response.error = null;
+
+ // Empties response every time that it is handled
+ res.innerText = "";
+ var skipCallbackInvocation = _trackMessageSize(text, rq, _response);
+ if (skipCallbackInvocation) {
+ return "";
+ }
+
+ _prepareCallback(_response.responseBody, "messageReceived", 200, rq.transport);
+ }
+
+ rq.lastIndex = 0;
+
+ if (cdoc.readyState === "complete") {
+ _invokeClose(true);
+ _open('re-connecting', rq.transport, rq);
+ if (rq.reconnectInterval > 0) {
+ rq.reconnectId = setTimeout(function () {
+ _ieStreaming(rq);
+ }, rq.reconnectInterval);
+ } else {
+ _ieStreaming(rq);
+ }
+ return false;
+ }
+ }, null);
+
+ return false;
+ } catch (err) {
+ _response.error = true;
+ _open('re-connecting', rq.transport, rq);
+ if (_requestCount++ < rq.maxReconnectOnClose) {
+ if (rq.reconnectInterval > 0) {
+ rq.reconnectId = setTimeout(function () {
+ _ieStreaming(rq);
+ }, rq.reconnectInterval);
+ } else {
+ _ieStreaming(rq);
+ }
+ } else {
+ _onError(0, "maxReconnectOnClose reached");
+ }
+ doc.execCommand("Stop");
+ doc.close();
+ return false;
+ }
+ });
+ },
+
+ close: function () {
+ if (stop) {
+ stop();
+ }
+
+ doc.execCommand("Stop");
+ _invokeClose(true);
+ }
+ };
+ }
+
+ /**
+ * Send message.
+ * Will be automatically dispatch to other connected.
+ *
+ * @param {Object, string} Message to send.
+ * @private
+ */
+ function _push(message) {
+
+ if (_localStorageService != null) {
+ _pushLocal(message);
+ } else if (_activeRequest != null || _sse != null) {
+ _pushAjaxMessage(message);
+ } else if (_ieStream != null) {
+ _pushIE(message);
+ } else if (_jqxhr != null) {
+ _pushJsonp(message);
+ } else if (_websocket != null) {
+ _pushWebSocket(message);
+ } else {
+ _onError(0, "No suspended connection available");
+ atmosphere.util.error("No suspended connection available. Make sure atmosphere.subscribe has been called and request.onOpen invoked before invoking this method");
+ }
+ }
+
+ function _pushOnClose(message, rq) {
+ if (!rq) {
+ rq = _getPushRequest(message);
+ }
+ rq.transport = "polling";
+ rq.method = "GET";
+ rq.async = false;
+ rq.withCredentials = false;
+ rq.reconnect = false;
+ rq.force = true;
+ rq.suspend = false;
+ rq.timeout = 1000;
+ _executeRequest(rq);
+ }
+
+ function _pushLocal(message) {
+ _localStorageService.send(message);
+ }
+
+ function _intraPush(message) {
+ // IE 9 will crash if not.
+ if (message.length === 0)
+ return;
+
+ try {
+ if (_localStorageService) {
+ _localStorageService.localSend(message);
+ } else if (_storageService) {
+ _storageService.signal("localMessage", atmosphere.util.stringifyJSON({
+ id: guid,
+ event: message
+ }));
+ }
+ } catch (err) {
+ atmosphere.util.error(err);
+ }
+ }
+
+ /**
+ * Send a message using currently opened ajax request (using http-streaming or long-polling).
+ *
+ * @param {string, Object} Message to send. This is an object, string message is saved in data member.
+ * @private
+ */
+ function _pushAjaxMessage(message) {
+ var rq = _getPushRequest(message);
+ _executeRequest(rq);
+ }
+
+ /**
+ * Send a message using currently opened ie streaming (using http-streaming or long-polling).
+ *
+ * @param {string, Object} Message to send. This is an object, string message is saved in data member.
+ * @private
+ */
+ function _pushIE(message) {
+ if (_request.enableXDR && atmosphere.util.checkCORSSupport()) {
+ var rq = _getPushRequest(message);
+ // Do not reconnect since we are pushing.
+ rq.reconnect = false;
+ _jsonp(rq);
+ } else {
+ _pushAjaxMessage(message);
+ }
+ }
+
+ /**
+ * Send a message using jsonp transport.
+ *
+ * @param {string, Object} Message to send. This is an object, string message is saved in data member.
+ * @private
+ */
+ function _pushJsonp(message) {
+ _pushAjaxMessage(message);
+ }
+
+ function _getStringMessage(message) {
+ var msg = message;
+ if (typeof (msg) === 'object') {
+ msg = message.data;
+ }
+ return msg;
+ }
+
+ /**
+ * Build request use to push message using method 'POST'
. Transport is defined as 'polling' and 'suspend' is set to false.
+ *
+ * @return {Object} Request object use to push message.
+ * @private
+ */
+ function _getPushRequest(message) {
+ var msg = _getStringMessage(message);
+
+ var rq = {
+ connected: false,
+ timeout: 60000,
+ method: 'POST',
+ url: _request.url,
+ contentType: _request.contentType,
+ headers: _request.headers,
+ reconnect: true,
+ callback: null,
+ data: msg,
+ suspend: false,
+ maxRequest: -1,
+ logLevel: 'info',
+ requestCount: 0,
+ withCredentials: _request.withCredentials,
+ async: _request.async,
+ transport: 'polling',
+ isOpen: true,
+ attachHeadersAsQueryString: true,
+ enableXDR: _request.enableXDR,
+ uuid: _request.uuid,
+ dispatchUrl: _request.dispatchUrl,
+ enableProtocol: false,
+ messageDelimiter: '|',
+ maxReconnectOnClose: _request.maxReconnectOnClose
+ };
+
+ if (typeof (message) === 'object') {
+ rq = atmosphere.util.extend(rq, message);
+ }
+
+ return rq;
+ }
+
+ /**
+ * Send a message using currently opened websocket.
+ *
+ */
+ function _pushWebSocket(message) {
+ var msg = atmosphere.util.isBinary(message) ? message : _getStringMessage(message);
+ var data;
+ try {
+ if (_request.dispatchUrl != null) {
+ data = _request.webSocketPathDelimiter + _request.dispatchUrl + _request.webSocketPathDelimiter + msg;
+ } else {
+ data = msg;
+ }
+
+ if (!_websocket.webSocketOpened) {
+ atmosphere.util.error("WebSocket not connected.");
+ return;
+ }
+
+ _websocket.send(data);
+
+ } catch (e) {
+ _websocket.onclose = function (message) {
+ };
+ _clearState();
+
+ _reconnectWithFallbackTransport("Websocket failed. Downgrading to Comet and resending " + message);
+ _pushAjaxMessage(message);
+ }
+ }
+
+ function _localMessage(message) {
+ var m = atmosphere.util.parseJSON(message);
+ if (m.id !== guid) {
+ if (typeof (_request.onLocalMessage) !== 'undefined') {
+ _request.onLocalMessage(m.event);
+ } else if (typeof (atmosphere.util.onLocalMessage) !== 'undefined') {
+ atmosphere.util.onLocalMessage(m.event);
+ }
+ }
+ }
+
+ function _prepareCallback(messageBody, state, errorCode, transport) {
+
+ _response.responseBody = messageBody;
+ _response.transport = transport;
+ _response.status = errorCode;
+ _response.state = state;
+
+ _invokeCallback();
+ }
+
+ function _readHeaders(xdr, request) {
+ if (!request.readResponsesHeaders && !request.enableProtocol) {
+ request.lastTimestamp = atmosphere.util.now();
+ request.uuid = guid;
+ return;
+ }
+
+ try {
+ var tempDate = xdr.getResponseHeader('X-Cache-Date');
+ if (tempDate && tempDate != null && tempDate.length > 0) {
+ request.lastTimestamp = tempDate.split(" ").pop();
+ }
+
+ var tempUUID = xdr.getResponseHeader('X-Atmosphere-tracking-id');
+ if (tempUUID && tempUUID != null) {
+ request.uuid = tempUUID.split(" ").pop();
+ }
+
+ // HOTFIX for firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=608735
+ if (request.headers) {
+ atmosphere.util.each(_request.headers, function (name) {
+ var v = xdr.getResponseHeader(name);
+ if (v) {
+ _response.headers[name] = v;
+ }
+ });
+ }
+ } catch (e) {
+ }
+ }
+
+ function _invokeFunction(response) {
+ _f(response, _request);
+ // Global
+ _f(response, atmosphere.util);
+ }
+
+ function _f(response, f) {
+ switch (response.state) {
+ case "messageReceived":
+ _requestCount = 0;
+ if (typeof (f.onMessage) !== 'undefined')
+ f.onMessage(response);
+ break;
+ case "error":
+ if (typeof (f.onError) !== 'undefined')
+ f.onError(response);
+ break;
+ case "opening":
+ delete _request.closed;
+ if (typeof (f.onOpen) !== 'undefined')
+ f.onOpen(response);
+ break;
+ case "messagePublished":
+ if (typeof (f.onMessagePublished) !== 'undefined')
+ f.onMessagePublished(response);
+ break;
+ case "re-connecting":
+ if (typeof (f.onReconnect) !== 'undefined')
+ f.onReconnect(_request, response);
+ break;
+ case "closedByClient":
+ if (typeof (f.onClientTimeout) !== 'undefined')
+ f.onClientTimeout(_request);
+ break;
+ case "re-opening":
+ delete _request.closed;
+ if (typeof (f.onReopen) !== 'undefined')
+ f.onReopen(_request, response);
+ break;
+ case "fail-to-reconnect":
+ if (typeof (f.onFailureToReconnect) !== 'undefined')
+ f.onFailureToReconnect(_request, response);
+ break;
+ case "unsubscribe":
+ case "closed":
+ var closed = typeof (_request.closed) !== 'undefined' ? _request.closed : false;
+ if (typeof (f.onClose) !== 'undefined' && !closed)
+ f.onClose(response);
+ _request.closed = true;
+ break;
+ }
+ }
+
+ function _invokeClose(wasOpen) {
+ if (_response.state !== 'closed') {
+ _response.state = 'closed';
+ _response.responseBody = "";
+ _response.messages = [];
+ _response.status = !wasOpen ? 501 : 200;
+ _invokeCallback();
+ }
+ }
+
+ /**
+ * Invoke request callbacks.
+ *
+ * @private
+ */
+ function _invokeCallback() {
+ var call = function (index, func) {
+ func(_response);
+ };
+
+ if (_localStorageService == null && _localSocketF != null) {
+ _localSocketF(_response.responseBody);
+ }
+
+ _request.reconnect = _request.mrequest;
+
+ var isString = typeof (_response.responseBody) === 'string';
+ var messages = (isString && _request.trackMessageLength) ? (_response.messages.length > 0 ? _response.messages : ['']) : new Array(
+ _response.responseBody);
+ for (var i = 0; i < messages.length; i++) {
+
+ if (messages.length > 1 && messages[i].length === 0) {
+ continue;
+ }
+ _response.responseBody = (isString) ? atmosphere.util.trim(messages[i]) : messages[i];
+
+ if (_localStorageService == null && _localSocketF != null) {
+ _localSocketF(_response.responseBody);
+ }
+
+ if (_response.responseBody.length === 0 && _response.state === "messageReceived") {
+ continue;
+ }
+
+ _invokeFunction(_response);
+
+ // Invoke global callbacks
+ if (callbacks.length > 0) {
+ if (_request.logLevel === 'debug') {
+ atmosphere.util.debug("Invoking " + callbacks.length + " global callbacks: " + _response.state);
+ }
+ try {
+ atmosphere.util.each(callbacks, call);
+ } catch (e) {
+ atmosphere.util.log(_request.logLevel, ["Callback exception" + e]);
+ }
+ }
+
+ // Invoke request callback
+ if (typeof (_request.callback) === 'function') {
+ if (_request.logLevel === 'debug') {
+ atmosphere.util.debug("Invoking request callbacks");
+ }
+ try {
+ _request.callback(_response);
+ } catch (e) {
+ atmosphere.util.log(_request.logLevel, ["Callback exception" + e]);
+ }
+ }
+ }
+ }
+
+ this.subscribe = function (options) {
+ _subscribe(options);
+ _execute();
+ };
+
+ this.execute = function () {
+ _execute();
+ };
+
+ this.close = function () {
+ _close();
+ };
+
+ this.disconnect = function () {
+ _disconnect();
+ };
+
+ this.getUrl = function () {
+ return _request.url;
+ };
+
+ this.push = function (message, dispatchUrl) {
+ if (dispatchUrl != null) {
+ var originalDispatchUrl = _request.dispatchUrl;
+ _request.dispatchUrl = dispatchUrl;
+ _push(message);
+ _request.dispatchUrl = originalDispatchUrl;
+ } else {
+ _push(message);
+ }
+ };
+
+ this.getUUID = function () {
+ return _request.uuid;
+ };
+
+ this.pushLocal = function (message) {
+ _intraPush(message);
+ };
+
+ this.enableProtocol = function (message) {
+ return _request.enableProtocol;
+ };
+
+ this.request = _request;
+ this.response = _response;
+ }
+ };
+
+ atmosphere.subscribe = function (url, callback, request) {
+ if (typeof (callback) === 'function') {
+ atmosphere.addCallback(callback);
+ }
+
+ if (typeof (url) !== "string") {
+ request = url;
+ } else {
+ request.url = url;
+ }
+
+ var rq = new atmosphere.AtmosphereRequest(request);
+ rq.execute();
+
+ requests[requests.length] = rq;
+ return rq;
+ };
+
+ atmosphere.unsubscribe = function () {
+ if (requests.length > 0) {
+ var requestsClone = [].concat(requests);
+ for (var i = 0; i < requestsClone.length; i++) {
+ var rq = requestsClone[i];
+ rq.close();
+ clearTimeout(rq.response.request.id);
+ }
+ }
+ requests = [];
+ callbacks = [];
+ };
+
+ atmosphere.unsubscribeUrl = function (url) {
+ var idx = -1;
+ if (requests.length > 0) {
+ for (var i = 0; i < requests.length; i++) {
+ var rq = requests[i];
+
+ // Suppose you can subscribe once to an url
+ if (rq.getUrl() === url) {
+ rq.close();
+ clearTimeout(rq.response.request.id);
+ idx = i;
+ break;
+ }
+ }
+ }
+ if (idx >= 0) {
+ requests.splice(idx, 1);
+ }
+ };
+
+ atmosphere.addCallback = function (func) {
+ if (atmosphere.util.inArray(func, callbacks) === -1) {
+ callbacks.push(func);
+ }
+ };
+
+ atmosphere.removeCallback = function (func) {
+ var index = atmosphere.util.inArray(func, callbacks);
+ if (index !== -1) {
+ callbacks.splice(index, 1);
+ }
+ };
+
+ atmosphere.util = {
+ browser: {},
+
+ parseHeaders: function (headerString) {
+ var match, rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, headers = {};
+ while (match = rheaders.exec(headerString)) {
+ headers[match[1]] = match[2];
+ }
+ return headers;
+ },
+
+ now: function () {
+ return new Date().getTime();
+ },
+
+ isArray: function (array) {
+ return Object.prototype.toString.call(array) === "[object Array]";
+ },
+
+ inArray: function (elem, array) {
+ if (!Array.prototype.indexOf) {
+ var len = array.length;
+ for (var i = 0; i < len; ++i) {
+ if (array[i] === elem) {
+ return i;
+ }
+ }
+ return -1;
+ }
+ return array.indexOf(elem);
+ },
+
+ isBinary: function (data) {
+ // True if data is an instance of Blob, ArrayBuffer or ArrayBufferView
+ return /^\[object\s(?:Blob|ArrayBuffer|.+Array)\]$/.test(Object.prototype.toString.call(data));
+ },
+
+ isFunction: function (fn) {
+ return Object.prototype.toString.call(fn) === "[object Function]";
+ },
+
+ getAbsoluteURL: function (url) {
+ var div = document.createElement("div");
+
+ // Uses an innerHTML property to obtain an absolute URL
+ div.innerHTML = '';
+
+ // encodeURI and decodeURI are needed to normalize URL between IE and non-IE,
+ // since IE doesn't encode the href property value and return it - http://jsfiddle.net/Yq9M8/1/
+ return encodeURI(decodeURI(div.firstChild.href));
+ },
+
+ prepareURL: function (url) {
+ // Attaches a time stamp to prevent caching
+ var ts = atmosphere.util.now();
+ var ret = url.replace(/([?&])_=[^&]*/, "$1_=" + ts);
+
+ return ret + (ret === url ? (/\?/.test(url) ? "&" : "?") + "_=" + ts : "");
+ },
+
+ trim: function (str) {
+ if (!String.prototype.trim) {
+ return str.toString().replace(/(?:(?:^|\n)\s+|\s+(?:$|\n))/g, "").replace(/\s+/g, " ");
+ } else {
+ return str.toString().trim();
+ }
+ },
+
+ param: function (params) {
+ var prefix, s = [];
+
+ function add(key, value) {
+ value = atmosphere.util.isFunction(value) ? value() : (value == null ? "" : value);
+ s.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));
+ }
+
+ function buildParams(prefix, obj) {
+ var name;
+
+ if (atmosphere.util.isArray(obj)) {
+ atmosphere.util.each(obj, function (i, v) {
+ if (/\[\]$/.test(prefix)) {
+ add(prefix, v);
+ } else {
+ buildParams(prefix + "[" + (typeof v === "object" ? i : "") + "]", v);
+ }
+ });
+ } else if (Object.prototype.toString.call(obj) === "[object Object]") {
+ for (name in obj) {
+ buildParams(prefix + "[" + name + "]", obj[name]);
+ }
+ } else {
+ add(prefix, obj);
+ }
+ }
+
+ for (prefix in params) {
+ buildParams(prefix, params[prefix]);
+ }
+
+ return s.join("&").replace(/%20/g, "+");
+ },
+
+ storage: !!(window.localStorage && window.StorageEvent),
+
+ iterate: function (fn, interval) {
+ var timeoutId;
+
+ // Though the interval is 0 for real-time application, there is a delay between setTimeout calls
+ // For detail, see https://developer.mozilla.org/en/window.setTimeout#Minimum_delay_and_timeout_nesting
+ interval = interval || 0;
+
+ (function loop() {
+ timeoutId = setTimeout(function () {
+ if (fn() === false) {
+ return;
+ }
+
+ loop();
+ }, interval);
+ })();
+
+ return function () {
+ clearTimeout(timeoutId);
+ };
+ },
+
+ each: function (obj, callback, args) {
+ if (!obj) return;
+ var value, i = 0, length = obj.length, isArray = atmosphere.util.isArray(obj);
+
+ if (args) {
+ if (isArray) {
+ for (; i < length; i++) {
+ value = callback.apply(obj[i], args);
+
+ if (value === false) {
+ break;
+ }
+ }
+ } else {
+ for (i in obj) {
+ value = callback.apply(obj[i], args);
+
+ if (value === false) {
+ break;
+ }
+ }
+ }
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if (isArray) {
+ for (; i < length; i++) {
+ value = callback.call(obj[i], i, obj[i]);
+
+ if (value === false) {
+ break;
+ }
+ }
+ } else {
+ for (i in obj) {
+ value = callback.call(obj[i], i, obj[i]);
+
+ if (value === false) {
+ break;
+ }
+ }
+ }
+ }
+
+ return obj;
+ },
+
+ extend: function (target) {
+ var i, options, name;
+
+ for (i = 1; i < arguments.length; i++) {
+ if ((options = arguments[i]) != null) {
+ for (name in options) {
+ target[name] = options[name];
+ }
+ }
+ }
+
+ return target;
+ },
+ on: function (elem, type, fn) {
+ if (elem.addEventListener) {
+ elem.addEventListener(type, fn, false);
+ } else if (elem.attachEvent) {
+ elem.attachEvent("on" + type, fn);
+ }
+ },
+ off: function (elem, type, fn) {
+ if (elem.removeEventListener) {
+ elem.removeEventListener(type, fn, false);
+ } else if (elem.detachEvent) {
+ elem.detachEvent("on" + type, fn);
+ }
+ },
+
+ log: function (level, args) {
+ if (window.console) {
+ var logger = window.console[level];
+ if (typeof logger === 'function') {
+ logger.apply(window.console, args);
+ }
+ }
+ },
+
+ warn: function () {
+ atmosphere.util.log('warn', arguments);
+ },
+
+ info: function () {
+ atmosphere.util.log('info', arguments);
+ },
+
+ debug: function () {
+ atmosphere.util.log('debug', arguments);
+ },
+
+ error: function () {
+ atmosphere.util.log('error', arguments);
+ },
+ xhr: function () {
+ try {
+ return new window.XMLHttpRequest();
+ } catch (e1) {
+ try {
+ return new window.ActiveXObject("Microsoft.XMLHTTP");
+ } catch (e2) {
+ }
+ }
+ },
+ parseJSON: function (data) {
+ return !data ? null : window.JSON && window.JSON.parse ? window.JSON.parse(data) : new Function("return " + data)();
+ },
+ // http://github.com/flowersinthesand/stringifyJSON
+ stringifyJSON: function (value) {
+ var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, meta = {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"': '\\"',
+ '\\': '\\\\'
+ };
+
+ function quote(string) {
+ return '"' + string.replace(escapable, function (a) {
+ var c = meta[a];
+ return typeof c === "string" ? c : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
+ }) + '"';
+ }
+
+ function f(n) {
+ return n < 10 ? "0" + n : n;
+ }
+
+ return window.JSON && window.JSON.stringify ? window.JSON.stringify(value) : (function str(key, holder) {
+ var i, v, len, partial, value = holder[key], type = typeof value;
+
+ if (value && typeof value === "object" && typeof value.toJSON === "function") {
+ value = value.toJSON(key);
+ type = typeof value;
+ }
+
+ switch (type) {
+ case "string":
+ return quote(value);
+ case "number":
+ return isFinite(value) ? String(value) : "null";
+ case "boolean":
+ return String(value);
+ case "object":
+ if (!value) {
+ return "null";
+ }
+
+ switch (Object.prototype.toString.call(value)) {
+ case "[object Date]":
+ return isFinite(value.valueOf()) ? '"' + value.getUTCFullYear() + "-" + f(value.getUTCMonth() + 1) + "-"
+ + f(value.getUTCDate()) + "T" + f(value.getUTCHours()) + ":" + f(value.getUTCMinutes()) + ":" + f(value.getUTCSeconds())
+ + "Z" + '"' : "null";
+ case "[object Array]":
+ len = value.length;
+ partial = [];
+ for (i = 0; i < len; i++) {
+ partial.push(str(i, value) || "null");
+ }
+
+ return "[" + partial.join(",") + "]";
+ default:
+ partial = [];
+ for (i in value) {
+ if (hasOwn.call(value, i)) {
+ v = str(i, value);
+ if (v) {
+ partial.push(quote(i) + ":" + v);
+ }
+ }
+ }
+
+ return "{" + partial.join(",") + "}";
+ }
+ }
+ })("", {
+ "": value
+ });
+ },
+
+ checkCORSSupport: function () {
+ if (atmosphere.util.browser.msie && !window.XDomainRequest) {
+ return true;
+ } else if (atmosphere.util.browser.opera && atmosphere.util.browser.version < 12.0) {
+ return true;
+ }
+
+ // KreaTV 4.1 -> 4.4
+ else if (atmosphere.util.trim(navigator.userAgent).slice(0, 16) === "KreaTVWebKit/531") {
+ return true;
+ }
+ // KreaTV 3.8
+ else if (atmosphere.util.trim(navigator.userAgent).slice(-7).toLowerCase() === "kreatel") {
+ return true;
+ }
+
+ // Force Android to use CORS as some version like 2.2.3 fail otherwise
+ var ua = navigator.userAgent.toLowerCase();
+ var isAndroid = ua.indexOf("android") > -1;
+ if (isAndroid) {
+ return true;
+ }
+ return false;
+ }
+ };
+
+ guid = atmosphere.util.now();
+
+ // Browser sniffing
+ (function () {
+ var ua = navigator.userAgent.toLowerCase(),
+ match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
+ /(webkit)[ \/]([\w.]+)/.exec(ua) ||
+ /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
+ /(msie) ([\w.]+)/.exec(ua) ||
+ ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
+ [];
+
+ atmosphere.util.browser[match[1] || ""] = true;
+ atmosphere.util.browser.version = match[2] || "0";
+
+ // The storage event of Internet Explorer and Firefox 3 works strangely
+ if (atmosphere.util.browser.msie || (atmosphere.util.browser.mozilla && atmosphere.util.browser.version.split(".")[0] === "1")) {
+ atmosphere.util.storage = false;
+ }
+ })();
+
+ atmosphere.util.on(window, "unload", function (event) {
+ atmosphere.unsubscribe();
+ });
+
+ // Pressing ESC key in Firefox kills the connection
+ atmosphere.util.on(window, "keypress", function (event) {
+ if (event.which === 27) {
+ event.preventDefault();
+ }
+ });
+
+ atmosphere.util.on(window, "offline", function () {
+ atmosphere.unsubscribe();
+ });
+ window.atmosphere = atmosphere;
+})();
+/* jshint eqnull:true, noarg:true, noempty:true, eqeqeq:true, evil:true, laxbreak:true, undef:true, browser:true, indent:false, maxerr:50 */
diff --git a/websocket/pom.xml b/websocket/pom.xml
index 2e3c555f8..119b087b4 100644
--- a/websocket/pom.xml
+++ b/websocket/pom.xml
@@ -46,4 +46,11 @@
endpoint-singleton
websocket-vs-rest-payload
+
+
+ org.javaee7
+ test-utils
+ ${project.version}
+
+