├── .gitignore ├── .jshintrc ├── .travis.yml ├── CONTRIBUTING.md ├── Gruntfile.js ├── README.md ├── custom_build_help.txt ├── external ├── crypto │ ├── LICENSE │ └── sjcl.js └── uuid │ ├── license.md │ └── uuid.js ├── package.json ├── src ├── aerogear.core.js ├── crypto │ └── aerogear.crypto.js ├── data-manager │ ├── adapters │ │ ├── base.js │ │ ├── indexeddb.js │ │ ├── memory.js │ │ ├── session-local.js │ │ └── websql.js │ └── aerogear.datamanager.js ├── diff-sync │ ├── aerogear.diff-sync-client.js │ ├── aerogear.diff-sync-engine.js │ └── engine-adapters │ │ ├── diff-match-patch.js │ │ └── json-patch.js ├── notifier │ ├── adapters │ │ ├── base.js │ │ ├── mqttws.js │ │ ├── simplePush.js │ │ ├── stompws.js │ │ └── vertx.js │ └── aerogear.notifier.js ├── simplepush │ └── aerogear.simplepush.js └── unifiedpush │ └── aerogear.unifiedpush.js └── tests ├── .jshintrc ├── polyfill └── bind-polyfill.js ├── unit ├── all.html ├── crypto │ ├── aerogear.crypto.html │ └── aerogear.crypto.js ├── data-manager-indexeddb │ ├── data-manager-indexeddb-encrypted.html │ ├── data-manager-indexeddb-encrypted.js │ ├── data-manager-indexeddb.html │ └── data-manager-indexeddb.js ├── data-manager-websql │ ├── data-manager-websql-encrypted.html │ ├── data-manager-websql-encrypted.js │ ├── data-manager-websql.html │ └── data-manager-websql.js ├── data-manager │ ├── data-manager-fallback-feature-detect.html │ ├── data-manager-fallback-feature-detect.js │ ├── data-manager-fallback.html │ ├── data-manager-fallback.js │ ├── data-manager-memory-async.html │ ├── data-manager-memory-async.js │ ├── data-manager-sessionLocal-async-encrypted.html │ ├── data-manager-sessionLocal-async-encrypted.js │ ├── data-manager-sessionLocal-async.html │ └── data-manager-sessionLocal-async.js ├── index.html ├── notifier │ ├── mqttws.js │ ├── notifier.html │ ├── simplepush.js │ ├── stompws.js │ └── vertx.js ├── simplepush │ ├── simplepush.html │ └── simplepush.js ├── sync │ ├── diff-sync-client.html │ ├── diff-sync-client.js │ ├── diff-sync-engine-dmp.html │ ├── diff-sync-engine-dmp.js │ ├── diff-sync-engine-json-patch.html │ └── diff-sync-engine-json-patch.js └── unifiedpush │ ├── unifiedpush.html │ └── unifiedpush.js └── vendor ├── diff_match_patch_uncompressed.js ├── jquery-2.0.3.min.js ├── json-patch-duplex.min.js ├── mqttws31.js ├── promise-0.1.1.js ├── qunit-composite.css ├── qunit-composite.js ├── qunit.css ├── qunit.js ├── sinon-1.9.0.js ├── sockjs.js ├── stomp.js └── vertxbus.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.yaml 2 | dist/ 3 | docs/ 4 | node_modules/ 5 | aerogear-js-integration/ -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "browser": true, 3 | "curly": true, 4 | "eqeqeq": true, 5 | "latedef": true, 6 | "loopfunc": true, 7 | "noarg": true, 8 | "noempty": true, 9 | "undef": true, 10 | "trailing": true, 11 | "globals": { 12 | "jQuery": true, 13 | "AeroGear": true, 14 | "Promise": true, 15 | "require": true, 16 | "uuid": true, 17 | "vertx": true, 18 | "SockJS": true, 19 | "Stomp": true, 20 | "Paho": true, 21 | "File": true, 22 | "sjcl": true, 23 | "crypto": true, 24 | "diff_match_patch": true, 25 | "jsonpatch": true 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | before_script: 5 | - git clone https://github.com/aerogear/aerogear-js-integration.git 6 | - npm install -g grunt-cli 7 | - pushd aerogear-js-integration; npm install; popd 8 | script: 9 | - grunt travis --verbose 10 | notifications: 11 | irc: "irc.freenode.org#aerogear" 12 | branches: 13 | only: 14 | - master 15 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to AeroGear 2 | 3 | As an open source project we are always on the lookout for community members that want to step up and help with the project. There are lots of ways to help, and this article will help you get started! 4 | 5 | ## Help Others 6 | 7 | Have experience with mobile or HTML5 based technologies? Interested in sharing your knowledge? 8 | 9 | If you've got ideas for the project, or want to discuss ways to improve AeroGear, join the [aerogear-dev](https://lists.jboss.org/mailman/listinfo/aerogear-dev) mailing list and share what your working on, or struggling with. For more immediate gratification join the IRC channel (#aerogear). There are are usually a good bunch of people hanging out there. Ask your questions, share your experience, or just make jokes :-) 10 | 11 | ## Found An Issue? 12 | Have you run into an issue with one of our examples, sites, or have a suggestion? The best place to start is probably talking about it in the forums or IRC. If you've already done that, or just want to create the issue please do at [https://issues.jboss.org/browse/AGJS](https://issues.jboss.org/browse/AGJS). 13 | 14 | We only ask that you review our [AeroGear Jira Usage and Guidelines](http://aerogear.org/docs/guides/JIRAUsage) and use your noodle! Help the community help you :-) 15 | 16 | ## Fix an Issue! 17 | 18 | Your ready to start taking action, dig into an issue, and update some code! Excellent!! 19 | 20 | ### Communicate 21 | 22 | The first recommended step is to communicate with the project team. This way you can get any advice, or feedback on your plans. 23 | 24 | The best ways to do this are through: 25 | 26 | * IRC: #aerogear @ irc.freenode.net 27 | * Developer mailing list: [aerogear-dev](https://lists.jboss.org/mailman/listinfo/aerogear-dev) 28 | * Project meetings: [AeroGear Community Calendar](https://www.jboss.org/aerogear/AeroGearCalendar) 29 | 30 | ### Pick your poison 31 | 32 | If what you want to work on is already in jira, then ask to have it assigned to you (see communicate section above). If there is no jira, go ahead and make one as described above, and in the [AeroGear Jira Usage and Guidelines](http://aerogear.org/docs/guides/JIRAUsage). 33 | 34 | ### Sign the JBoss community license agreement 35 | 36 | This is pretty standard stuff for open source. Just use your JBoss user, and pick AeroGear for the project here: 37 | 38 | [https://cla.jboss.org](https://cla.jboss.org) 39 | 40 | ### Follow AeroGear.js's coding conventions and style 41 | 42 | Our coding style comes from jQuery's style. Here is a link to their [style guide](http://contribute.jquery.org/style-guide/js/) 43 | 44 | Our grunt build will run JSHint to let you know if most things are in line. 45 | 46 | Most editors will have support for the provided `.jshintrc` file. This will help during development. 47 | 48 | In addition, we have added a grunt watch task which executes the corresponding unit tests whenever a file is added, changed or deleted. 49 | 50 | ### Follow AeroGear's github workflow* 51 | 52 | Follow our document that will walk you through how to work with GitHub, and the AeroGear repositories: [AeroGear GitHub Workflow & Instructions](http://aerogear.org/docs/guides/GitHubWorkflow). 53 | 54 | ### Get Fame and Applause from Everyone!! 55 | 56 | Talk about your work, create a blog, or tweet about it using the #AeroGear hashtag! 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aerogear-js [![Build Status](https://travis-ci.org/aerogear/aerogear-js.png)](https://travis-ci.org/aerogear/aerogear-js) [![devDependency Status](https://david-dm.org/aerogear/aerogear-js/dev-status.png)](https://david-dm.org/aerogear/aerogear-js#info=devDependencies) # 2 | 3 | JavaScript client library implementation for AeroGear. Eventually, this will include API's for persistence, security, data synchronization and more. For more information and downloads, visit [AeroGear.org](http://aerogear.org/javascript). 4 | 5 | | | Project Info | 6 | | --------------- | ------------- | 7 | | License: | Apache License, Version 2.0 | 8 | | Build: | NPM, Grunt | 9 | | Documentation: | https://aerogear.org/docs/specs/aerogear-js/ | 10 | | Issue tracker: | https://issues.jboss.org/browse/AGJS | 11 | | Mailing lists: | [aerogear-users](http://aerogear-users.1116366.n5.nabble.com/) ([subscribe](https://lists.jboss.org/mailman/listinfo/aerogear-users)) | 12 | | | [aerogear-dev](http://aerogear-dev.1069024.n5.nabble.com/) ([subscribe](https://lists.jboss.org/mailman/listinfo/aerogear-dev)) | 13 | 14 | ## Authorization 15 | - - - 16 | 17 | _This api has been deprecated and removed as of 2.1.0. To use it you will need the latest 1.x release, which can be [found here](https://github.com/aerogear/aerogear-js/tree/1.x))_ 18 | 19 | ## Auth 20 | - - - 21 | 22 | _This api has been deprecated. To use it you will need the latest 1.x release, which can be [found here](https://github.com/aerogear/aerogear-js/tree/1.x))_ 23 | 24 | ## Crypto 25 | - - - 26 | 27 | The AeroGear.Crypto namespace provides a straightforward API to provide an easy to use cryptography interface for data encryption and decryption. 28 | 29 | See the [Crypto API Docs](http://aerogear.org/docs/specs/aerogear-js/AeroGear.Crypto.html) for more info. 30 | 31 | ## DataManager 32 | - - - 33 | 34 | A collection of data connections (stores) and their corresponding data models. This object provides a standard way to interact with client side data no matter the data format or storage mechanism used. 35 | 36 | See the [DataManager API docs](http://aerogear.org/docs/specs/aerogear-js/AeroGear.DataManager.html) for more info. 37 | 38 | ## Notifier 39 | - - - 40 | 41 | _This api has been deprecated as of 2.1.0 and will be removed in a future version_ 42 | 43 | Notifier is a collection of adapters which provide a unified or similar API for interacting with different messaging services and protocols. 44 | 45 | See the [Notifier API docs](http://aerogear.org/docs/specs/aerogear-js/AeroGear.Notifier.html) for more info. 46 | 47 | ## Pipeline 48 | - - - 49 | 50 | _This api has been deprecated. To use it you will need the latest 1.x release, which can be [found here](https://github.com/aerogear/aerogear-js/tree/1.x))_ 51 | 52 | ## SimplePushClient 53 | - - - 54 | 55 | SimplePushClient is a client implementation and polyfill for the Mozilla SimplePush specification. SimplePush allows for simple push notification support in web, as well as Firefox OS, applications. This implementation does differ slightly from the specification in that it only works in applications that are "online" and active in the browser. This implementation also supports connecting to both Mozilla's SimplePush server as well as the AeroGear project's server. 56 | 57 | See the [SimplePushClient API docs](http://aerogear.org/docs/specs/aerogear-js/AeroGear.SimplePushClient.html) for more info. Also, please see the [Mozilla SimplePush specification](https://wiki.mozilla.org/WebAPI/SimplePush) for more info on SimplePush. 58 | 59 | ## Diff Sync 60 | - - - 61 | 62 | The Diff Sync client and server are based on an implementation of Google's [Differential Synchonrization](http://research.google.com/pubs/pub35605.html) by Neil Fraser. 63 | 64 | The [DiffSyncClient](http://aerogear.org/docs/specs/aerogear-js/AeroGear.DiffSyncClient.html) connects to the [AeroGear Sync Server](https://github.com/aerogear/aerogear-sync-server) 65 | 66 | The [DiffSyncEngine](http://aerogear.org/docs/specs/aerogear-js/AeroGear.DiffSyncEngine.html) is responsible for the algorithm logic - there are two adapters available: [JSON Patch](http://aerogear.org/docs/specs/aerogear-js/AeroGear.DiffSyncEngine.adapters.jsonPatch.html) and [DiffMatchPatch](http://aerogear.org/docs/specs/aerogear-js/AeroGear.DiffSyncEngine.adapters.diffMatchPatch.html). 67 | 68 | 69 | ## UnifiedPushClient 70 | - - - 71 | 72 | UnifiedPushClient is used in conjunction with AeroGear's UnifiedPush server to register web applications for push notifications. Using the SimplePushClient, a web application can register for push notifications from a SimplePush network and then inform the UnifiedPush server as to where it should send those push notifications. 73 | 74 | See the [UnifiedPushClient API docs](http://aerogear.org/docs/specs/aerogear-js/AeroGear.UnifiedPushClient.html) for more info. 75 | 76 | ## Feature Stability 77 | - - - 78 | 79 | All features of the library are given a stability rating which is noted in the documentation for that feature. The stability ratings are as follows: 80 | 81 | * Experimental - This feature is new and has not been thoroughly tested outside of development. This feature could be changed or removed at any time. Use of these features in a production environment is at your own risk. 82 | * Stable - This feature has existed for a full release cycle and has been thoroughly tested. These features are considered safe for use in production environments. 83 | * Deprecated - This feature is being removed or replaced. As with experimental features, these features could be removed at any time and their use in production environments is at your own risk. For features being replaced, it is recommended to update to the next version and begin using the new feature. 84 | 85 | ## Library Dependencies 86 | - - - 87 | 88 | Some parts of AeroGear.js depend on external libraries which are not bundled in the same file. Below is a list of each plugin and their adapters along with external dependencies, if they have any. It is recommended to use the latest stable version of each dependency unless otherwise noted. 89 | 90 | ### Crypto 91 | * [SJCL](https://github.com/bitwiseshiftleft/sjcl) - bundled w/ AeroGear.js 92 | 93 | ### DataManager 94 | 95 | * **Memory** 96 | * [ES6 Promise polyfill](https://github.com/jakearchibald/es6-promise) 97 | * **SessionLocal** 98 | * [ES6 Promise polyfill](https://github.com/jakearchibald/es6-promise) 99 | * **IndexedDB** 100 | * [ES6 Promise polyfill](https://github.com/jakearchibald/es6-promise) 101 | * **WebSQL** 102 | * [ES6 Promise polyfill](https://github.com/jakearchibald/es6-promise) 103 | 104 | ### Notifier 105 | 106 | * **STOMP-WS** 107 | * [STOMP Over WebSocket](https://github.com/jmesnil/stomp-websocket/) 108 | * **vert.x** 109 | * [vert.x Event Bus](http://vertx.io/downloads.html) 110 | * [SockJS](http://cdn.sockjs.org/) 111 | * **MQTT-WS** 112 | * [Eclipse Paho MQTT JavaScript Client](http://download.eclipse.org/paho/1.0/paho.javascript-1.0.0.zip) 113 | * **SimplePush** 114 | * See SimplePush Plugin 115 | 116 | ### SimplePush 117 | * [jQuery](http://jquery.com/download/) 118 | * [SockJS](http://cdn.sockjs.org/) 119 | 120 | ### UnifiedPush 121 | * [ES6 Promise polyfill](https://github.com/jakearchibald/es6-promise) 122 | 123 | ### Diff Sync 124 | * **Diff Match Patch** 125 | * [Google Diff Match Patch](https://code.google.com/p/google-diff-match-patch/) 126 | 127 | * **JSON Patch** 128 | * [JSON Patch](https://github.com/Starcounter-Jack/JSON-Patch) 129 | 130 | ## Building 131 | - - - 132 | 133 | ### Grunt 134 | 135 | [Grunt](http://gruntjs.com/) is used as the build tool which requires [Node.js](http://nodejs.org/) version >= 0.10. 136 | Please refer to [nodejs.org](http://nodejs.org) for details regarding installing Node.js. 137 | Please refer to Grunt's [getting started](http://gruntjs.com/getting-started) guide for details regarding installing Grunt. 138 | 139 | ### Installing Build Dependencies 140 | To install the dependencies of the project run the following command: 141 | 142 | $ npm install 143 | 144 | This will install the versions of the dependencies declared in package.json. This is only required to be done once before 145 | building the first time, or if the dependencies in package.json have been updated. 146 | 147 | ### Building the project 148 | 149 | $ grunt 150 | 151 | The produced JavaScript will be in the __dist__ directory. 152 | 153 | ### Custom Build 154 | 155 | There is a special grunt task called, `custom` to help create custom builds of the library. 156 | 157 | The custom task takes a comma delimited list of "modules". 158 | 159 | For example, if you wanted a build with Authorization/OAuth2 and the SimplePushClient, you would do 160 | 161 | $ grunt custom:oauth2,simplePush 162 | 163 | The produced JavaScript will be in the __dist__ directory as __aerogear.custom.js__. 164 | 165 | For usage and a list of available "modules" run, 166 | 167 | $ grunt custom:help 168 | 169 | ### Generating the documentation 170 | To generate the API docs, run the following command: 171 | 172 | $ jsdoc-aerogear src/ -r -d docs README.md 173 | 174 | or by running the grunt `docs` task 175 | 176 | $ grunt docs 177 | 178 | _The docs use a slightly modified version of jsdoc_ 179 | 180 | ## Documentation 181 | 182 | For more details about the current release, please consult [our documentation](https://aerogear.org/docs/specs/aerogear-js/). 183 | 184 | ## Development 185 | 186 | If you would like to help develop AeroGear you can join our [developer's mailing list](https://lists.jboss.org/mailman/listinfo/aerogear-dev), join #aerogear on Freenode, or shout at us on Twitter @aerogears. 187 | 188 | Also takes some time and skim the [contributor guide](http://aerogear.org/docs/guides/Contributing/) 189 | 190 | ## Questions? 191 | 192 | Join our [user mailing list](https://lists.jboss.org/mailman/listinfo/aerogear-users) for any questions or help! We really hope you enjoy app development with AeroGear! 193 | 194 | ## Found a bug? 195 | 196 | If you found a bug please create a ticket for us on [Jira](https://issues.jboss.org/browse/AGJS) with some steps to reproduce it. 197 | -------------------------------------------------------------------------------- /custom_build_help.txt: -------------------------------------------------------------------------------- 1 | A task to create custom builds of the library based on the 'concat' task 2 | 3 | Usage: 4 | grunt custom:[ task[,task...] ] - create a custom build 5 | 6 | grunt custom:help - Show this help 7 | 8 | 9 | Example: A custom build containing IndexedDB Adapter 10 | 11 | grunt custom:dataManagerIndexedDB 12 | 13 | ---------------------------------------------------------------------------- 14 | 15 | Below is a listing of the available modules: 16 | -------------------------------------------------------------------------------- /external/crypto/LICENSE: -------------------------------------------------------------------------------- 1 | SJCL is open. You can use, modify and redistribute it under a BSD license or under the GNU GPL, version 2.0 or higher. 2 | http://bitwiseshiftleft.github.io/sjcl/ 3 | -------------------------------------------------------------------------------- /external/uuid/license.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2012 Robert Kieffer 2 | MIT License - http://opensource.org/licenses/mit-license.php -------------------------------------------------------------------------------- /external/uuid/uuid.js: -------------------------------------------------------------------------------- 1 | // node-uuid/uuid.js 2 | // 3 | // Copyright (c) 2010 Robert Kieffer 4 | // Dual licensed under the MIT and GPL licenses. 5 | // Documentation and details at https://github.com/broofa/node-uuid 6 | (function() { 7 | var _global = this; 8 | 9 | // Unique ID creation requires a high quality random # generator, but 10 | // Math.random() does not guarantee "cryptographic quality". So we feature 11 | // detect for more robust APIs, normalizing each method to return 128-bits 12 | // (16 bytes) of random data. 13 | var mathRNG, nodeRNG, whatwgRNG; 14 | 15 | // Math.random()-based RNG. All platforms, very fast, unknown quality 16 | var _rndBytes = new Array(16); 17 | mathRNG = function() { 18 | var r, b = _rndBytes, i = 0; 19 | 20 | for (var i = 0, r; i < 16; i++) { 21 | if ((i & 0x03) == 0) r = Math.random() * 0x100000000; 22 | b[i] = r >>> ((i & 0x03) << 3) & 0xff; 23 | } 24 | 25 | return b; 26 | } 27 | 28 | // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto 29 | // WebKit only (currently), moderately fast, high quality 30 | if (_global.crypto && crypto.getRandomValues) { 31 | var _rnds = new Uint32Array(4); 32 | whatwgRNG = function() { 33 | crypto.getRandomValues(_rnds); 34 | 35 | for (var c = 0 ; c < 16; c++) { 36 | _rndBytes[c] = _rnds[c >> 2] >>> ((c & 0x03) * 8) & 0xff; 37 | } 38 | return _rndBytes; 39 | } 40 | } 41 | 42 | // Node.js crypto-based RNG - http://nodejs.org/docs/v0.6.2/api/crypto.html 43 | // Node.js only, moderately fast, high quality 44 | try { 45 | var _rb = require('crypto').randomBytes; 46 | nodeRNG = _rb && function() { 47 | return _rb(16); 48 | }; 49 | } catch (e) {} 50 | 51 | // Select RNG with best quality 52 | var _rng = nodeRNG || whatwgRNG || mathRNG; 53 | 54 | // Buffer class to use 55 | var BufferClass = typeof(Buffer) == 'function' ? Buffer : Array; 56 | 57 | // Maps for number <-> hex string conversion 58 | var _byteToHex = []; 59 | var _hexToByte = {}; 60 | for (var i = 0; i < 256; i++) { 61 | _byteToHex[i] = (i + 0x100).toString(16).substr(1); 62 | _hexToByte[_byteToHex[i]] = i; 63 | } 64 | 65 | // **`parse()` - Parse a UUID into it's component bytes** 66 | function parse(s, buf, offset) { 67 | var i = (buf && offset) || 0, ii = 0; 68 | 69 | buf = buf || []; 70 | s.toLowerCase().replace(/[0-9a-f]{2}/g, function(byte) { 71 | if (ii < 16) { // Don't overflow! 72 | buf[i + ii++] = _hexToByte[byte]; 73 | } 74 | }); 75 | 76 | // Zero out remaining bytes if string was short 77 | while (ii < 16) { 78 | buf[i + ii++] = 0; 79 | } 80 | 81 | return buf; 82 | } 83 | 84 | // **`unparse()` - Convert UUID byte array (ala parse()) into a string** 85 | function unparse(buf, offset) { 86 | var i = offset || 0, bth = _byteToHex; 87 | return bth[buf[i++]] + bth[buf[i++]] + 88 | bth[buf[i++]] + bth[buf[i++]] + '-' + 89 | bth[buf[i++]] + bth[buf[i++]] + '-' + 90 | bth[buf[i++]] + bth[buf[i++]] + '-' + 91 | bth[buf[i++]] + bth[buf[i++]] + '-' + 92 | bth[buf[i++]] + bth[buf[i++]] + 93 | bth[buf[i++]] + bth[buf[i++]] + 94 | bth[buf[i++]] + bth[buf[i++]]; 95 | } 96 | 97 | // **`v1()` - Generate time-based UUID** 98 | // 99 | // Inspired by https://github.com/LiosK/UUID.js 100 | // and http://docs.python.org/library/uuid.html 101 | 102 | // random #'s we need to init node and clockseq 103 | var _seedBytes = _rng(); 104 | 105 | // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1) 106 | var _nodeId = [ 107 | _seedBytes[0] | 0x01, 108 | _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5] 109 | ]; 110 | 111 | // Per 4.2.2, randomize (14 bit) clockseq 112 | var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff; 113 | 114 | // Previous uuid creation time 115 | var _lastMSecs = 0, _lastNSecs = 0; 116 | 117 | // See https://github.com/broofa/node-uuid for API details 118 | function v1(options, buf, offset) { 119 | var i = buf && offset || 0; 120 | var b = buf || []; 121 | 122 | options = options || {}; 123 | 124 | var clockseq = options.clockseq != null ? options.clockseq : _clockseq; 125 | 126 | // UUID timestamps are 100 nano-second units since the Gregorian epoch, 127 | // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so 128 | // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs' 129 | // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00. 130 | var msecs = options.msecs != null ? options.msecs : new Date().getTime(); 131 | 132 | // Per 4.2.1.2, use count of uuid's generated during the current clock 133 | // cycle to simulate higher resolution clock 134 | var nsecs = options.nsecs != null ? options.nsecs : _lastNSecs + 1; 135 | 136 | // Time since last uuid creation (in msecs) 137 | var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000; 138 | 139 | // Per 4.2.1.2, Bump clockseq on clock regression 140 | if (dt < 0 && options.clockseq == null) { 141 | clockseq = clockseq + 1 & 0x3fff; 142 | } 143 | 144 | // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new 145 | // time interval 146 | if ((dt < 0 || msecs > _lastMSecs) && options.nsecs == null) { 147 | nsecs = 0; 148 | } 149 | 150 | // Per 4.2.1.2 Throw error if too many uuids are requested 151 | if (nsecs >= 10000) { 152 | throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec'); 153 | } 154 | 155 | _lastMSecs = msecs; 156 | _lastNSecs = nsecs; 157 | _clockseq = clockseq; 158 | 159 | // Per 4.1.4 - Convert from unix epoch to Gregorian epoch 160 | msecs += 12219292800000; 161 | 162 | // `time_low` 163 | var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; 164 | b[i++] = tl >>> 24 & 0xff; 165 | b[i++] = tl >>> 16 & 0xff; 166 | b[i++] = tl >>> 8 & 0xff; 167 | b[i++] = tl & 0xff; 168 | 169 | // `time_mid` 170 | var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff; 171 | b[i++] = tmh >>> 8 & 0xff; 172 | b[i++] = tmh & 0xff; 173 | 174 | // `time_high_and_version` 175 | b[i++] = tmh >>> 24 & 0xf | 0x10; // include version 176 | b[i++] = tmh >>> 16 & 0xff; 177 | 178 | // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant) 179 | b[i++] = clockseq >>> 8 | 0x80; 180 | 181 | // `clock_seq_low` 182 | b[i++] = clockseq & 0xff; 183 | 184 | // `node` 185 | var node = options.node || _nodeId; 186 | for (var n = 0; n < 6; n++) { 187 | b[i + n] = node[n]; 188 | } 189 | 190 | return buf ? buf : unparse(b); 191 | } 192 | 193 | // **`v4()` - Generate random UUID** 194 | 195 | // See https://github.com/broofa/node-uuid for API details 196 | function v4(options, buf, offset) { 197 | // Deprecated - 'format' argument, as supported in v1.2 198 | var i = buf && offset || 0; 199 | 200 | if (typeof(options) == 'string') { 201 | buf = options == 'binary' ? new BufferClass(16) : null; 202 | options = null; 203 | } 204 | options = options || {}; 205 | 206 | var rnds = options.random || (options.rng || _rng)(); 207 | 208 | // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` 209 | rnds[6] = (rnds[6] & 0x0f) | 0x40; 210 | rnds[8] = (rnds[8] & 0x3f) | 0x80; 211 | 212 | // Copy bytes to buffer, if provided 213 | if (buf) { 214 | for (var ii = 0; ii < 16; ii++) { 215 | buf[i + ii] = rnds[ii]; 216 | } 217 | } 218 | 219 | return buf || unparse(rnds); 220 | } 221 | 222 | // Export public API 223 | var uuid = v4; 224 | uuid.v1 = v1; 225 | uuid.v4 = v4; 226 | uuid.parse = parse; 227 | uuid.unparse = unparse; 228 | uuid.BufferClass = BufferClass; 229 | 230 | // Export RNG options 231 | uuid.mathRNG = mathRNG; 232 | uuid.nodeRNG = nodeRNG; 233 | uuid.whatwgRNG = whatwgRNG; 234 | 235 | // Play nice with browsers 236 | var _previousRoot = _global.uuid; 237 | 238 | // **`noConflict()` - (browser only) to reset global 'uuid' var** 239 | uuid.noConflict = function() { 240 | _global.uuid = _previousRoot; 241 | return uuid; 242 | } 243 | _global.uuid = uuid; 244 | }()); 245 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aerogear", 3 | "title": "AeroGear JavaScript Library", 4 | "description": "Provides a lightweight set of utilities for communication, security, storage and more.", 5 | "version": "2.2.0-dev", 6 | "homepage": "https://github.com/aerogear/aerogear-js", 7 | "author": { 8 | "name": "Red Hat, Inc.", 9 | "url": "http://aerogear.org" 10 | }, 11 | "maintainers": { 12 | "name": "Lucas Holmquist", 13 | "email": "lholmqui@redhat.com" 14 | }, 15 | "contributors": [ 16 | { 17 | "name": "Kris Borchers", 18 | "url": "http://twitter.com/kborchers" 19 | }, 20 | { 21 | "name": "Lucas Holmquist", 22 | "url": "http://twitter.com/sienaluke" 23 | }, 24 | { 25 | "name": "Bruno Oliveira", 26 | "url": "http://twitter.com/abstractj" 27 | }, 28 | { 29 | "name": "Matthias Wessendorf", 30 | "url": "http://twitter.com/mwessendorf" 31 | }, 32 | { 33 | "name": "Daniel Bevenius", 34 | "url": "http://twitter.com/dbevenius" 35 | }, 36 | { 37 | "name": "Douglas Campos", 38 | "url": "http://twitter.com/qmx" 39 | }, 40 | { 41 | "name": "Sebastien Blanc", 42 | "url": "http://twitter.com/sebi2706" 43 | }, 44 | { 45 | "name": "Jay Balunas", 46 | "url": "http://twitter.com/tech4j" 47 | }, 48 | { 49 | "name": "Christos Vasilakis", 50 | "url": "http://twitter.com/cvasilak" 51 | }, 52 | { 53 | "name": "Yavuz Yilmaz", 54 | "url": "https://github.com/yavuzsel" 55 | }, 56 | { 57 | "name": "Tolis Emmanouilidis", 58 | "url": "https://twitter.com/tolis_e" 59 | }, 60 | { 61 | "name": "Lukáš Fryč", 62 | "url": "https://twitter.com/lfryc" 63 | } 64 | ], 65 | "repository": { 66 | "type": "git", 67 | "url": "aerogear/aerogear-js.git" 68 | }, 69 | "bugs": { 70 | "url": "https://issues.jboss.org/browse/AGJS" 71 | }, 72 | "licenses": [ 73 | { 74 | "type": "Apache-2.0", 75 | "url": "http://www.apache.org/licenses/LICENSE-2.0" 76 | } 77 | ], 78 | "keywords": [], 79 | "devDependencies": { 80 | "grunt": "~0.4.1", 81 | "grunt-cli": "~0.1.13", 82 | "grunt-contrib-concat": "~0.1.2", 83 | "grunt-contrib-jshint": "~0.10.0", 84 | "grunt-contrib-qunit": "~0.5.2", 85 | "grunt-contrib-uglify": "~0.6.0", 86 | "grunt-contrib-watch": "~0.6.1", 87 | "grunt-shell": "0.3.1", 88 | "jsdoc-aerogear": "~3.2.0-dev", 89 | "lodash": "~2.4.1", 90 | "request": "~2.27.0", 91 | "shelljs": "^0.3.0" 92 | }, 93 | "scripts": { 94 | "test": "grunt travis --verbose" 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/aerogear.core.js: -------------------------------------------------------------------------------- 1 | /* AeroGear JavaScript Library 2 | * https://github.com/aerogear/aerogear-js 3 | * JBoss, Home of Professional Open Source 4 | * Copyright Red Hat, Inc., and individual contributors 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | The AeroGear namespace provides a way to encapsulate the library's properties and methods away from the global namespace 18 | @namespace 19 | */ 20 | this.AeroGear = {}; 21 | 22 | /** 23 | AeroGear.Core is a base for all of the library modules to extend. It is not to be instantiated and will throw an error when attempted 24 | @class 25 | @private 26 | */ 27 | AeroGear.Core = function() { 28 | // Prevent instantiation of this base class 29 | if ( this instanceof AeroGear.Core ) { 30 | throw "Invalid instantiation of base class AeroGear.Core"; 31 | } 32 | 33 | /** 34 | This function is used by the different parts of AeroGear to add a new Object to its respective collection. 35 | @name AeroGear.add 36 | @method 37 | @param {String|Array|Object} config - This can be a variety of types specifying how to create the object. See the particular constructor for the object calling .add for more info. 38 | @returns {Object} The object containing the collection that was updated 39 | */ 40 | this.add = function( config ) { 41 | var i, 42 | current, 43 | collection = this[ this.collectionName ] || {}; 44 | this[ this.collectionName ] = collection; 45 | 46 | if ( !config ) { 47 | return this; 48 | } else if ( typeof config === "string" ) { 49 | // config is a string so use default adapter type 50 | collection[ config ] = AeroGear[ this.lib ].adapters[ this.type ]( config, this.config ); 51 | } else if ( Array.isArray( config ) ) { 52 | // config is an array so loop through each item in the array 53 | for ( i = 0; i < config.length; i++ ) { 54 | current = config[ i ]; 55 | 56 | if ( typeof current === "string" ) { 57 | collection[ current ] = AeroGear[ this.lib ].adapters[ this.type ]( current, this.config ); 58 | } else { 59 | if( current.name ) { 60 | 61 | // Merge the Module( authz, datamanger, ... )config with the adapters settings 62 | current.settings = AeroGear.extend( current.settings || {}, this.config ); 63 | 64 | collection[ current.name ] = AeroGear[ this.lib ].adapters[ current.type || this.type ]( current.name, current.settings ); 65 | } 66 | } 67 | } 68 | } else { 69 | if( !config.name ) { 70 | return this; 71 | } 72 | 73 | // Merge the Module( authz, datamanger, ... )config with the adapters settings 74 | // config is an object so use that signature 75 | config.settings = AeroGear.extend( config.settings || {}, this.config ); 76 | 77 | collection[ config.name ] = AeroGear[ this.lib ].adapters[ config.type || this.type ]( config.name, config.settings ); 78 | } 79 | 80 | // reset the collection instance 81 | this[ this.collectionName ] = collection; 82 | 83 | return this; 84 | }; 85 | /** 86 | This function is used internally by datamanager, etc. to remove an Object (store, etc.) from the respective collection. 87 | @name AeroGear.remove 88 | @method 89 | @param {String|String[]|Object[]|Object} config - This can be a variety of types specifying how to remove the object. See the particular constructor for the object calling .remove for more info. 90 | @returns {Object} The object containing the collection that was updated 91 | */ 92 | this.remove = function( config ) { 93 | var i, 94 | current, 95 | collection = this[ this.collectionName ] || {}; 96 | 97 | if ( typeof config === "string" ) { 98 | // config is a string so delete that item by name 99 | delete collection[ config ]; 100 | } else if ( Array.isArray( config ) ) { 101 | // config is an array so loop through each item in the array 102 | for ( i = 0; i < config.length; i++ ) { 103 | current = config[ i ]; 104 | 105 | if ( typeof current === "string" ) { 106 | delete collection[ current ]; 107 | } else { 108 | delete collection[ current.name ]; 109 | } 110 | } 111 | } else if ( config ) { 112 | // config is an object so use that signature 113 | delete collection[ config.name ]; 114 | } 115 | 116 | // reset the collection instance 117 | this[ this.collectionName ] = collection; 118 | 119 | return this; 120 | }; 121 | }; 122 | 123 | /** 124 | Utility function to merge many Objects in one target Object which is the first object in arguments list. 125 | @private 126 | @method 127 | */ 128 | AeroGear.extend = function() { 129 | var name, i, source, 130 | target = arguments[ 0 ]; 131 | for( i=1; i 0 ) { 81 | patchMsg.edits = pendingEdits.concat( patchMsg.edits ); 82 | } 83 | 84 | return patchMsg; 85 | }; 86 | 87 | /** 88 | * Performs the client side patch process. 89 | * 90 | * @param patchMsg the patch message that is sent from the server 91 | * 92 | * @example: 93 | * { 94 | * "msgType":"patch", 95 | * "id":"12345", 96 | * "clientId":"3346dff7-aada-4d5f-a3da-c93ff0ffc472", 97 | * "edits":[{ 98 | * "clientVersion":0, 99 | * "serverVersion":0, 100 | * "checksum":"5f9844b21c298ea1f3ed7bf37f96e42df03395b", 101 | * "diffs":[ 102 | * {"operation":"UNCHANGED","text":"I'm a Je"}, 103 | * {"operation":"DELETE","text":"di"}] 104 | * }] 105 | * } 106 | */ 107 | this.patch = function( patchMsg ) { 108 | // Flow is based on the server side 109 | // patch the shadow 110 | var patchedShadow = this.patchShadow( patchMsg ); 111 | // Then patch the document 112 | this.patchDocument( patchedShadow ); 113 | // then save backup shadow 114 | this._saveShadowBackup( patchedShadow, patchedShadow.clientVersion ); 115 | 116 | }; 117 | 118 | this.patchShadow = function( patchMsg ) { 119 | // First get the shadow document for this doc.id and clientId 120 | var i, patched, edit, 121 | shadow = this.getShadow( patchMsg.id ), 122 | edits = patchMsg.edits; 123 | //Iterate over the edits of the doc 124 | for ( i = 0; i < edits.length; i++ ) { 125 | edit = edits[i]; 126 | 127 | //Check for dropped packets? 128 | // edit.clientVersion < shadow.ClientVersion 129 | if( edit.clientVersion < shadow.clientVersion && !this._isSeeded( edit ) ) { 130 | // Dropped packet? // restore from back 131 | shadow = this._restoreBackup( shadow, edit ); 132 | continue; 133 | } 134 | 135 | //check if we already have this one 136 | // IF SO discard the edit 137 | // edit.serverVersion < shadow.ServerVesion 138 | if( edit.serverVersion < shadow.serverVersion ) { 139 | // discard edit 140 | this._removeEdit( patchMsg.id, edit ); 141 | continue; 142 | } 143 | 144 | //make sure the versions match 145 | if( (edit.serverVersion === shadow.serverVersion && edit.clientVersion === shadow.clientVersion) || this._isSeeded( edit )) { 146 | // Good , Patch the shadow 147 | this.applyEditsToShadow( edit, shadow ); 148 | if ( this._isSeeded( edit ) ) { 149 | shadow.clientVersion = 0; 150 | } else if ( edit.clientVersion >= 0 ) { 151 | shadow.serverVersion++; 152 | } 153 | this._saveShadow( shadow ); 154 | this._removeEdit( patchMsg.id, edit ); 155 | } 156 | } 157 | 158 | return shadow; 159 | }; 160 | 161 | // A seeded patch is when all clients start with a base document. They all send this base version as 162 | // part of the addDocument call. The server will respond with a patchMsg enabling the client to 163 | // patch it's local version to get the latest updates. Such an edit is identified by a clientVersion 164 | // set to '-1'. 165 | this._isSeeded = function( edit ) { 166 | return edit.clientVersion === -1; 167 | }; 168 | 169 | this.applyEditsToShadow = function ( edits, shadow ) { 170 | var patchResult; 171 | // returns true or false, should probably do something with it? 172 | patchResult = jsonpatch.apply( shadow.content, edits.diffs ); 173 | return shadow; 174 | }; 175 | 176 | this.patchDocument = function( shadow ) { 177 | var doc, diffs, patch; 178 | 179 | // first get the document based on the shadowdocs ID 180 | doc = this.getDocument( shadow.id ); 181 | 182 | diffs = jsonpatch.compare( doc.content, shadow.content ); 183 | 184 | patch = jsonpatch.apply( doc.content, diffs ); 185 | 186 | //save the newly patched document, do we save if the apply failed? 187 | this._saveDocument( doc ); 188 | 189 | return patch; 190 | }; 191 | 192 | this._saveData = function( data, type ) { 193 | data = Array.isArray( data ) ? data : [ data ]; 194 | 195 | stores[ type ] = data; 196 | }; 197 | 198 | this._readData = function( id, type ) { 199 | return stores[ type ].filter( function( doc ) { 200 | return doc.id === id; 201 | }); 202 | }; 203 | 204 | this._saveDocument = function( doc ) { 205 | this._saveData( doc, "docs" ); 206 | return doc; 207 | }; 208 | 209 | this._saveShadow = function( doc ) { 210 | var shadow = { 211 | id: doc.id, 212 | serverVersion: doc.serverVersion || 0, 213 | clientId: doc.clientId, 214 | clientVersion: doc.clientVersion || 0, 215 | content: doc.content 216 | }; 217 | 218 | this._saveData( shadow, "shadows" ); 219 | return shadow; 220 | }; 221 | 222 | this._saveShadowBackup = function( shadow, clientVersion ) { 223 | var backup = { id: shadow.id, clientVersion: clientVersion, content: shadow.content }; 224 | this._saveData( backup, "backups" ); 225 | return backup; 226 | }; 227 | 228 | this.getDocument = function( id ) { 229 | return this._readData( id, "docs" )[ 0 ]; 230 | }; 231 | 232 | this.getShadow = function( id ) { 233 | return this._readData( id, "shadows" )[ 0 ]; 234 | }; 235 | 236 | this.getBackup = function( id ) { 237 | return this._readData( id, "backups" )[ 0 ]; 238 | }; 239 | 240 | this._saveEdits = function( patchMsg ) { 241 | var record = { id: patchMsg.id, clientId: patchMsg.clientId, edits: patchMsg.edits}; 242 | this._saveData( record, "edits" ); 243 | return record; 244 | }; 245 | 246 | this._getEdits = function( id ) { 247 | var patchMessages = this._readData( id, "edits" ); 248 | 249 | return patchMessages.length ? patchMessages.edits : []; 250 | }; 251 | 252 | this._removeEdit = function( documentId, edit ) { 253 | var pendingEdits = this._readData( documentId, "edits" ), i, j, pendingEdit; 254 | for ( i = 0; i < pendingEdits.length; i++ ) { 255 | pendingEdit = pendingEdits[i]; 256 | for ( j = 0; j < pendingEdit.edits.length; j++) { 257 | if ( pendingEdit.edits[j].clientVersion <= edit.clientVersion) { 258 | pendingEdit.edits.splice(i, 1); 259 | break; 260 | } 261 | } 262 | } 263 | }; 264 | 265 | this._removeEdits = function( documentId ) { 266 | var edits = this._readData( documentId, "edits" ), i; 267 | edits.splice(0, edits.length); 268 | }; 269 | 270 | this._restoreBackup = function( shadow, edit) { 271 | var patchedShadow, restoredBackup, 272 | backup = this.getBackup( shadow.id ); 273 | 274 | if ( edit.clientVersion === backup.clientVersion ) { 275 | 276 | restoredBackup = { 277 | id: backup.id, 278 | clientVersion: backup.clientVersion, 279 | content: backup.content 280 | }; 281 | 282 | patchedShadow = this.applyEditsToShadow( edit, restoredBackup ); 283 | restoredBackup.serverVersion++; 284 | this._removeEdits( shadow.id ); 285 | 286 | return this._saveShadow( patchedShadow ); 287 | } else { 288 | throw "Edit's clientVersion '" + edit.clientVersion + "' does not match the backups clientVersion '" + backup.clientVersion + "'"; 289 | } 290 | }; 291 | }; 292 | -------------------------------------------------------------------------------- /src/notifier/adapters/base.js: -------------------------------------------------------------------------------- 1 | /* AeroGear JavaScript Library 2 | * https://github.com/aerogear/aerogear-js 3 | * JBoss, Home of Professional Open Source 4 | * Copyright Red Hat, Inc., and individual contributors 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | The Base Notifier adapter that all other Notifier adapters( except SimplePush ) will extend from. 18 | Not to be Instantiated directly 19 | */ 20 | 21 | AeroGear.Notifier.adapters.base = function( clientName, settings ) { 22 | if ( this instanceof AeroGear.Notifier.adapters.base ) { 23 | throw "Invalid instantiation of base class AeroGear.Notifier.adapters.base"; 24 | } 25 | 26 | settings = settings || {}; 27 | 28 | var connectURL = settings.connectURL || "", 29 | channels = settings.channels || [], 30 | autoConnect = !!settings.autoConnect || channels.length, 31 | state = AeroGear.Notifier.CONNECTING, 32 | name = clientName, 33 | client = null; 34 | 35 | // Privileged methods 36 | /** 37 | Returns the value of the private connectURL var 38 | @private 39 | @augments AeroGear.Notifier.adapters.base 40 | */ 41 | this.getConnectURL = function() { 42 | return connectURL; 43 | }; 44 | 45 | /** 46 | Set the value of the private connectURL var 47 | @private 48 | @augments AeroGear.Notifier.adapters.base 49 | @param {String} url - New connectURL for this client 50 | */ 51 | this.setConnectURL = function( url ) { 52 | connectURL = url; 53 | }; 54 | 55 | /** 56 | Returns the value of the private channels var 57 | @private 58 | @augments AeroGear.Notifier.adapters.base 59 | */ 60 | this.getChannels = function() { 61 | return channels; 62 | }; 63 | 64 | /** 65 | Adds a channel to the set 66 | @param {Object} channel - The channel object to add to the set 67 | @private 68 | @augments AeroGear.Notifier.adapters.base 69 | */ 70 | this.addChannel = function( channel ) { 71 | channels.push( channel ); 72 | }; 73 | 74 | /** 75 | Check if subscribed to a channel 76 | @param {String} address - The address of the channel object to search for in the set 77 | @private 78 | @augments AeroGear.Notifier.adapters.base 79 | */ 80 | this.getChannelIndex = function( address ) { 81 | for ( var i = 0; i < channels.length; i++ ) { 82 | if ( channels[ i ].address === address ) { 83 | return i; 84 | } 85 | } 86 | return -1; 87 | }; 88 | 89 | /** 90 | Removes a channel from the set 91 | @param {Object} channel - The channel object to remove from the set 92 | @private 93 | @augments AeroGear.Notifier.adapters.base 94 | */ 95 | this.removeChannel = function( channel ) { 96 | var index = this.getChannelIndex( channel.address ); 97 | if ( index >= 0 ) { 98 | channels.splice( index, 1 ); 99 | } 100 | }; 101 | 102 | /** 103 | Returns the value of the private state var 104 | @private 105 | @augments AeroGear.Notifier.adapters.base 106 | */ 107 | this.getState = function() { 108 | return state; 109 | }; 110 | 111 | /** 112 | Sets the value of the private state var 113 | @private 114 | @augments AeroGear.Notifier.adapters.base 115 | */ 116 | this.setState = function( newState ) { 117 | state = newState; 118 | }; 119 | 120 | /** 121 | Returns the value of the private client var 122 | @private 123 | @augments AeroGear.Notifier.adapters.base 124 | */ 125 | this.getClient = function() { 126 | return client; 127 | }; 128 | 129 | /** 130 | Sets the value of the private client var 131 | @private 132 | @augments AeroGear.Notifier.adapters.base 133 | */ 134 | this.setClient = function( newClient ) { 135 | client = newClient; 136 | }; 137 | 138 | // Handle auto-connect. 139 | // for stompws ONLY - If Login or Password are needed, autoConnect won't happen 140 | if ( ( autoConnect || channels.length ) && ( !settings.login && !settings.password ) ) { 141 | this.connect({ 142 | url: connectURL, 143 | onConnect: settings.onConnect, 144 | onDisconnect: settings.onDisconnect, // for Vertx 145 | onConnectError: settings.onConnectError, 146 | onMessage: settings.onMessage // for mqttws 147 | }); 148 | } 149 | }; 150 | -------------------------------------------------------------------------------- /src/notifier/aerogear.notifier.js: -------------------------------------------------------------------------------- 1 | /* AeroGear JavaScript Library 2 | * https://github.com/aerogear/aerogear-js 3 | * JBoss, Home of Professional Open Source 4 | * Copyright Red Hat, Inc., and individual contributors 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | The AeroGear.Notifier namespace provides a messaging API. Through the use of adapters, this library provides common methods like connect, disconnect, subscribe, unsubscribe and publish. 18 | @deprecated since 2.1.0 and will be removed in a future release. 19 | @status Deprecated 20 | @class 21 | @augments AeroGear.Core 22 | @param {String|Array|Object} [config] - A configuration for the client(s) being created along with the notifier. If an object or array containing objects is used, the objects can have the following properties: 23 | @param {String} config.name - the name that the client will later be referenced by 24 | @param {String} [config.type="vertx"] - the type of client as determined by the adapter used 25 | @param {Object} [config.settings={}] - the settings to be passed to the adapter 26 | @returns {Object} The created notifier containing any messaging clients that may have been created 27 | @example 28 | // Create an empty notifier 29 | var notifier = AeroGear.Notifier(); 30 | 31 | // Create a single client using the default adapter 32 | var notifier2 = AeroGear.Notifier( "myNotifier" ); 33 | 34 | // Create multiple clients using the default adapter 35 | var notifier3 = AeroGear.Notifier( [ "someNotifier", "anotherNotifier" ] ); 36 | 37 | // Create a default adapter with settings 38 | var notifier4 = AeroGear.Notifier({ 39 | name: "vertxNotifier", 40 | type: "vertx", 41 | settings: { ... } 42 | }); 43 | 44 | // Create a stompws adapter with settings 45 | var notifier5 = AeroGear.Notifier({ 46 | name: "STOMPNotifier", 47 | type: "stompws", 48 | settings: { ... } 49 | }); 50 | 51 | // Create a vertx and stompws adapter with settings 52 | var notifier6 = AeroGear.Notifier([ 53 | { 54 | name: "vertxNotifier", 55 | type: "vertx", 56 | settings: { ... } 57 | }, 58 | { 59 | name: "STOMPNotifier", 60 | type: "stompws", 61 | settings: { ... } 62 | } 63 | ]); 64 | */ 65 | AeroGear.Notifier = function( config ) { 66 | // Allow instantiation without using new 67 | if ( !( this instanceof AeroGear.Notifier ) ) { 68 | return new AeroGear.Notifier( config ); 69 | } 70 | // Super Constructor 71 | AeroGear.Core.call( this ); 72 | 73 | this.lib = "Notifier"; 74 | this.type = config ? config.type || "vertx" : "vertx"; 75 | 76 | /** 77 | The name used to reference the collection of notifier client instances created from the adapters 78 | @memberOf AeroGear.Notifier 79 | @type Object 80 | @default modules 81 | */ 82 | this.collectionName = "clients"; 83 | 84 | this.add( config ); 85 | }; 86 | 87 | AeroGear.Notifier.prototype = AeroGear.Core; 88 | AeroGear.Notifier.constructor = AeroGear.Notifier; 89 | 90 | /** 91 | The adapters object is provided so that adapters can be added to the AeroGear.Notifier namespace dynamically and still be accessible to the add method 92 | @augments AeroGear.Notifier 93 | */ 94 | AeroGear.Notifier.adapters = {}; 95 | 96 | /** 97 | A set of constants used to track the state of a client connection. 98 | */ 99 | AeroGear.Notifier.CONNECTING = 0; 100 | AeroGear.Notifier.CONNECTED = 1; 101 | AeroGear.Notifier.DISCONNECTING = 2; 102 | AeroGear.Notifier.DISCONNECTED = 3; 103 | -------------------------------------------------------------------------------- /src/simplepush/aerogear.simplepush.js: -------------------------------------------------------------------------------- 1 | /* AeroGear JavaScript Library 2 | * https://github.com/aerogear/aerogear-js 3 | * JBoss, Home of Professional Open Source 4 | * Copyright Red Hat, Inc., and individual contributors 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | (function( AeroGear, $, undefined ) { 17 | /** 18 | The SimplePushClient object is used as a sort of polyfill/implementation of the SimplePush spec implemented in Firefox OS and the Firefox browser and provides a mechanism for subscribing to and acting on push notifications in a web application. See https://wiki.mozilla.org/WebAPI/SimplePush 19 | @status Experimental 20 | @constructs AeroGear.SimplePushClient 21 | @param {Object} options - an object used to initialize the connection to the SimplePush server 22 | @param {Boolean} [options.useNative=false] - if true, the connection will first try to use the Mozilla push network (still in development and not ready for production) before falling back to the SimplePush server specified 23 | @param {String} [options.simplePushServerURL] - the URL of the SimplePush server. This option is optional but only if you don't want to support browsers that are missing websocket support and you trust the not yet production ready Mozilla push server. 24 | @param {Function} options.onConnect - a callback to fire when a connection is established with the SimplePush server. This is a deviation from the SimplePush spec as it is not necessary when you using the in browser functionality since the browser establishes the connection before the application is started. 25 | @param {Function} options.onClose - a callback to fire when a connection to the SimplePush server is closed or lost. 26 | @returns {Object} The created unified push server client 27 | @example 28 | // Create the SimplePushClient object: 29 | var client = AeroGear.SimplePushClient({ 30 | simplePushServerURL: "https://localhost:7777/simplepush", 31 | onConnect: myConnectCallback, 32 | onClose: myCloseCallback 33 | }); 34 | */ 35 | AeroGear.SimplePushClient = function( options ) { 36 | // Allow instantiation without using new 37 | if ( !( this instanceof AeroGear.SimplePushClient ) ) { 38 | return new AeroGear.SimplePushClient( options ); 39 | } 40 | 41 | this.options = options || {}; 42 | 43 | // Check for native push support 44 | if ( !!navigator.push && this.options.useNative ) { 45 | // Browser supports push so let it handle it 46 | if ( options.onConnect ) { 47 | options.onConnect(); 48 | } 49 | return; 50 | } 51 | 52 | if ( this.options.useNative ) { 53 | if ("WebSocket" in window ) { 54 | // No native push support but want to use Mozilla servers 55 | this.options.simplePushServerURL = "wss://push.services.mozilla.com"; 56 | } else if ( !this.options.simplePushServerURL ) { 57 | // No native push support and no websocket support so can't talk to Mozilla server 58 | throw "SimplePushConfigurationError"; 59 | } else { 60 | // No websocket, no native support but SimplePush server specified so try SockJS connection 61 | this.options.useNative = false; 62 | } 63 | } 64 | 65 | var spClient = this, 66 | connectOptions = { 67 | onConnect: function() { 68 | /** 69 | The window.navigator object 70 | @namespace navigator 71 | */ 72 | 73 | /** 74 | Add the push object to the global navigator object 75 | @status Experimental 76 | @constructs navigator.push 77 | */ 78 | navigator.push = (function() { 79 | return { 80 | /** 81 | Register a push notification channel with the SimplePush server 82 | @function 83 | @memberof navigator.push 84 | @returns {Object} - The request object where a connection success callback can be registered 85 | @example 86 | var mailRequest = navigator.push.register(); 87 | */ 88 | register: function() { 89 | var request = { 90 | onsuccess: function( event ) {} 91 | }; 92 | 93 | if ( !spClient.simpleNotifier ) { 94 | throw "SimplePushConnectionError"; 95 | } 96 | 97 | spClient.simpleNotifier.subscribe({ 98 | requestObject: request 99 | }); 100 | 101 | return request; 102 | }, 103 | 104 | /** 105 | Unregister a push notification channel from the SimplePush server 106 | @function 107 | @memberof navigator.push 108 | @example 109 | navigator.push.unregister( mailEndpoint ); 110 | */ 111 | unregister: function( endpoint ) { 112 | spClient.simpleNotifier.unsubscribe( endpoint ); 113 | }, 114 | 115 | /** 116 | Reestablish the connection with the SimplePush server when closed or lost. This is an addition and not part of the SimplePush spec 117 | @function 118 | @memberof navigator.push 119 | @param {Object} options - an object used to initialize the connection to the SimplePush server 120 | @param {String} options.simplePushServerURL - the URL of the SimplePush server 121 | @param {Function} options.onConnect - a callback to fire when a connection is established with the SimplePush server. This is a deviation from the SimplePush spec as it is not necessary when you using the in browser functionality since the browser establishes the connection before the application is started. 122 | @param {Function} options.onClose - a callback to fire when a connection to the SimplePush server is closed or lost. 123 | @example 124 | navigator.push.reconnect({ 125 | simplePushServerURL: "https://localhost:7777/simplepush", 126 | onConnect: myConnectCallback, 127 | onClose: myCloseCallback 128 | }); 129 | */ 130 | reconnect: function() { 131 | spClient.simpleNotifier.connect( connectOptions ); 132 | } 133 | }; 134 | })(); 135 | 136 | /** 137 | Add the setMessageHandler/mozSetMessageHandler function to the global navigator object 138 | @status Experimental 139 | @constructs navigator.setMessageHandler/navigator.mozSetMessageHandler 140 | @param {String} messageType - a name or category to give the messages being received and in this implementation, likely 'push' 141 | @param {Function} callback - the function to be called when a message of this type is received 142 | @example 143 | navigator.setMessageHandler( "push", function( message ) { 144 | if ( message.channelID === mailEndpoint.channelID ) { 145 | console.log("Mail Message Received"); 146 | } 147 | }); 148 | 149 | or 150 | // Mozilla's spec currently has the 'moz' prefix 151 | navigator.mozSetMessageHandler( "push", function( message ) { 152 | if ( message.channelID === mailEndpoint.channelID ) { 153 | console.log("Mail Message Received"); 154 | } 155 | }); 156 | */ 157 | navigator.setMessageHandler = navigator.mozSetMessageHandler = function( messageType, callback ) { 158 | $( navigator.push ).on( messageType, function( event ) { 159 | var message = event.message; 160 | callback.call( this, message ); 161 | }); 162 | }; 163 | 164 | if ( spClient.options.onConnect ) { 165 | spClient.options.onConnect(); 166 | } 167 | }, 168 | onClose: function() { 169 | spClient.simpleNotifier.disconnect( spClient.options.onClose ); 170 | } 171 | }; 172 | 173 | // Create a Notifier connection to the Push Network 174 | spClient.simpleNotifier = AeroGear.Notifier({ 175 | name: "agPushNetwork", 176 | type: "SimplePush", 177 | settings: { 178 | connectURL: spClient.options.simplePushServerURL, 179 | useNative: spClient.options.useNative 180 | } 181 | }).clients.agPushNetwork; 182 | 183 | spClient.simpleNotifier.connect( connectOptions ); 184 | }; 185 | })( AeroGear, jQuery ); 186 | -------------------------------------------------------------------------------- /src/unifiedpush/aerogear.unifiedpush.js: -------------------------------------------------------------------------------- 1 | /* AeroGear JavaScript Library 2 | * https://github.com/aerogear/aerogear-js 3 | * JBoss, Home of Professional Open Source 4 | * Copyright Red Hat, Inc., and individual contributors 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | (function( AeroGear, undefined ) { 17 | /** 18 | The UnifiedPushClient object is used to perfom register and unregister operations against the AeroGear UnifiedPush server. 19 | @status Experimental 20 | @constructs AeroGear.UnifiedPushClient 21 | @param {String} variantID - the id representing the mobile application variant 22 | @param {String} variantSecret - the secret for the mobile application variant 23 | @param {String} pushServerURL - the location of the UnifiedPush server e.g. http(s)//host:port/context 24 | @returns {Object} The created unified push server client 25 | @example 26 | // Create the UnifiedPush client object: 27 | var client = AeroGear.UnifiedPushClient( 28 | "myVariantID", 29 | "myVariantSecret", 30 | "http://SERVER:PORT/CONTEXT" 31 | ); 32 | 33 | // assemble the metadata for the registration: 34 | var metadata = { 35 | deviceToken: "http://server.com/simplePushEndpoint", 36 | alias: "some_username", 37 | categories: [ "email" ] 38 | }; 39 | 40 | var settings = {}; 41 | 42 | settings.metadata = metadata; 43 | 44 | // perform the registration against the UnifiedPush server: 45 | client.registerWithPushServer( settings ); 46 | 47 | */ 48 | AeroGear.UnifiedPushClient = function( variantID, variantSecret, pushServerURL ) { 49 | 50 | // we require all arguments to be present, otherwise it does not work 51 | if ( !variantID || !variantSecret || !pushServerURL ) { 52 | throw "UnifiedPushClientException"; 53 | } 54 | 55 | // Allow instantiation without using new 56 | if ( !( this instanceof AeroGear.UnifiedPushClient ) ) { 57 | return new AeroGear.UnifiedPushClient( variantID, variantSecret, pushServerURL ); 58 | } 59 | 60 | pushServerURL = pushServerURL.substr(-1) === '/' ? pushServerURL : pushServerURL + '/'; 61 | 62 | this._ajax = function( settings ) { 63 | return new Promise( function( resolve, reject ) { 64 | var header, 65 | that = this, 66 | request = new XMLHttpRequest(); 67 | 68 | 69 | request.open( settings.type || "GET", settings.url, true, settings.username, settings.password ); 70 | 71 | request.setRequestHeader( "Content-Type", "application/json" ); 72 | request.setRequestHeader( "Accept", "application/json" ); 73 | 74 | if( settings.headers ) { 75 | for ( header in settings.headers ) { 76 | request.setRequestHeader( header, settings.headers[ header ] ); 77 | } 78 | } 79 | 80 | // Success and 400's 81 | request.onload = function() { 82 | var status = ( request.status < 400 ) ? "success" : "error", 83 | promiseValue = that._createPromiseValue( request, status ); 84 | 85 | if( status === "success" ) { 86 | return resolve( promiseValue ); 87 | } 88 | 89 | return reject( promiseValue ); 90 | }; 91 | 92 | // Network errors 93 | request.onerror = function() { 94 | return reject( that._createPromiseValue( request, "error" ) ); 95 | }; 96 | 97 | // create promise arguments 98 | this._createPromiseValue = function( request, status ) { 99 | return { 100 | data: request.response, 101 | statusText: request.statusText || status, 102 | agXHR: request 103 | }; 104 | }; 105 | 106 | request.send( settings.data ); 107 | }); 108 | }; 109 | /** 110 | Performs a register request against the UnifiedPush Server using the given metadata which represents a client that wants to register with the server. 111 | @param {Object} settings The settings to pass in 112 | @param {Object} settings.metadata - the metadata for the client 113 | @param {String} settings.metadata.deviceToken - identifies the client within its PushNetwork. On Android this is the registrationID, on iOS this is the deviceToken and on SimplePush this is the URL of the given SimplePush server/network. 114 | @param {String} [settings.metadata.alias] - Application specific alias to identify users with the system. Common use case would be an email address or a username. 115 | @param {Array} [settings.metadata.categories] - In SimplePush this is the name of the registration endpoint. On Hybrid platforms like Apache Cordova this is used for tagging the registered client. 116 | @param {String} [settings.metadata.operatingSystem] - Useful on Hybrid platforms like Apache Cordova to specifiy the underlying operating system. 117 | @param {String} [settings.metadata.osVersion] - Useful on Hybrid platforms like Apache Cordova to specify the version of the underlying operating system. 118 | @param {String} [settings.metadata.deviceType] - Useful on Hybrid platforms like Apache Cordova to specify the type of the used device, like iPad or Android-Phone. 119 | @returns {Object} An ES6 Promise 120 | */ 121 | this.registerWithPushServer = function( settings ) { 122 | settings = settings || {}; 123 | var metadata = settings.metadata || {}; 124 | 125 | // we need a deviceToken, registrationID or a channelID: 126 | if ( !metadata.deviceToken ) { 127 | throw "UnifiedPushRegistrationException"; 128 | } 129 | 130 | // Make sure that settings.metadata.categories is an Array 131 | metadata.categories = Array.isArray( metadata.categories ) ? metadata.categories : ( metadata.categories ? [ metadata.categories ] : [] ); 132 | 133 | return this._ajax({ 134 | type: "POST", 135 | url: pushServerURL + "rest/registry/device", 136 | headers: { 137 | "Authorization": "Basic " + window.btoa(variantID + ":" + variantSecret) 138 | }, 139 | data: JSON.stringify( metadata ) 140 | }); 141 | }; 142 | 143 | /** 144 | Performs an unregister request against the UnifiedPush Server for the given deviceToken. The deviceToken identifies the client within its PushNetwork. On Android this is the registrationID, on iOS this is the deviceToken and on SimplePush this is the URL of the given SimplePush server/network. 145 | @param {String} deviceToken - unique String which identifies the client that is being unregistered. 146 | @returns {Object} An ES6 Promise 147 | */ 148 | this.unregisterWithPushServer = function( deviceToken ) { 149 | return this._ajax({ 150 | type: "DELETE", 151 | url: pushServerURL + "rest/registry/device/" + deviceToken, 152 | headers: { 153 | "Authorization": "Basic " + window.btoa(variantID + ":" + variantSecret) 154 | } 155 | }); 156 | }; 157 | }; 158 | 159 | })( AeroGear ); 160 | -------------------------------------------------------------------------------- /tests/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "browser": true, 3 | "curly": true, 4 | "eqeqeq": true, 5 | "latedef": true, 6 | "loopfunc": true, 7 | "noarg": true, 8 | "noempty": true, 9 | "undef": true, 10 | "trailing": true, 11 | "globals": { 12 | "suiteData": true, 13 | "ok": true, 14 | "throws": true, 15 | "raises": true, 16 | "start": true, 17 | "stop": true, 18 | "console": true, 19 | "equal": true, 20 | "strictEqual": true, 21 | "notEqual": true, 22 | "asyncTest": true, 23 | "test": true, 24 | "sinon": true, 25 | "module": true, 26 | "expect": true, 27 | "jQuery": true, 28 | "AeroGear": true, 29 | "Promise": true, 30 | "require": true, 31 | "uuid": true, 32 | "vertx": true, 33 | "SockJS": true, 34 | "Stomp": true, 35 | "Messaging": true, 36 | "File": true, 37 | "sjcl": true, 38 | "crypto": true, 39 | "diff_match_patch": true, 40 | "jsonpatch": true 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/polyfill/bind-polyfill.js: -------------------------------------------------------------------------------- 1 | if (!Function.prototype.bind) { 2 | Function.prototype.bind = function(oThis) { 3 | if (typeof this !== 'function') { 4 | // closest thing possible to the ECMAScript 5 5 | // internal IsCallable function 6 | throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); 7 | } 8 | 9 | var aArgs = Array.prototype.slice.call(arguments, 1), 10 | fToBind = this, 11 | fNOP = function() {}, 12 | fBound = function() { 13 | return fToBind.apply(this instanceof fNOP && oThis 14 | ? this 15 | : oThis, 16 | aArgs.concat(Array.prototype.slice.call(arguments))); 17 | }; 18 | 19 | fNOP.prototype = this.prototype; 20 | fBound.prototype = new fNOP(); 21 | 22 | return fBound; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /tests/unit/all.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AeroGear.js Test Suite 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 37 | 38 | 39 | 40 |

AeroGear.js Test Suite

41 |

42 |
43 |

44 |
    45 |
    46 | 47 |
    48 | 49 | 50 | -------------------------------------------------------------------------------- /tests/unit/crypto/aerogear.crypto.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Cryptoparty Test Suite 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    AeroGear Crypto Qunit Test Suite
    15 |
    16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/unit/data-manager-indexeddb/data-manager-indexeddb-encrypted.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | AeroGear Data Manager Test Suite 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
    AeroGear Data Manager Qunit Test Suite
    18 |
    19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/unit/data-manager-indexeddb/data-manager-indexeddb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | AeroGear Data Manager Test Suite 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
    AeroGear Data Manager Qunit Test Suite
    16 |
    17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/unit/data-manager-websql/data-manager-websql-encrypted.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | AeroGear Data Manager Test Suite 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
    AeroGear Data Manager Qunit Test Suite
    18 |
    19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/unit/data-manager-websql/data-manager-websql.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | AeroGear Data Manager Test Suite 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
    AeroGear Data Manager Qunit Test Suite
    16 |
    17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/unit/data-manager/data-manager-fallback-feature-detect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | AeroGear Data Manager Test Suite 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
    AeroGear Data Manager Qunit Test Suite
    18 |
    19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/unit/data-manager/data-manager-fallback-feature-detect.js: -------------------------------------------------------------------------------- 1 | ( function() { 2 | // The non hacky way 3 | 4 | module( "DataManager Creation with fallbacks - Feature Detection", { 5 | setup: function() { 6 | }, 7 | teardown: function() { 8 | } 9 | }); 10 | 11 | test( "create IndexedDB - Fallsback if not available - name string", function() { 12 | expect( 4 ); 13 | 14 | var adapter, 15 | type; 16 | if( "IndexedDB" in AeroGear.DataManager.validAdapters ) { 17 | adapter = AeroGear.DataManager.adapters.IndexedDB; 18 | type = "IndexedDB"; 19 | } else { 20 | for( var i = 0; i < AeroGear.DataManager.preferred.length; i++ ) { 21 | if( AeroGear.DataManager.preferred[ i ] in AeroGear.DataManager.validAdapters ) { 22 | adapter = AeroGear.DataManager.adapters[ AeroGear.DataManager.preferred[ i ] ]; 23 | type = AeroGear.DataManager.preferred[ i ]; 24 | break; 25 | } 26 | } 27 | } 28 | var dm = AeroGear.DataManager( { name: "createTest1", type: "IndexedDB" } ).stores; 29 | equal( Object.keys( dm ).length, 1, "Single Store created" ); 30 | equal( Object.keys( dm )[ 0 ], "createTest1", "Store Name createTest1" ); 31 | equal( dm.createTest1 instanceof adapter, true, type + " Created " ); 32 | equal( dm.createTest1.getAsync(), true, "Adapter should be in async mode since it fellback" ); 33 | }); 34 | 35 | test( "create WebSQL - Fallsback if not available - name string", function() { 36 | expect( 4 ); 37 | 38 | var adapter, 39 | type; 40 | if( "WebSQL" in AeroGear.DataManager.validAdapters ) { 41 | adapter = AeroGear.DataManager.adapters.WebSQL; 42 | type = "WebSQL"; 43 | } else { 44 | for( var i = 0; i < AeroGear.DataManager.preferred.length; i++ ) { 45 | if( AeroGear.DataManager.preferred[ i ] in AeroGear.DataManager.validAdapters ) { 46 | adapter = AeroGear.DataManager.adapters[ AeroGear.DataManager.preferred[ i ] ]; 47 | type = AeroGear.DataManager.preferred[ i ]; 48 | break; 49 | } 50 | } 51 | } 52 | 53 | var dm = AeroGear.DataManager( { name: "createTest1", type: "WebSQL" } ).stores; 54 | equal( Object.keys( dm ).length, 1, "Single Store created" ); 55 | equal( Object.keys( dm )[ 0 ], "createTest1", "Store Name createTest1" ); 56 | equal( dm.createTest1 instanceof adapter, true, type + " Created " ); 57 | equal( dm.createTest1.getAsync(), true, "Adapter should be in async mode since it fellback" ); 58 | }); 59 | })(); 60 | -------------------------------------------------------------------------------- /tests/unit/data-manager/data-manager-fallback.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | AeroGear Data Manager Test Suite 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
    AeroGear Data Manager Qunit Test Suite
    18 |
    19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/unit/data-manager/data-manager-fallback.js: -------------------------------------------------------------------------------- 1 | ( function() { 2 | 3 | // A Little Bit of setup 4 | // Remove the indexedDB and webSQL adapters from our list of valid adapters 5 | // so we can show the fallback to sessionLocal. 6 | // but first lets save a reference 7 | var validAdapters = AeroGear.DataManager.validAdapters; 8 | 9 | module( "DataManager Creation with fallbacks", { 10 | setup: function() { 11 | 12 | for( var adapter in AeroGear.DataManager.validAdapters ) { 13 | if( adapter === "IndexedDB" || adapter === "WebSQL") { 14 | delete AeroGear.DataManager.validAdapters[ adapter ]; 15 | } 16 | } 17 | }, 18 | teardown: function() { 19 | AeroGear.DataManager.validAdapters = validAdapters; 20 | } 21 | }); 22 | 23 | test( "create IndexedDB - Fallsback to SessionLocal - name string", function() { 24 | expect( 3 ); 25 | 26 | var dm = AeroGear.DataManager( { name: "createTest1", type: "IndexedDB" } ).stores; 27 | equal( Object.keys( dm ).length, 1, "Single Store created" ); 28 | equal( Object.keys( dm )[ 0 ], "createTest1", "Store Name createTest1" ); 29 | equal( dm.createTest1 instanceof AeroGear.DataManager.adapters.SessionLocal, true, "Fellback to SessionLocal" ); 30 | }); 31 | 32 | test( "create IndexedDB and WebSQL - Fallsback to SessionLocal - name array", function() { 33 | expect( 7 ); 34 | 35 | var dm = AeroGear.DataManager([ 36 | { 37 | name: "createTest21", 38 | type: "WebSQL" 39 | }, 40 | { 41 | name: "createTest22", 42 | type: "IndexedDB" 43 | }, 44 | "createTest23" 45 | ]).stores; 46 | 47 | equal( Object.keys( dm ).length, 3, "3 Stores created" ); 48 | equal( Object.keys( dm )[ 0 ], "createTest21", "Store Name createTest21" ); 49 | equal( Object.keys( dm )[ 1 ], "createTest22", "Store Name createTest22" ); 50 | equal( Object.keys( dm )[ 2 ], "createTest23", "Store Name createTest23" ); 51 | equal( dm.createTest21 instanceof AeroGear.DataManager.adapters.SessionLocal, true, "Fellback to SessionLocal" ); 52 | equal( dm.createTest22 instanceof AeroGear.DataManager.adapters.SessionLocal, true, "Fellback to SessionLocal" ); 53 | equal( dm.createTest23 instanceof AeroGear.DataManager.adapters.Memory, true, "No Fallback should happen" ); 54 | }); 55 | 56 | test( "create - object", function() { 57 | expect( 5 ); 58 | 59 | var dm = AeroGear.DataManager([ 60 | { 61 | name: "createTest31" 62 | }, 63 | { 64 | name: "createTest32", 65 | type: "WebSQL" 66 | } 67 | ]).stores; 68 | 69 | equal( Object.keys( dm ).length, 2, "2 Stores created" ); 70 | equal( Object.keys( dm )[ 0 ], "createTest31", "Store Name createTest31" ); 71 | equal( Object.keys( dm )[ 1 ], "createTest32", "Store Name createTest32" ); 72 | equal( dm.createTest31 instanceof AeroGear.DataManager.adapters.Memory, true, "No Fallback should happen" ); 73 | equal( dm.createTest32 instanceof AeroGear.DataManager.adapters.SessionLocal, true, "Fellback to SessionLocal" ); 74 | }); 75 | 76 | module( "DataManager Creation with fallbacks off", { 77 | setup: function() { 78 | 79 | for( var adapter in AeroGear.DataManager.validAdapters ) { 80 | if( adapter === "IndexedDB" || adapter === "WebSQL") { 81 | delete AeroGear.DataManager.validAdapters[ adapter ]; 82 | } 83 | } 84 | }, 85 | teardown: function() { 86 | AeroGear.DataManager.validAdapters = validAdapters; 87 | } 88 | }); 89 | 90 | test( "create WebSQL - No Fallback - name string", function() { 91 | expect( 3 ); 92 | 93 | var dm = AeroGear.DataManager( { name: "createTest1", type: "WebSQL", settings: { fallback: false } } ).stores; 94 | equal( Object.keys( dm ).length, 1, "Single Store created" ); 95 | equal( Object.keys( dm )[ 0 ], "createTest1", "Store Name createTest1" ); 96 | equal( dm.createTest1 instanceof AeroGear.DataManager.adapters.WebSQL, true, "Didn't Fallback" ); 97 | }); 98 | 99 | test( "create WebSQL - No Fallback - name array", function() { 100 | expect( 5 ); 101 | 102 | var dm = AeroGear.DataManager([ 103 | { 104 | name: "createTest21", 105 | type: "WebSQL", 106 | settings: { 107 | fallback: false 108 | } 109 | }, 110 | "createTest23" 111 | ]).stores; 112 | 113 | equal( Object.keys( dm ).length, 2, "2 Stores created" ); 114 | equal( Object.keys( dm )[ 0 ], "createTest21", "Store Name createTest21" ); 115 | equal( Object.keys( dm )[ 1 ], "createTest23", "Store Name createTest23" ); 116 | equal( dm.createTest21 instanceof AeroGear.DataManager.adapters.WebSQL, true, "Didn't Fallback" ); 117 | equal( dm.createTest23 instanceof AeroGear.DataManager.adapters.Memory, true, "No Fallback should happen" ); 118 | }); 119 | 120 | test( "create - object", function() { 121 | expect( 5 ); 122 | 123 | var dm = AeroGear.DataManager([ 124 | { 125 | name: "createTest31" 126 | }, 127 | { 128 | name: "createTest32", 129 | type: "WebSQL", 130 | settings: { 131 | fallback: false 132 | } 133 | } 134 | ]).stores; 135 | 136 | equal( Object.keys( dm ).length, 2, "2 Stores created" ); 137 | equal( Object.keys( dm )[ 0 ], "createTest31", "Store Name createTest31" ); 138 | equal( Object.keys( dm )[ 1 ], "createTest32", "Store Name createTest32" ); 139 | equal( dm.createTest31 instanceof AeroGear.DataManager.adapters.Memory, true, "No Fallback should happen" ); 140 | equal( dm.createTest32 instanceof AeroGear.DataManager.adapters.WebSQL, true, "Didn't Fallback" ); 141 | }); 142 | 143 | module( "DataManager Creation with fallbacks while supplying preferred list", { 144 | setup: function() { 145 | 146 | for( var adapter in AeroGear.DataManager.validAdapters ) { 147 | if( adapter === "IndexedDB" || adapter === "WebSQL") { 148 | delete AeroGear.DataManager.validAdapters[ adapter ]; 149 | } 150 | } 151 | }, 152 | teardown: function() { 153 | AeroGear.DataManager.validAdapters = validAdapters; 154 | } 155 | }); 156 | 157 | test( "create IndexedDB - Fallsback to Memory - name string", function() { 158 | expect( 3 ); 159 | 160 | var dm = AeroGear.DataManager( { name: "createTest1", type: "IndexedDB", settings: { preferred: [ "Memory" ] } } ).stores; 161 | equal( Object.keys( dm ).length, 1, "Single Store created" ); 162 | equal( Object.keys( dm )[ 0 ], "createTest1", "Store Name createTest1" ); 163 | equal( dm.createTest1 instanceof AeroGear.DataManager.adapters.Memory, true, "Fellback to Memory" ); 164 | }); 165 | 166 | test( "create IndexedDB and WebSQL - Fallsback to Memory - name array", function() { 167 | expect( 7 ); 168 | 169 | var dm = AeroGear.DataManager([ 170 | { 171 | name: "createTest21", 172 | type: "WebSQL", 173 | settings: { preferred: [ "Memory" ] } 174 | }, 175 | { 176 | name: "createTest22", 177 | type: "IndexedDB" 178 | }, 179 | "createTest23" 180 | ]).stores; 181 | 182 | equal( Object.keys( dm ).length, 3, "3 Stores created" ); 183 | equal( Object.keys( dm )[ 0 ], "createTest21", "Store Name createTest21" ); 184 | equal( Object.keys( dm )[ 1 ], "createTest22", "Store Name createTest22" ); 185 | equal( Object.keys( dm )[ 2 ], "createTest23", "Store Name createTest23" ); 186 | equal( dm.createTest21 instanceof AeroGear.DataManager.adapters.Memory, true, "Fellback to Memory" ); 187 | equal( dm.createTest22 instanceof AeroGear.DataManager.adapters.SessionLocal, true, "Fellback to SessionLocal" ); 188 | equal( dm.createTest23 instanceof AeroGear.DataManager.adapters.Memory, true, "No Fallback should happen" ); 189 | }); 190 | })( ); 191 | -------------------------------------------------------------------------------- /tests/unit/data-manager/data-manager-memory-async.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | AeroGear Data Manager Test Suite 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    AeroGear Data Manager Qunit Test Suite
    15 |
    16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/unit/data-manager/data-manager-sessionLocal-async-encrypted.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | AeroGear Data Manager Test Suite 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
    AeroGear Data Manager Qunit Test Suite
    18 |
    19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/unit/data-manager/data-manager-sessionLocal-async.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | AeroGear Data Manager Test Suite 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
    AeroGear Data Manager Qunit Test Suite
    16 |
    17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/unit/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AeroGear.js Unit Tests 6 | 7 | 8 | 9 |

    AeroGear.js Unit Tests

    10 | 13 | 14 |

    Crypto

    15 | 18 | 19 |

    Data Manager

    20 | 31 | 32 |

    Notifier

    33 | 36 | 37 |

    SimplePush

    38 | 41 | 42 |

    UnifiedPush

    43 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /tests/unit/notifier/mqttws.js: -------------------------------------------------------------------------------- 1 | (function( $ ) { 2 | 3 | module( "Notifier: MQTT Websocket" ); 4 | 5 | test( "create - object", function() { 6 | expect( 2 ); 7 | 8 | var mqtt = AeroGear.Notifier({ 9 | name: "createTest", 10 | type: "mqttws" 11 | }).clients; 12 | 13 | equal( Object.keys( mqtt ).length, 1, "1 Client created" ); 14 | equal( Object.keys( mqtt )[ 0 ], "createTest", "Client Name createTest" ); 15 | }); 16 | 17 | test( "create - array", function() { 18 | expect( 3 ); 19 | 20 | var mqtt = AeroGear.Notifier([ 21 | { 22 | name: "createTest", 23 | type: "mqttws" 24 | }, 25 | { 26 | name: "createTest2", 27 | type: "mqttws" 28 | }]).clients; 29 | 30 | equal( Object.keys( mqtt ).length, 2, "2 Clients created" ); 31 | equal( Object.keys( mqtt )[ 0 ], "createTest", "Client Name createTest" ); 32 | equal( Object.keys( mqtt )[ 1 ], "createTest2", "Client Name createTest2" ); 33 | }); 34 | 35 | // Add client test 36 | test( "add method", function() { 37 | expect( 2 ); 38 | 39 | var mqtt = AeroGear.Notifier().add({ 40 | name: "addTest", 41 | type: "mqttws" 42 | }).clients; 43 | 44 | equal( Object.keys( mqtt ).length, 1, "Single Client added" ); 45 | equal( Object.keys( mqtt )[ 0 ], "addTest", "Client Name addTest" ); 46 | }); 47 | 48 | // Remove client test 49 | test( "remove method", function() { 50 | expect( 3 ); 51 | 52 | var mqtt = AeroGear.Notifier({ 53 | name: "removeTest", 54 | type: "mqttws" 55 | }); 56 | 57 | equal( Object.keys( mqtt.clients ).length, 1, "Single Client added" ); 58 | mqtt.remove("removeTest"); 59 | equal( Object.keys( mqtt.clients ).length, 0, "Single Client removed" ); 60 | equal( mqtt.clients.removeTest, undefined, "Removed client is really gone" ); 61 | }); 62 | 63 | })( jQuery ); 64 | -------------------------------------------------------------------------------- /tests/unit/notifier/notifier.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | AeroGear Notifier Test Suite 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
    AeroGear Notifier Qunit Test Suite
    25 |
    26 | 27 | 28 | -------------------------------------------------------------------------------- /tests/unit/notifier/simplepush.js: -------------------------------------------------------------------------------- 1 | (function( $ ) { 2 | 3 | module( "Notifier: SimplePush" ); 4 | 5 | test( "create - object", function() { 6 | expect( 2 ); 7 | 8 | var sp = AeroGear.Notifier({ 9 | name: "createTest", 10 | type: "SimplePush" 11 | }).clients; 12 | 13 | equal( Object.keys( sp ).length, 1, "1 Client created" ); 14 | equal( Object.keys( sp )[ 0 ], "createTest", "Client Name createTest" ); 15 | }); 16 | 17 | test( "create - array", function() { 18 | expect( 3 ); 19 | 20 | var sp = AeroGear.Notifier([ 21 | { 22 | name: "createTest", 23 | type: "SimplePush" 24 | }, 25 | { 26 | name: "createTest2", 27 | type: "SimplePush" 28 | }]).clients; 29 | 30 | equal( Object.keys( sp ).length, 2, "2 Clients created" ); 31 | equal( Object.keys( sp )[ 0 ], "createTest", "Client Name createTest" ); 32 | equal( Object.keys( sp )[ 1 ], "createTest2", "Client Name createTest2" ); 33 | }); 34 | 35 | // Add client test 36 | test( "add method - object", function() { 37 | expect( 2 ); 38 | 39 | var sp = AeroGear.Notifier().add({ 40 | name: "addTest", 41 | type: "SimplePush" 42 | }).clients; 43 | equal( Object.keys( sp ).length, 1, "Single Client added" ); 44 | equal( Object.keys( sp )[ 0 ], "addTest", "Client Name addTest" ); 45 | }); 46 | 47 | // Remove client test 48 | test( "remove method", function() { 49 | expect( 3 ); 50 | 51 | var sp = AeroGear.Notifier({ 52 | name: "removeTest", 53 | type: "SimplePush" 54 | }); 55 | equal( Object.keys( sp.clients ).length, 1, "Single Client added" ); 56 | 57 | sp.remove("removeTest"); 58 | equal( Object.keys( sp.clients ).length, 0, "Single Client removed" ); 59 | equal( sp.clients.removeTest, undefined, "Removed client is really gone" ); 60 | }); 61 | 62 | })( jQuery ); 63 | -------------------------------------------------------------------------------- /tests/unit/notifier/stompws.js: -------------------------------------------------------------------------------- 1 | (function( $ ) { 2 | 3 | module( "Notifier: STOMP Websocket" ); 4 | 5 | test( "create - object", function() { 6 | expect( 2 ); 7 | 8 | var stomp = AeroGear.Notifier({ 9 | name: "createTest", 10 | type: "stompws" 11 | }).clients; 12 | 13 | equal( Object.keys( stomp ).length, 1, "1 Client created" ); 14 | equal( Object.keys( stomp )[ 0 ], "createTest", "Client Name createTest" ); 15 | }); 16 | 17 | test( "create - array", function() { 18 | expect( 3 ); 19 | 20 | var stomp = AeroGear.Notifier([ 21 | { 22 | name: "createTest", 23 | type: "stompws" 24 | }, 25 | { 26 | name: "createTest2", 27 | type: "stompws" 28 | }]).clients; 29 | 30 | equal( Object.keys( stomp ).length, 2, "2 Clients created" ); 31 | equal( Object.keys( stomp )[ 0 ], "createTest", "Client Name createTest" ); 32 | equal( Object.keys( stomp )[ 1 ], "createTest2", "Client Name createTest2" ); 33 | }); 34 | 35 | // Add client test 36 | test( "add method", function() { 37 | expect( 2 ); 38 | 39 | var stomp = AeroGear.Notifier().add({ 40 | name: "addTest", 41 | type: "stompws" 42 | }).clients; 43 | equal( Object.keys( stomp ).length, 1, "Single Client added" ); 44 | equal( Object.keys( stomp )[ 0 ], "addTest", "Client Name addTest" ); 45 | }); 46 | 47 | // Remove client test 48 | test( "remove method", function() { 49 | expect( 3 ); 50 | 51 | var stomp = AeroGear.Notifier({ 52 | name: "removeTest", 53 | type: "stompws" 54 | }); 55 | equal( Object.keys( stomp.clients ).length, 1, "Single Client added" ); 56 | 57 | stomp.remove("removeTest"); 58 | equal( Object.keys( stomp.clients ).length, 0, "Single Client removed" ); 59 | equal( stomp.clients.removeTest, undefined, "Removed client is really gone" ); 60 | }); 61 | 62 | })( jQuery ); 63 | -------------------------------------------------------------------------------- /tests/unit/notifier/vertx.js: -------------------------------------------------------------------------------- 1 | (function( $ ) { 2 | 3 | module( "Notifier: Vert.x" ); 4 | 5 | test( "create - string (Vert.x is default adapter so a string name is also allowed)", function() { 6 | expect( 2 ); 7 | 8 | var vertx = AeroGear.Notifier("createTest").clients; 9 | 10 | equal( Object.keys( vertx ).length, 1, "1 Client created" ); 11 | equal( Object.keys( vertx )[ 0 ], "createTest", "Client Name createTest" ); 12 | }); 13 | 14 | test( "create - object", function() { 15 | expect( 2 ); 16 | 17 | var vertx = AeroGear.Notifier({ 18 | name: "createTest", 19 | type: "vertx" 20 | }).clients; 21 | 22 | equal( Object.keys( vertx ).length, 1, "1 Client created" ); 23 | equal( Object.keys( vertx )[ 0 ], "createTest", "Client Name createTest" ); 24 | }); 25 | 26 | test( "create - array", function() { 27 | expect( 3 ); 28 | 29 | var vertx = AeroGear.Notifier([ 30 | "createTest", 31 | { 32 | name: "createTest2", 33 | type: "vertx" 34 | }]).clients; 35 | 36 | equal( Object.keys( vertx ).length, 2, "2 Clients created" ); 37 | equal( Object.keys( vertx )[ 0 ], "createTest", "Client Name createTest" ); 38 | equal( Object.keys( vertx )[ 1 ], "createTest2", "Client Name createTest2" ); 39 | }); 40 | 41 | // Add client test 42 | test( "add method - string", function() { 43 | expect( 2 ); 44 | 45 | var vertx = AeroGear.Notifier().add("addTest").clients; 46 | equal( Object.keys( vertx ).length, 1, "Single Client added" ); 47 | equal( Object.keys( vertx )[ 0 ], "addTest", "Client Name addTest" ); 48 | }); 49 | 50 | test( "add method - object", function() { 51 | expect( 2 ); 52 | 53 | var vertx = AeroGear.Notifier().add({ 54 | name: "addTest", 55 | type: "vertx" 56 | }).clients; 57 | equal( Object.keys( vertx ).length, 1, "Single Client added" ); 58 | equal( Object.keys( vertx )[ 0 ], "addTest", "Client Name addTest" ); 59 | }); 60 | 61 | // Remove client test 62 | test( "remove method", function() { 63 | expect( 3 ); 64 | 65 | var vertx = AeroGear.Notifier("removeTest"); 66 | equal( Object.keys( vertx.clients ).length, 1, "Single Client added" ); 67 | 68 | vertx.remove("removeTest"); 69 | equal( Object.keys( vertx.clients ).length, 0, "Single Client removed" ); 70 | equal( vertx.clients.removeTest, undefined, "Removed client is really gone" ); 71 | }); 72 | 73 | })( jQuery ); 74 | -------------------------------------------------------------------------------- /tests/unit/simplepush/simplepush.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | AeroGear SimplePush Client Test Suite 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
    AeroGear SimplePush Qunit Test Suite
    17 |
    18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/unit/simplepush/simplepush.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | module("SimplePush Client - Configuration"); 4 | 5 | test("create - New SimplePush Client - SimplePush Server specified", function () { 6 | expect(7); 7 | 8 | var client = AeroGear.SimplePushClient({ 9 | simplePushServerURL: "https://localhost:7777/simplepush", 10 | useNative: false 11 | }); 12 | 13 | equal(client instanceof AeroGear.SimplePushClient, true, "client should be an instance of SimplePush Client"); 14 | ok(client.options, "Client options exist"); 15 | equal(Object.keys(client.options).length, 2, "Client options are set"); 16 | equal(client.options.simplePushServerURL, "https://localhost:7777/simplepush", "Client SimplePush Server URL is set"); 17 | equal(client.options.useNative, false, "Client useNative flag is set"); 18 | ok(client.simpleNotifier, "SimpleNotifier exists"); 19 | equal(client.simpleNotifier instanceof AeroGear.Notifier.adapters.SimplePush, true, "SimplePush Notifier is created"); 20 | }); 21 | 22 | test("create - New SimplePush Client - Native Mozilla SimplePush Server", function () { 23 | expect(6); 24 | 25 | var client = AeroGear.SimplePushClient({ 26 | useNative: true 27 | }); 28 | 29 | equal(client instanceof AeroGear.SimplePushClient, true, "client should be an instance of SimplePush Client"); 30 | ok(client.options, "Client options exist"); 31 | equal(client.options.simplePushServerURL, "wss://push.services.mozilla.com", "SimplePush Server URL is set to Mozilla SimplePush Server"); 32 | equal(client.options.useNative, true, "Client useNative flag is set"); 33 | ok(client.simpleNotifier, "SimpleNotifier exists"); 34 | equal(client.simpleNotifier instanceof AeroGear.Notifier.adapters.SimplePush, true, "SimplePush Notifier is created"); 35 | }); 36 | 37 | })(jQuery); 38 | -------------------------------------------------------------------------------- /tests/unit/sync/diff-sync-client.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Differential Synchronization JavaScript Client Test Suite 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
    Core tests
    19 |
    20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/unit/sync/diff-sync-client.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | module( 'Diff Sync integration test' ); 4 | 5 | test( 'AeroGear.DiffSyncClient should support creation without the new keyword', function() { 6 | var client = AeroGear.DiffSyncClient( { serverUrl: 'ws://localhost:7777/sync' } ); 7 | ok( client , 'Should be no problem not using new when creating' ); 8 | }); 9 | 10 | test( 'serverUrl is mandatory', function() { 11 | throws( function() { AeroGear.DiffSyncClient(); } , Error, "'serverUrl' must be specified" ); 12 | }); 13 | 14 | test( 'member access', function() { 15 | var client = AeroGear.DiffSyncClient( { serverUrl: 'ws://localhost:7777/sync' } ); 16 | equal( client.serverUrl, undefined, 'Should not be able to access private members serverUrl' ); 17 | equal( client.sendQueue, undefined, 'Should not be able to access private members sendQueue' ); 18 | equal( client.ws, undefined, 'Should not be able to access private members ws' ); 19 | }); 20 | 21 | test( 'remove document method should err', function() { 22 | var client = AeroGear.DiffSyncClient( { serverUrl: 'ws://localhost:7777/sync' } ); 23 | 24 | try { 25 | client.removeDoc('12345'); 26 | } catch (err) { 27 | equal(err.message, 'Method Not Yet Implemented'); 28 | } 29 | }); 30 | 31 | function uuid() 32 | { 33 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function( c ) { 34 | var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8); 35 | return v.toString( 16 ); 36 | }); 37 | } 38 | 39 | })(); 40 | -------------------------------------------------------------------------------- /tests/unit/sync/diff-sync-engine-dmp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Differential Synchronization Client Engine Test Suite 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
    Diff Sync Engine tests
    18 |
    19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/unit/sync/diff-sync-engine-dmp.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | module( 'Sync Engine test' ); 4 | 5 | test ( 'AeroGear.DiffSyncEngine should support creation without the new keyword', function() { 6 | var engine = AeroGear.DiffSyncEngine({type: 'diffMatchPatch'}); 7 | ok( engine , 'Should be no problem not using new when creating' ); 8 | }); 9 | 10 | test( 'add document', function() { 11 | var engine = AeroGear.DiffSyncEngine({type: 'diffMatchPatch'}), doc = { id: 1234, clientId: 'client1', content: { name: 'Fletch' } }; 12 | engine.addDocument( { id: 1234, clientId: 'client1', content: { name: 'Fletch' } } ); 13 | var actualDoc = engine.getDocument( 1234 ); 14 | equal( actualDoc.id, 1234, 'Document id should match' ); 15 | }); 16 | 17 | test( 'diff document', function() { 18 | var engine = AeroGear.DiffSyncEngine({type: 'diffMatchPatch'}); 19 | var doc = { id: 1234, clientId: 'client1', content: { name: 'Fletch' } }; 20 | engine.addDocument( doc ); 21 | 22 | // update the name field 23 | doc.content.name = 'Mr.Poon'; 24 | 25 | var patchMsg = engine.diff( doc ); 26 | equal ( patchMsg.msgType, 'patch', 'The message type should be "patch"'); 27 | equal ( patchMsg.id, 1234, 'document id should be 1234'); 28 | equal ( patchMsg.clientId, 'client1', 'clientId should be client1'); 29 | 30 | var edit = patchMsg.edits[0]; 31 | equal ( edit.clientVersion, 0, 'version should be zero'); 32 | equal ( edit.serverVersion, 0, 'version should be zero'); 33 | equal ( edit.checksum, '', 'checksum is currently not implemented.'); 34 | 35 | var diffs = edit.diffs; 36 | ok( diffs instanceof Array, 'diffs should be an array of tuples' ); 37 | ok( diffs.length === 4, 'there should be 4 diff tuples generated'); 38 | equal ( diffs[0].operation, 'UNCHANGED', 'operation should be UNCHANGED'); 39 | equal ( diffs[0].text, '{"name":"', 'should not change the "name" field'); 40 | equal ( diffs[1].operation, 'DELETE' ,'operation should be DELETE'); 41 | equal ( diffs[1].text, 'Fletch', 'Fletch was the name before the update'); 42 | equal ( diffs[2].operation, 'ADD', 'operation should be ADD'); 43 | equal ( diffs[2].text, 'Mr.Poon', 'Mr.Poon is the new name'); 44 | equal ( diffs[3].operation, "UNCHANGED", 'operation should be UNCHANGED'); 45 | equal ( diffs[3].text, '"}', 'closing bracket'); 46 | }); 47 | 48 | test( 'patch document', function() { 49 | var engine = AeroGear.DiffSyncEngine({type: 'diffMatchPatch'}); 50 | var doc = { id: 1234, clientId: 'client1', content: {name: 'Fletch' } }; 51 | engine.addDocument( doc ); 52 | 53 | var shadowDoc = engine.getShadow( doc.id ); 54 | 55 | shadowDoc.content.name = 'Mr.Poon'; 56 | 57 | var patch = engine.patchDocument( shadowDoc ); 58 | equal( patch[1][0], true, 'patch should have been successful.' ); 59 | equal( patch[0], '{"name":"Mr.Poon"}', 'name should have been updated to Mr.Poon' ); 60 | 61 | doc = engine.getDocument( doc.id ); 62 | equal( doc.content.name, 'Mr.Poon', 'name should be updated'); 63 | }); 64 | 65 | test( 'patch shadow - content is a String', function() { 66 | var engine = AeroGear.DiffSyncEngine({type: 'diffMatchPatch'}); 67 | var dmp = new diff_match_patch(); 68 | var content = 'Fletch'; 69 | var doc = { id: 1234, clientId: 'client1', content: content }; 70 | var shadow; 71 | engine.addDocument( doc ); 72 | doc.content = 'John Coctolstol'; 73 | 74 | shadow = engine.getShadow( doc.id ); 75 | 76 | var patchMsg = { 77 | msgType: 'patch', 78 | id: doc.id, 79 | clientId: shadow.clientId, 80 | edits: [{ 81 | clientVersion: shadow.clientVersion, 82 | serverVersion: shadow.serverVersion, 83 | // currently not implemented but we probably need this for checking the client and server shadow are identical be for patching. 84 | checksum: '', 85 | diffs: engine._asAeroGearDiffs( dmp.diff_main( JSON.stringify( shadow.content ), JSON.stringify( doc.content ) ) ) 86 | }] 87 | }; 88 | //var patchMsg = engine.diff( doc ); 89 | console.log('patchMsg', patchMsg); 90 | 91 | var updatedShadow = engine.patchShadow( patchMsg ); 92 | console.log(updatedShadow); 93 | equal( updatedShadow.content, 'John Coctolstol', 'name should have been updated to John Coctolstol' ); 94 | equal( shadow.serverVersion, 1, 'Server version should have been updated.' ); 95 | equal( shadow.clientVersion, 0, 'Client version should not have been updated.' ); 96 | }); 97 | 98 | test( 'patch shadow - content is an Object', function() { 99 | var engine = AeroGear.DiffSyncEngine({type: 'diffMatchPatch'}); 100 | var dmp = new diff_match_patch(); 101 | var content = { name: 'Fletch' }; 102 | var doc = { id: 1234, clientId: 'client1', content: content }; 103 | var shadow; 104 | engine.addDocument( doc ); 105 | doc.content.name = 'John Coctolstol'; 106 | 107 | shadow = engine.getShadow( doc.id ); 108 | 109 | var patchMsg = { 110 | msgType: 'patch', 111 | id: doc.id, 112 | clientId: shadow.clientId, 113 | edits: [{ 114 | clientVersion: shadow.clientVersion, 115 | serverVersion: shadow.serverVersion, 116 | // currently not implemented but we probably need this for checking the client and server shadow are identical be for patching. 117 | checksum: '', 118 | diffs: engine._asAeroGearDiffs( dmp.diff_main( JSON.stringify( shadow.content ), JSON.stringify( doc.content ) ) ) 119 | }] 120 | }; 121 | //var patchMsg = engine.diff( doc ); 122 | console.log('patchMsg', patchMsg); 123 | 124 | var updatedShadow = engine.patchShadow( patchMsg ); 125 | console.log(updatedShadow); 126 | equal( JSON.stringify(updatedShadow.content), '{"name":"John Coctolstol"}', 'name should have been updated to John Coctolstol' ); 127 | equal( shadow.serverVersion, 1, 'Server version should have been updated.' ); 128 | equal( shadow.clientVersion, 0, 'Client version should not have been updated.' ); 129 | }); 130 | 131 | test( 'already seen edit should be deleted', function() { 132 | var engine = AeroGear.DiffSyncEngine({type: 'diffMatchPatch'}); 133 | var dmp = new diff_match_patch(); 134 | var content = { name: 'Fletch' }; 135 | var doc = { id: 1234, clientId: 'client1', content: content }; 136 | var shadow; 137 | engine.addDocument( doc ); 138 | doc.content.name = 'John Coctolstol'; 139 | 140 | shadow = engine.getShadow( doc.id ); 141 | var patchMsg = { 142 | msgType: 'patch', 143 | id: doc.id, 144 | clientId: shadow.clientId, 145 | edits: [{ 146 | clientVersion: shadow.clientVersion, 147 | serverVersion: shadow.serverVersion, 148 | checksum: '', 149 | diffs: engine._asAeroGearDiffs( dmp.diff_main( JSON.stringify( shadow.content ), JSON.stringify( doc.content ) ) ) 150 | }] 151 | }; 152 | 153 | // patch twice, second patch should not change the outcome and should simply be discarded. 154 | engine.patchShadow( patchMsg ); 155 | var updatedShadow = engine.patchShadow( patchMsg ); 156 | 157 | equal( JSON.stringify(updatedShadow.content), '{"name":"John Coctolstol"}', 'name should have been updated to John Coctolstol' ); 158 | equal( shadow.serverVersion, 1, 'Server version should have been updated.' ); 159 | equal( shadow.clientVersion, 0, 'Client version should not have been updated.' ); 160 | }); 161 | 162 | test( 'restore from backup', function() { 163 | var engine = AeroGear.DiffSyncEngine({type: 'diffMatchPatch'}); 164 | var dmp = new diff_match_patch(); 165 | var content = { name: 'Fletch' }; 166 | var doc = { id: 1234, clientId: 'client1', content: content }; 167 | var shadow; 168 | engine.addDocument( doc ); 169 | doc.content.name = 'John Coctolstol'; 170 | 171 | shadow = engine.getShadow( doc.id ); 172 | var patchMsg = { 173 | msgType: 'patch', 174 | id: doc.id, 175 | clientId: shadow.clientId, 176 | edits: [{ 177 | clientVersion: 0, 178 | serverVersion: 0, 179 | checksum: '', 180 | diffs: engine._asAeroGearDiffs( dmp.diff_main( JSON.stringify( shadow.content ), JSON.stringify( doc.content ) ) ) 181 | }] 182 | }; 183 | 184 | // simulate that the client has performed a diff which will increment the client version. 185 | shadow.clientVersion = 1; 186 | engine._saveShadow( shadow ); 187 | 188 | var updatedShadow = engine.patchShadow( patchMsg ); 189 | 190 | equal( JSON.stringify(updatedShadow.content), '{"name":"John Coctolstol"}', 'name should have been updated to John Coctolstol' ); 191 | equal( shadow.serverVersion, 0, 'Server version should have been updated.' ); 192 | equal( shadow.clientVersion, 1, 'Client version should not have been updated.' ); 193 | }); 194 | })(); 195 | -------------------------------------------------------------------------------- /tests/unit/sync/diff-sync-engine-json-patch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Differential Synchronization Client Engine Test Suite 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
    Diff Sync Engine tests
    18 |
    19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/unit/sync/diff-sync-engine-json-patch.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | module( 'JSON Patch - Sync Engine test' ); 4 | 5 | test ( 'JSON Patch should be the default engine', function() { 6 | var engine = AeroGear.DiffSyncEngine(); 7 | equal( engine instanceof AeroGear.DiffSyncEngine.adapters.jsonPatch , true, 'Should be an instance of jsonpatch adapter' ); 8 | }); 9 | 10 | test( 'add document', function() { 11 | var engine = AeroGear.DiffSyncEngine(), doc = { id: 1234, clientId: 'client1', content: { name: 'Fletch' } }; 12 | engine.addDocument( { id: 1234, clientId: 'client1', content: { name: 'Fletch' } } ); 13 | var actualDoc = engine.getDocument( 1234 ); 14 | equal( actualDoc.id, 1234, 'Document id should match' ); 15 | }); 16 | 17 | test( 'diff document', function() { 18 | var engine = AeroGear.DiffSyncEngine(); 19 | var doc = { id: 1234, clientId: 'client1', content: { name: 'Fletch' } }; 20 | engine.addDocument( doc ); 21 | 22 | // update the name field 23 | doc.content.name = 'Mr.Poon'; 24 | 25 | var patchMsg = engine.diff( doc ); 26 | equal ( patchMsg.msgType, 'patch', 'The message type should be "patch"'); 27 | equal ( patchMsg.id, 1234, 'document id should be 1234'); 28 | equal ( patchMsg.clientId, 'client1', 'clientId should be client1'); 29 | 30 | var edit = patchMsg.edits[0]; 31 | equal ( edit.clientVersion, 0, 'version should be zero'); 32 | equal ( edit.serverVersion, 0, 'version should be zero'); 33 | equal ( edit.checksum, '', 'checksum is currently not implemented.'); 34 | 35 | var diffs = edit.diffs; 36 | ok( diffs instanceof Array, 'diffs should be an array of tuples' ); 37 | ok( diffs.length === 1, 'there should be 1 diff tuples generated'); 38 | //{op: "replace", path: "/name", value: "Mr.Poon"} 39 | equal ( diffs[0].op, 'replace', 'operation should be replace'); 40 | equal ( diffs[0].value, 'Mr.Poon','value should be "Mr. Poon"'); 41 | equal ( diffs[0].path, '/name','path should be /name'); 42 | }); 43 | 44 | test( 'patch document', function() { 45 | var engine = AeroGear.DiffSyncEngine(); 46 | var doc = { id: 1234, clientId: 'client1', content: {name: 'Fletch' } }; 47 | engine.addDocument( doc ); 48 | 49 | var shadowDoc = engine.getShadow( doc.id ); 50 | 51 | shadowDoc.content.name = 'Mr.Poon'; 52 | 53 | var patch = engine.patchDocument( shadowDoc ); 54 | equal( patch, true, 'patch should have been successful.' ); 55 | 56 | doc = engine.getDocument( doc.id ); 57 | equal( doc.content.name, 'Mr.Poon', 'name should be updated'); 58 | }); 59 | 60 | test( 'patch shadow - content is an Object', function() { 61 | var engine = AeroGear.DiffSyncEngine(); 62 | var content = { name: 'Fletch' }; 63 | var doc = { id: 1234, clientId: 'client1', content: content }; 64 | var shadow; 65 | engine.addDocument( doc ); 66 | doc.content.name = 'John Coctolstol'; 67 | 68 | shadow = engine.getShadow( doc.id ); 69 | 70 | var patchMsg = { 71 | msgType: 'patch', 72 | id: doc.id, 73 | clientId: shadow.clientId, 74 | edits: [{ 75 | clientVersion: shadow.clientVersion, 76 | serverVersion: shadow.serverVersion, 77 | // currently not implemented but we probably need this for checking the client and server shadow are identical be for patching. 78 | checksum: '', 79 | diffs: jsonpatch.compare(shadow.content, doc.content) 80 | }] 81 | }; 82 | //var patchMsg = engine.diff( doc ); 83 | console.log('patchMsg', patchMsg); 84 | 85 | var updatedShadow = engine.patchShadow( patchMsg ); 86 | equal( JSON.stringify(updatedShadow.content), '{"name":"John Coctolstol"}', 'name should have been updated to John Coctolstol' ); 87 | equal( shadow.serverVersion, 1, 'Server version should have been updated.' ); 88 | equal( shadow.clientVersion, 0, 'Client version should not have been updated.' ); 89 | }); 90 | 91 | test( 'already seen edit should be deleted', function() { 92 | var engine = AeroGear.DiffSyncEngine(); 93 | var content = { name: 'Fletch' }; 94 | var doc = { id: 1234, clientId: 'client1', content: content }; 95 | var shadow; 96 | engine.addDocument( doc ); 97 | doc.content.name = 'John Coctolstol'; 98 | 99 | shadow = engine.getShadow( doc.id ); 100 | var patchMsg = { 101 | msgType: 'patch', 102 | id: doc.id, 103 | clientId: shadow.clientId, 104 | edits: [{ 105 | clientVersion: shadow.clientVersion, 106 | serverVersion: shadow.serverVersion, 107 | checksum: '', 108 | diffs: jsonpatch.compare(shadow.content, doc.content) 109 | }] 110 | }; 111 | 112 | // patch twice, second patch should not change the outcome and should simply be discarded. 113 | engine.patchShadow( patchMsg ); 114 | var updatedShadow = engine.patchShadow( patchMsg ); 115 | 116 | equal( JSON.stringify(updatedShadow.content), '{"name":"John Coctolstol"}', 'name should have been updated to John Coctolstol' ); 117 | equal( shadow.serverVersion, 1, 'Server version should have been updated.' ); 118 | equal( shadow.clientVersion, 0, 'Client version should not have been updated.' ); 119 | }); 120 | 121 | test( 'restore from backup', function() { 122 | var engine = AeroGear.DiffSyncEngine(); 123 | var content = { name: 'Fletch' }; 124 | var doc = { id: 1234, clientId: 'client1', content: content }; 125 | var shadow; 126 | engine.addDocument( doc ); 127 | doc.content.name = 'John Coctolstol'; 128 | 129 | shadow = engine.getShadow( doc.id ); 130 | var patchMsg = { 131 | msgType: 'patch', 132 | id: doc.id, 133 | clientId: shadow.clientId, 134 | edits: [{ 135 | clientVersion: 0, 136 | serverVersion: 0, 137 | checksum: '', 138 | diffs: jsonpatch.compare(shadow.content, doc.content) 139 | }] 140 | }; 141 | 142 | // simulate that the client has performed a diff which will increment the client version. 143 | shadow.clientVersion = 1; 144 | engine._saveShadow( shadow ); 145 | 146 | var updatedShadow = engine.patchShadow( patchMsg ); 147 | 148 | equal( JSON.stringify(updatedShadow.content), '{"name":"John Coctolstol"}', 'name should have been updated to John Coctolstol' ); 149 | equal( shadow.serverVersion, 0, 'Server version should have been updated.' ); 150 | equal( shadow.clientVersion, 1, 'Client version should not have been updated.' ); 151 | }); 152 | })(); 153 | -------------------------------------------------------------------------------- /tests/unit/unifiedpush/unifiedpush.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | AeroGear UnifiedPush Client Test Suite 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    AeroGear UnifiedPush Qunit Test Suite
    15 |
    16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/unit/unifiedpush/unifiedpush.js: -------------------------------------------------------------------------------- 1 | (function( $ ) { 2 | 3 | module( "UnifiedPush Client - General" ); 4 | 5 | test( "create - New UnifiedPush Client with no arguments", function() { 6 | expect( 1 ); 7 | 8 | raises( function() { 9 | AeroGear.UnifiedPushClient(); 10 | }, 11 | "UnifiedPushClientException", 12 | "throws UnifiedPushClientException" 13 | ); 14 | }); 15 | 16 | test( "create - New UnifiedPush Client with 1 argument", function() { 17 | expect( 1 ); 18 | 19 | raises( function() { 20 | AeroGear.UnifiedPushClient( "Arg" ); 21 | }, 22 | "UnifiedPushClientException", 23 | "throws UnifiedPushClientException" 24 | ); 25 | }); 26 | 27 | test( "create - New UnifiedPush Client with 2 arguments", function() { 28 | expect( 1 ); 29 | 30 | raises( function() { 31 | AeroGear.UnifiedPushClient( "Arg", "Secret" ); 32 | }, 33 | "UnifiedPushClientException", 34 | "throws UnifiedPushClientException" 35 | ); 36 | }); 37 | 38 | test( "create - New UnifiedPush Client with arguments", function() { 39 | expect(3); 40 | 41 | var client = AeroGear.UnifiedPushClient( "VARIANT_ID", "SECRET", "URL" ); 42 | 43 | equal( client instanceof AeroGear.UnifiedPushClient, true, "client should be an instance of UPS Client" ); 44 | equal( client.hasOwnProperty( "registerWithPushServer" ), true, "client should have a registerWithPushServer method" ); 45 | equal( client.hasOwnProperty( "unregisterWithPushServer" ), true, "client should have a unregisterWithPushServer method" ); 46 | }); 47 | 48 | module( "UnifiedPush Client - Register and UnRegister", { 49 | setup: function() { 50 | var that = this; 51 | this.requests = []; 52 | this.xhr = sinon.useFakeXMLHttpRequest(); 53 | this.xhr.onCreate = function( xhr ) { 54 | that.requests.push( xhr ); 55 | }; 56 | }, 57 | teardown: function() { 58 | this.xhr.restore(); 59 | } 60 | }); 61 | 62 | test( "call register with no device token", function() { 63 | expect(1); 64 | 65 | raises( function() { 66 | AeroGear.UnifiedPushClient( "VARIANT_ID", "SECRET", "URL" ).registerWithPushServer(); 67 | }, 68 | "UnifiedPushRegistrationException", 69 | "throws UnifiedPushRegistrationException" 70 | ); 71 | }); 72 | 73 | test( "call register with proper settings", function() { 74 | expect(4); 75 | 76 | var client, settings, ret; 77 | 78 | settings = {}; 79 | 80 | settings.metadata = { 81 | deviceToken: "12345" 82 | }; 83 | 84 | client = AeroGear.UnifiedPushClient( "VARIANT_ID", "SECRET", "/api/pushserver" ); 85 | 86 | ret = client.registerWithPushServer( settings ); 87 | var request = this.requests[0]; 88 | 89 | equal( ret instanceof Promise, true, "the return value should be an es6 promise" ); 90 | equal( request.url, "/api/pushserver" + "/rest/registry/device", "request.url should be the concatenation of push server url and device registry url" ); 91 | equal( request.method, "POST", "request.method should a POST request" ); 92 | equal( JSON.parse( request.requestBody ).deviceToken, "12345", "request body should have a request token param" ); 93 | 94 | }); 95 | 96 | test( "call register with proper settings with a trailing slash", function() { 97 | expect(4); 98 | 99 | var client, settings, ret; 100 | 101 | settings = {}; 102 | 103 | settings.metadata = { 104 | deviceToken: "12345" 105 | }; 106 | 107 | client = AeroGear.UnifiedPushClient( "VARIANT_ID", "SECRET", "/api/pushserver/" ); 108 | 109 | ret = client.registerWithPushServer( settings ); 110 | var request = this.requests[0]; 111 | 112 | equal( ret instanceof Promise, true, "the return value should be an es6 promise" ); 113 | equal( request.url, "/api/pushserver" + "/rest/registry/device", "request.url should be the concatenation of push server url and device registry url" ); 114 | equal( request.method, "POST", "request.method should a POST request" ); 115 | equal( JSON.parse( request.requestBody ).deviceToken, "12345", "request body should have a request token param" ); 116 | 117 | }); 118 | 119 | test( "call unregister", function() { 120 | expect(3); 121 | 122 | var client, settings, ret, 123 | deviceToken = "12345"; 124 | 125 | settings = {}; 126 | 127 | client = AeroGear.UnifiedPushClient( "VARIANT_ID", "SECRET", "/api/pushserver" ); 128 | 129 | ret = client.unregisterWithPushServer( deviceToken, settings ); 130 | var request = this.requests[0]; 131 | 132 | equal( ret instanceof Promise, true, "the return value should be an es6 promise" ); 133 | equal( request.url, "/api/pushserver" + "/rest/registry/device/" + deviceToken, "request.url should be the concatenation of push server url, device registry url and device token" ); 134 | equal( request.method, "DELETE", "request.method should a DELETE request" ); 135 | 136 | }); 137 | 138 | module( "UnifiedPush Client - Register fake server", { 139 | setup: function () { 140 | var testData = { 141 | "id": "402880e43fa95bb3013fa960f9ee0002", 142 | "deviceToken": "12345" 143 | }; 144 | this.server = sinon.fakeServer.create(); 145 | this.server.respondWith( "POST", "/api/pushserver/rest/registry/device", [ 200, { "Content-Type": "application/json" }, JSON.stringify(testData)]); 146 | }, 147 | teardown: function () { 148 | this.server.restore(); 149 | } 150 | }); 151 | 152 | asyncTest( "register successfully with a promise", function() { 153 | expect(2); 154 | 155 | var client, settings, ret; 156 | 157 | settings = {}; 158 | 159 | settings.metadata = { 160 | deviceToken: "12345" 161 | }; 162 | 163 | client = AeroGear.UnifiedPushClient( "VARIANT_ID", "SECRET", "/api/pushserver" ); 164 | 165 | ret = client.registerWithPushServer( settings ); 166 | this.server.respond(); 167 | 168 | ret.then( 169 | function( promiseValue ) { 170 | equal( promiseValue.agXHR.status, 200, "should be a 200" ); 171 | start(); 172 | } 173 | ); 174 | 175 | equal( ret instanceof Promise, true, "the return value should be an es6 promise" ); 176 | 177 | }); 178 | 179 | asyncTest( "register unsuccessfully with a promise", function() { 180 | expect(2); 181 | 182 | var client, settings, ret; 183 | 184 | settings = {}; 185 | 186 | settings.metadata = { 187 | deviceToken: "12345" 188 | }; 189 | 190 | client = AeroGear.UnifiedPushClient( "VARIANT_ID", "SECRET", "/api/pushserv" ); 191 | 192 | ret = client.registerWithPushServer( settings ); 193 | this.server.respond(); 194 | 195 | ret.catch( 196 | function( promiseValue ) { 197 | equal( promiseValue.agXHR.status, 404, "should be a 404" ); 198 | start(); 199 | } 200 | ); 201 | 202 | equal( ret instanceof Promise, true, "the return value should be an es6 promise" ); 203 | 204 | }); 205 | 206 | module( "UnifiedPush Client - unRegister fake server", { 207 | setup: function () { 208 | this.server = sinon.fakeServer.create(); 209 | this.server.respondWith( "DELETE", "/api/pushserver/rest/registry/device/12345", [ 204, { "Content-Type": "application/json" }, JSON.stringify({})]); 210 | }, 211 | teardown: function () { 212 | this.server.restore(); 213 | } 214 | }); 215 | 216 | asyncTest( "unregister successfully with a promise", function() { 217 | expect(2); 218 | 219 | var client, settings, ret, 220 | deviceToken = "12345"; 221 | 222 | settings = {}; 223 | 224 | client = AeroGear.UnifiedPushClient( "VARIANT_ID", "SECRET", "/api/pushserver" ); 225 | 226 | ret = client.unregisterWithPushServer( deviceToken, settings ); 227 | this.server.respond(); 228 | 229 | ret.then( 230 | function( promiseValue ) { 231 | equal( promiseValue.agXHR.status, 204, "should be a 204" ); 232 | start(); 233 | } 234 | ); 235 | 236 | equal( ret instanceof Promise, true, "the return value should be an es6 promise" ); 237 | }); 238 | 239 | asyncTest( "unregister unsuccessfully with a promise", function() { 240 | expect(2); 241 | 242 | var client, settings, ret; 243 | 244 | settings = {}; 245 | 246 | client = AeroGear.UnifiedPushClient( "VARIANT_ID", "SECRET", "/api/pushserv" ); 247 | 248 | ret = client.unregisterWithPushServer( settings ); 249 | this.server.respond(); 250 | 251 | ret.catch( 252 | function( promiseValue ) { 253 | equal( promiseValue.agXHR.status, 404, "should be a 404" ); 254 | start(); 255 | } 256 | ); 257 | 258 | equal( ret instanceof Promise, true, "the return value should be an es6 promise" ); 259 | 260 | }); 261 | })( jQuery ); 262 | -------------------------------------------------------------------------------- /tests/vendor/json-patch-duplex.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * https://github.com/Starcounter-Jack/Fast-JSON-Patch 3 | * json-patch-duplex.js 0.5.0 4 | * (c) 2013 Joachim Wester 5 | * MIT license 6 | */ 7 | var jsonpatch;!function(a){function b(a,c){switch(typeof a){case"undefined":case"boolean":case"string":case"number":return a===c;case"object":if(null===a)return null===c;if(A(a)){if(!A(c)||a.length!==c.length)return!1;for(var d=0,e=a.length;e>d;d++)if(!b(a[d],c[d]))return!1;return!0}var f=s(c),g=f.length;if(s(a).length!==g)return!1;for(var d=0;g>d;d++)if(!b(a[d],c[d]))return!1;return!0;default:return!1}}function c(a){return-1===a.indexOf("/")&&-1===a.indexOf("~")?a:a.replace(/~/g,"~0").replace(/\//g,"~1")}function d(a,b){var e;for(var f in a)if(a.hasOwnProperty(f)){if(a[f]===b)return c(f)+"/";if("object"==typeof a[f]&&(e=d(a[f],b),""!=e))return c(f)+"/"+e}return""}function e(a,b){if(a===b)return"/";var c=d(a,b);if(""===c)throw new Error("Object not found in root");return"/"+c}function f(a){for(var b=0,c=x.length;c>b;b++)if(x[b].obj===a)return x[b]}function g(a,b){for(var c=0,d=a.observers.length;d>c;c++)if(a.observers[c].callback===b)return a.observers[c].observer}function h(a,b){for(var c=0,d=a.observers.length;d>c;c++)if(a.observers[c].observer===b)return void a.observers.splice(c,1)}function i(a,b){n(b),Object.observe?m(b,a):clearTimeout(b.next);var c=f(a);h(c,b)}function j(a){return"object"==typeof a?JSON.parse(JSON.stringify(a)):a}function k(a,b){var c,d=[],h=a,i=f(a);if(i?c=g(i,b):(i=new y(a),x.push(i)),c)return c;if(Object.observe)c=function(f){m(c,a),l(c,a);for(var g=0,i=f.length;i>g;){if(("length"!==f[g].name||!A(f[g].object))&&"__Jasmine_been_here_before__"!==f[g].name){var j=f[g].type;switch(j){case"new":j="add";break;case"deleted":j="delete";break;case"updated":j="update"}w[j].call(f[g],d,e(h,f[g].object))}g++}d&&b&&b(d),c.patches=d,d=[]};else if(c={},i.value=j(a),b){c.callback=b,c.next=null;var k=this.intervals||[100,1e3,1e4,6e4];if(void 0===k.push)throw new Error("jsonpatch.intervals must be an array");var o=0,p=function(){n(c)},q=function(){clearTimeout(c.next),c.next=setTimeout(function(){p(),o=0,c.next=setTimeout(r,k[o++])},0)},r=function(){p(),o==k.length&&(o=k.length-1),c.next=setTimeout(r,k[o++])};"undefined"!=typeof window&&(window.addEventListener?(window.addEventListener("mousedown",q),window.addEventListener("mouseup",q),window.addEventListener("keydown",q)):(window.attachEvent("onmousedown",q),window.attachEvent("onmouseup",q),window.attachEvent("onkeydown",q))),c.next=setTimeout(r,k[o++])}return c.patches=d,c.object=a,i.observers.push(new z(b,c)),l(c,a)}function l(a,b){if(Object.observe){Object.observe(b,a);for(var c in b)if(b.hasOwnProperty(c)){var d=b[c];d&&"object"==typeof d&&l(a,d)}}return a}function m(a,b){if(Object.observe){Object.unobserve(b,a);for(var c in b)if(b.hasOwnProperty(c)){var d=b[c];d&&"object"==typeof d&&m(a,d)}}return a}function n(a){if(Object.observe)Object.deliverChangeRecords(a);else{for(var b,c=0,d=x.length;d>c;c++)if(x[c].obj===a.object){b=x[c];break}o(b.value,a.object,a.patches,""),a.patches.length&&q(b.value,a.patches)}var e=a.patches;return e.length>0&&(a.patches=[],a.callback&&a.callback(e)),e}function o(a,b,d,e){for(var f=s(b),g=s(a),h=!1,i=!1,k=g.length-1;k>=0;k--){var l=g[k],m=a[l];if(b.hasOwnProperty(l)){var n=b[l];m instanceof Object?o(m,n,d,e+"/"+c(l)):m!=n&&(h=!0,d.push({op:"replace",path:e+"/"+c(l),value:j(n)}))}else d.push({op:"remove",path:e+"/"+c(l)}),i=!0}if(i||f.length!=g.length)for(var k=0;kc;){b=a.charCodeAt(c);{if(!(b>=48&&57>=b))return!1;c++}}return!0}function q(a,b){for(var c,d=!1,e=0,f=b.length;f>e;){c=b[e],e++;var g=c.path.split("/"),h=a,i=1,j=g.length;if(void 0===c.value&&("add"===c.op||"replace"===c.op||"test"===c.op))throw new Error("'value' MUST be defined");if(void 0===c.from&&("copy"===c.op||"move"===c.op))throw new Error("'from' MUST be defined");for(;;)if(A(h)){var k;if("-"===g[i])k=h.length;else{if(!p(g[i]))throw new Error("Expected an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index");k=parseInt(g[i],10)}if(i++,i>=j){d=u[c.op].call(c,h,k,a);break}h=h[k]}else{var l=g[i];if(void 0!==l){if(l&&-1!=l.indexOf("~")&&(l=l.replace(/~1/g,"/").replace(/~0/g,"~")),i++,i>=j){d=t[c.op].call(c,h,l,a);break}}else if(i++,i>=j){d=v[c.op].call(c,h,l,a);break}h=h[l]}}return d}function r(a,b){var c=[];return o(a,b,c,""),c}if(!a.observe){var s=function(){return Object.keys?Object.keys:function(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c);return b}}(),t={add:function(a,b){return a[b]=this.value,!0},remove:function(a,b){return delete a[b],!0},replace:function(a,b){return a[b]=this.value,!0},move:function(a,b,c){var d={op:"_get",path:this.from};return q(c,[d]),q(c,[{op:"remove",path:this.from}]),q(c,[{op:"add",path:this.path,value:d.value}]),!0},copy:function(a,b,c){var d={op:"_get",path:this.from};return q(c,[d]),q(c,[{op:"add",path:this.path,value:d.value}]),!0},test:function(a,c){return b(a[c],this.value)},_get:function(a,b){this.value=a[b]}},u={add:function(a,b){if(b>a.length)throw new Error("The specified index MUST NOT be greater than the number of elements in the array.");return a.splice(b,0,this.value),!0},remove:function(a,b){return a.splice(b,1),!0},replace:function(a,b){return a[b]=this.value,!0},move:t.move,copy:t.copy,test:t.test,_get:t._get},v={add:function(a){v.remove.call(this,a);for(var b in this.value)this.value.hasOwnProperty(b)&&(a[b]=this.value[b]);return!0},remove:function(a){for(var b in a)a.hasOwnProperty(b)&&t.remove.call(this,a,b);return!0},replace:function(a){return q(a,[{op:"remove",path:this.path}]),q(a,[{op:"add",path:this.path,value:this.value}]),!0},move:t.move,copy:t.copy,test:function(a){return JSON.stringify(a)===JSON.stringify(this.value)},_get:t._get},w={add:function(a,b){var d={op:"add",path:b+c(this.name),value:this.object[this.name]};a.push(d)},"delete":function(a,b){var d={op:"remove",path:b+c(this.name)};a.push(d)},update:function(a,b){var d={op:"replace",path:b+c(this.name),value:this.object[this.name]};a.push(d)}},x=[];a.intervals;var y=function(){function a(a){this.observers=[],this.obj=a}return a}(),z=function(){function a(a,b){this.callback=a,this.observer=b}return a}();a.unobserve=i,a.observe=k,a.generate=n;var A;A=Array.isArray?Array.isArray:function(a){return a.push&&"number"==typeof a.length},a.apply=q,a.compare=r}}(jsonpatch||(jsonpatch={})),"undefined"!=typeof exports&&(exports.apply=jsonpatch.apply,exports.observe=jsonpatch.observe,exports.unobserve=jsonpatch.unobserve,exports.generate=jsonpatch.generate,exports.compare=jsonpatch.compare); -------------------------------------------------------------------------------- /tests/vendor/qunit-composite.css: -------------------------------------------------------------------------------- 1 | iframe.qunit-subsuite { 2 | position: fixed; 3 | bottom: 0; 4 | left: 0; 5 | 6 | margin: 0; 7 | padding: 0; 8 | border-width: 1px 0 0; 9 | height: 45%; 10 | width: 100%; 11 | 12 | background: #fff; 13 | } 14 | -------------------------------------------------------------------------------- /tests/vendor/qunit-composite.js: -------------------------------------------------------------------------------- 1 | (function( QUnit ) { 2 | 3 | QUnit.extend( QUnit, { 4 | testSuites: function( suites ) { 5 | QUnit.begin(function() { 6 | QUnit.initIframe(); 7 | }); 8 | 9 | for ( var i = 0; i < suites.length; i++ ) { 10 | QUnit.runSuite( suites[i] ); 11 | } 12 | 13 | QUnit.done(function() { 14 | this.iframe.style.display = "none"; 15 | }); 16 | }, 17 | 18 | runSuite: function( suite ) { 19 | var path = suite; 20 | 21 | if ( QUnit.is( 'object', suite ) ) { 22 | path = suite.path; 23 | suite = suite.name; 24 | } 25 | 26 | asyncTest( suite, function() { 27 | QUnit.iframe.setAttribute( "src", path ); 28 | }); 29 | }, 30 | 31 | initIframe: function() { 32 | var body = document.body, 33 | iframe = this.iframe = document.createElement( "iframe" ), 34 | iframeWin; 35 | 36 | iframe.className = "qunit-subsuite"; 37 | body.appendChild( iframe ); 38 | 39 | function onIframeLoad() { 40 | var module, test, 41 | count = 0; 42 | 43 | if (iframe.src === "") { 44 | return; 45 | } 46 | 47 | iframeWin.QUnit.moduleStart(function( data ) { 48 | // capture module name for messages 49 | module = data.name; 50 | }); 51 | 52 | iframeWin.QUnit.testStart(function( data ) { 53 | // capture test name for messages 54 | test = data.name; 55 | }); 56 | iframeWin.QUnit.testDone(function() { 57 | test = null; 58 | }); 59 | 60 | iframeWin.QUnit.log(function( data ) { 61 | if (test === null) { 62 | return; 63 | } 64 | // pass all test details through to the main page 65 | var message = module + ": " + test + ": " + data.message; 66 | expect( ++count ); 67 | QUnit.push( data.result, data.actual, data.expected, message ); 68 | }); 69 | 70 | iframeWin.QUnit.done(function() { 71 | // start the wrapper test from the main page 72 | start(); 73 | }); 74 | } 75 | QUnit.addEvent( iframe, "load", onIframeLoad ); 76 | 77 | iframeWin = iframe.contentWindow; 78 | } 79 | }); 80 | 81 | QUnit.testStart(function( data ) { 82 | // update the test status to show which test suite is running 83 | QUnit.id( "qunit-testresult" ).innerHTML = "Running " + data.name + "...
     "; 84 | }); 85 | 86 | QUnit.testDone(function() { 87 | var i, 88 | current = QUnit.id( this.config.current.id ), 89 | children = current.children, 90 | src = this.iframe.src; 91 | 92 | // undo the auto-expansion of failed tests 93 | for ( i = 0; i < children.length; i++ ) { 94 | if ( children[i].nodeName === "OL" ) { 95 | children[i].style.display = "none"; 96 | } 97 | } 98 | 99 | QUnit.addEvent(current, "dblclick", function( e ) { 100 | var target = e && e.target ? e.target : window.event.srcElement; 101 | if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) { 102 | target = target.parentNode; 103 | } 104 | if ( window.location && target.nodeName.toLowerCase() === "strong" ) { 105 | window.location = src; 106 | } 107 | }); 108 | 109 | current.getElementsByTagName('a')[0].href = src; 110 | }); 111 | 112 | }( QUnit ) ); 113 | -------------------------------------------------------------------------------- /tests/vendor/qunit.css: -------------------------------------------------------------------------------- 1 | /** 2 | * QUnit v1.11.0 - A JavaScript Unit Testing Framework 3 | * 4 | * http://qunitjs.com 5 | * 6 | * Copyright 2012 jQuery Foundation and other contributors 7 | * Released under the MIT license. 8 | * http://jquery.org/license 9 | */ 10 | 11 | /** Font Family and Sizes */ 12 | 13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; 15 | } 16 | 17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 18 | #qunit-tests { font-size: smaller; } 19 | 20 | 21 | /** Resets */ 22 | 23 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { 24 | margin: 0; 25 | padding: 0; 26 | } 27 | 28 | 29 | /** Header */ 30 | 31 | #qunit-header { 32 | padding: 0.5em 0 0.5em 1em; 33 | 34 | color: #8699a4; 35 | background-color: #0d3349; 36 | 37 | font-size: 1.5em; 38 | line-height: 1em; 39 | font-weight: normal; 40 | 41 | border-radius: 5px 5px 0 0; 42 | -moz-border-radius: 5px 5px 0 0; 43 | -webkit-border-top-right-radius: 5px; 44 | -webkit-border-top-left-radius: 5px; 45 | } 46 | 47 | #qunit-header a { 48 | text-decoration: none; 49 | color: #c2ccd1; 50 | } 51 | 52 | #qunit-header a:hover, 53 | #qunit-header a:focus { 54 | color: #fff; 55 | } 56 | 57 | #qunit-testrunner-toolbar label { 58 | display: inline-block; 59 | padding: 0 .5em 0 .1em; 60 | } 61 | 62 | #qunit-banner { 63 | height: 5px; 64 | } 65 | 66 | #qunit-testrunner-toolbar { 67 | padding: 0.5em 0 0.5em 2em; 68 | color: #5E740B; 69 | background-color: #eee; 70 | overflow: hidden; 71 | } 72 | 73 | #qunit-userAgent { 74 | padding: 0.5em 0 0.5em 2.5em; 75 | background-color: #2b81af; 76 | color: #fff; 77 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 78 | } 79 | 80 | #qunit-modulefilter-container { 81 | float: right; 82 | } 83 | 84 | /** Tests: Pass/Fail */ 85 | 86 | #qunit-tests { 87 | list-style-position: inside; 88 | } 89 | 90 | #qunit-tests li { 91 | padding: 0.4em 0.5em 0.4em 2.5em; 92 | border-bottom: 1px solid #fff; 93 | list-style-position: inside; 94 | } 95 | 96 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { 97 | display: none; 98 | } 99 | 100 | #qunit-tests li strong { 101 | cursor: pointer; 102 | } 103 | 104 | #qunit-tests li a { 105 | padding: 0.5em; 106 | color: #c2ccd1; 107 | text-decoration: none; 108 | } 109 | #qunit-tests li a:hover, 110 | #qunit-tests li a:focus { 111 | color: #000; 112 | } 113 | 114 | #qunit-tests li .runtime { 115 | float: right; 116 | font-size: smaller; 117 | } 118 | 119 | .qunit-assert-list { 120 | margin-top: 0.5em; 121 | padding: 0.5em; 122 | 123 | background-color: #fff; 124 | 125 | border-radius: 5px; 126 | -moz-border-radius: 5px; 127 | -webkit-border-radius: 5px; 128 | } 129 | 130 | .qunit-collapsed { 131 | display: none; 132 | } 133 | 134 | #qunit-tests table { 135 | border-collapse: collapse; 136 | margin-top: .2em; 137 | } 138 | 139 | #qunit-tests th { 140 | text-align: right; 141 | vertical-align: top; 142 | padding: 0 .5em 0 0; 143 | } 144 | 145 | #qunit-tests td { 146 | vertical-align: top; 147 | } 148 | 149 | #qunit-tests pre { 150 | margin: 0; 151 | white-space: pre-wrap; 152 | word-wrap: break-word; 153 | } 154 | 155 | #qunit-tests del { 156 | background-color: #e0f2be; 157 | color: #374e0c; 158 | text-decoration: none; 159 | } 160 | 161 | #qunit-tests ins { 162 | background-color: #ffcaca; 163 | color: #500; 164 | text-decoration: none; 165 | } 166 | 167 | /*** Test Counts */ 168 | 169 | #qunit-tests b.counts { color: black; } 170 | #qunit-tests b.passed { color: #5E740B; } 171 | #qunit-tests b.failed { color: #710909; } 172 | 173 | #qunit-tests li li { 174 | padding: 5px; 175 | background-color: #fff; 176 | border-bottom: none; 177 | list-style-position: inside; 178 | } 179 | 180 | /*** Passing Styles */ 181 | 182 | #qunit-tests li li.pass { 183 | color: #3c510c; 184 | background-color: #fff; 185 | border-left: 10px solid #C6E746; 186 | } 187 | 188 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 189 | #qunit-tests .pass .test-name { color: #366097; } 190 | 191 | #qunit-tests .pass .test-actual, 192 | #qunit-tests .pass .test-expected { color: #999999; } 193 | 194 | #qunit-banner.qunit-pass { background-color: #C6E746; } 195 | 196 | /*** Failing Styles */ 197 | 198 | #qunit-tests li li.fail { 199 | color: #710909; 200 | background-color: #fff; 201 | border-left: 10px solid #EE5757; 202 | white-space: pre; 203 | } 204 | 205 | #qunit-tests > li:last-child { 206 | border-radius: 0 0 5px 5px; 207 | -moz-border-radius: 0 0 5px 5px; 208 | -webkit-border-bottom-right-radius: 5px; 209 | -webkit-border-bottom-left-radius: 5px; 210 | } 211 | 212 | #qunit-tests .fail { color: #000000; background-color: #EE5757; } 213 | #qunit-tests .fail .test-name, 214 | #qunit-tests .fail .module-name { color: #000000; } 215 | 216 | #qunit-tests .fail .test-actual { color: #EE5757; } 217 | #qunit-tests .fail .test-expected { color: green; } 218 | 219 | #qunit-banner.qunit-fail { background-color: #EE5757; } 220 | 221 | 222 | /** Result */ 223 | 224 | #qunit-testresult { 225 | padding: 0.5em 0.5em 0.5em 2.5em; 226 | 227 | color: #2b81af; 228 | background-color: #D2E0E6; 229 | 230 | border-bottom: 1px solid white; 231 | } 232 | #qunit-testresult .module-name { 233 | font-weight: bold; 234 | } 235 | 236 | /** Fixture */ 237 | 238 | #qunit-fixture { 239 | position: absolute; 240 | top: -10000px; 241 | left: -10000px; 242 | width: 1000px; 243 | height: 1000px; 244 | } 245 | -------------------------------------------------------------------------------- /tests/vendor/stomp.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.4.0 2 | (function() { 3 | var Byte, Client, Frame, Stomp, 4 | __hasProp = {}.hasOwnProperty; 5 | 6 | Byte = { 7 | LF: '\x0A', 8 | NULL: '\x00' 9 | }; 10 | 11 | Frame = (function() { 12 | 13 | function Frame(command, headers, body) { 14 | this.command = command; 15 | this.headers = headers != null ? headers : {}; 16 | this.body = body != null ? body : ''; 17 | } 18 | 19 | Frame.prototype.toString = function() { 20 | var lines, name, value, _ref; 21 | lines = [this.command]; 22 | _ref = this.headers; 23 | for (name in _ref) { 24 | if (!__hasProp.call(_ref, name)) continue; 25 | value = _ref[name]; 26 | lines.push("" + name + ":" + value); 27 | } 28 | if (this.body) { 29 | lines.push("content-length:" + ('' + this.body).length); 30 | } 31 | lines.push(Byte.LF + this.body); 32 | return lines.join(Byte.LF); 33 | }; 34 | 35 | Frame._unmarshallSingle = function(data) { 36 | var body, chr, command, divider, headerLines, headers, i, idx, len, line, start, trim, _i, _j, _ref, _ref1; 37 | divider = data.search(RegExp("" + Byte.LF + Byte.LF)); 38 | headerLines = data.substring(0, divider).split(Byte.LF); 39 | command = headerLines.shift(); 40 | headers = {}; 41 | trim = function(str) { 42 | return str.replace(/^\s+|\s+$/g, ''); 43 | }; 44 | line = idx = null; 45 | for (i = _i = 0, _ref = headerLines.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { 46 | line = headerLines[i]; 47 | idx = line.indexOf(':'); 48 | headers[trim(line.substring(0, idx))] = trim(line.substring(idx + 1)); 49 | } 50 | body = ''; 51 | start = divider + 2; 52 | if (headers['content-length']) { 53 | len = parseInt(headers['content-length']); 54 | body = ('' + data).substring(start, start + len); 55 | } else { 56 | chr = null; 57 | for (i = _j = start, _ref1 = data.length; start <= _ref1 ? _j < _ref1 : _j > _ref1; i = start <= _ref1 ? ++_j : --_j) { 58 | chr = data.charAt(i); 59 | if (chr === Byte.NULL) { 60 | break; 61 | } 62 | body += chr; 63 | } 64 | } 65 | return new Frame(command, headers, body); 66 | }; 67 | 68 | Frame.unmarshall = function(datas) { 69 | var data; 70 | return (function() { 71 | var _i, _len, _ref, _results; 72 | _ref = datas.split(RegExp("" + Byte.NULL + Byte.LF + "*")); 73 | _results = []; 74 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 75 | data = _ref[_i]; 76 | if ((data != null ? data.length : void 0) > 0) { 77 | _results.push(Frame._unmarshallSingle(data)); 78 | } 79 | } 80 | return _results; 81 | })(); 82 | }; 83 | 84 | Frame.marshall = function(command, headers, body) { 85 | var frame; 86 | frame = new Frame(command, headers, body); 87 | return frame.toString() + Byte.NULL; 88 | }; 89 | 90 | return Frame; 91 | 92 | })(); 93 | 94 | Client = (function() { 95 | 96 | function Client(ws) { 97 | this.ws = ws; 98 | this.ws.binaryType = "arraybuffer"; 99 | this.counter = 0; 100 | this.connected = false; 101 | this.heartbeat = { 102 | outgoing: 10000, 103 | incoming: 10000 104 | }; 105 | this.subscriptions = {}; 106 | } 107 | 108 | Client.prototype._transmit = function(command, headers, body) { 109 | var out; 110 | out = Frame.marshall(command, headers, body); 111 | if (typeof this.debug === "function") { 112 | this.debug(">>> " + out); 113 | } 114 | return this.ws.send(out); 115 | }; 116 | 117 | Client.prototype._setupHeartbeat = function(headers) { 118 | var serverIncoming, serverOutgoing, ttl, v, _ref, _ref1, 119 | _this = this; 120 | if ((_ref = headers.version) !== Stomp.VERSIONS.V1_1 && _ref !== Stomp.VERSIONS.V1_2) { 121 | return; 122 | } 123 | _ref1 = (function() { 124 | var _i, _len, _ref1, _results; 125 | _ref1 = headers['heart-beat'].split(","); 126 | _results = []; 127 | for (_i = 0, _len = _ref1.length; _i < _len; _i++) { 128 | v = _ref1[_i]; 129 | _results.push(parseInt(v)); 130 | } 131 | return _results; 132 | })(), serverOutgoing = _ref1[0], serverIncoming = _ref1[1]; 133 | if (!(this.heartbeat.outgoing === 0 || serverIncoming === 0)) { 134 | ttl = Math.max(this.heartbeat.outgoing, serverIncoming); 135 | if (typeof this.debug === "function") { 136 | this.debug("send PING every " + ttl + "ms"); 137 | } 138 | this.pinger = typeof window !== "undefined" && window !== null ? window.setInterval(function() { 139 | _this.ws.send(Byte.LF); 140 | return typeof _this.debug === "function" ? _this.debug(">>> PING") : void 0; 141 | }, ttl) : void 0; 142 | } 143 | if (!(this.heartbeat.incoming === 0 || serverOutgoing === 0)) { 144 | ttl = Math.max(this.heartbeat.incoming, serverOutgoing); 145 | if (typeof this.debug === "function") { 146 | this.debug("check PONG every " + ttl + "ms"); 147 | } 148 | return this.ponger = typeof window !== "undefined" && window !== null ? window.setInterval(function() { 149 | var delta; 150 | delta = Date.now() - _this.serverActivity; 151 | if (delta > ttl * 2) { 152 | if (typeof _this.debug === "function") { 153 | _this.debug("did not receive server activity for the last " + delta + "ms"); 154 | } 155 | return _this.ws.close(); 156 | } 157 | }, ttl) : void 0; 158 | } 159 | }; 160 | 161 | Client.prototype.connect = function(login, passcode, connectCallback, errorCallback, vhost) { 162 | var _this = this; 163 | this.connectCallback = connectCallback; 164 | if (typeof this.debug === "function") { 165 | this.debug("Opening Web Socket..."); 166 | } 167 | this.ws.onmessage = function(evt) { 168 | var arr, c, data, frame, onreceive, _i, _len, _ref, _results; 169 | data = typeof ArrayBuffer !== 'undefined' && evt.data instanceof ArrayBuffer ? (arr = new Uint8Array(evt.data), typeof _this.debug === "function" ? _this.debug("--- got data length: " + arr.length) : void 0, ((function() { 170 | var _i, _len, _results; 171 | _results = []; 172 | for (_i = 0, _len = arr.length; _i < _len; _i++) { 173 | c = arr[_i]; 174 | _results.push(String.fromCharCode(c)); 175 | } 176 | return _results; 177 | })()).join('')) : evt.data; 178 | _this.serverActivity = Date.now(); 179 | if (data === Byte.LF) { 180 | if (typeof _this.debug === "function") { 181 | _this.debug("<<< PONG"); 182 | } 183 | return; 184 | } 185 | if (typeof _this.debug === "function") { 186 | _this.debug("<<< " + data); 187 | } 188 | _ref = Frame.unmarshall(data); 189 | _results = []; 190 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 191 | frame = _ref[_i]; 192 | switch (frame.command) { 193 | case "CONNECTED": 194 | if (typeof _this.debug === "function") { 195 | _this.debug("connected to server " + frame.headers.server); 196 | } 197 | _this.connected = true; 198 | _this._setupHeartbeat(frame.headers); 199 | _results.push(typeof _this.connectCallback === "function" ? _this.connectCallback(frame) : void 0); 200 | break; 201 | case "MESSAGE": 202 | onreceive = _this.subscriptions[frame.headers.subscription]; 203 | _results.push(typeof onreceive === "function" ? onreceive(frame) : void 0); 204 | break; 205 | case "RECEIPT": 206 | _results.push(typeof _this.onreceipt === "function" ? _this.onreceipt(frame) : void 0); 207 | break; 208 | case "ERROR": 209 | _results.push(typeof errorCallback === "function" ? errorCallback(frame) : void 0); 210 | break; 211 | default: 212 | _results.push(typeof _this.debug === "function" ? _this.debug("Unhandled frame: " + frame) : void 0); 213 | } 214 | } 215 | return _results; 216 | }; 217 | this.ws.onclose = function() { 218 | var msg; 219 | msg = "Whoops! Lost connection to " + _this.ws.url; 220 | if (typeof _this.debug === "function") { 221 | _this.debug(msg); 222 | } 223 | _this._cleanUp(); 224 | return typeof errorCallback === "function" ? errorCallback(msg) : void 0; 225 | }; 226 | return this.ws.onopen = function() { 227 | var headers; 228 | if (typeof _this.debug === "function") { 229 | _this.debug('Web Socket Opened...'); 230 | } 231 | headers = { 232 | "accept-version": Stomp.VERSIONS.supportedVersions(), 233 | "heart-beat": [_this.heartbeat.outgoing, _this.heartbeat.incoming].join(',') 234 | }; 235 | if (vhost) { 236 | headers.host = vhost; 237 | } 238 | if (login) { 239 | headers.login = login; 240 | } 241 | if (passcode) { 242 | headers.passcode = passcode; 243 | } 244 | return _this._transmit("CONNECT", headers); 245 | }; 246 | }; 247 | 248 | Client.prototype.disconnect = function(disconnectCallback) { 249 | this._transmit("DISCONNECT"); 250 | this.ws.onclose = null; 251 | this.ws.close(); 252 | this._cleanUp(); 253 | return typeof disconnectCallback === "function" ? disconnectCallback() : void 0; 254 | }; 255 | 256 | Client.prototype._cleanUp = function() { 257 | this.connected = false; 258 | if (this.pinger) { 259 | if (typeof window !== "undefined" && window !== null) { 260 | window.clearInterval(this.pinger); 261 | } 262 | } 263 | if (this.ponger) { 264 | return typeof window !== "undefined" && window !== null ? window.clearInterval(this.ponger) : void 0; 265 | } 266 | }; 267 | 268 | Client.prototype.send = function(destination, headers, body) { 269 | if (headers == null) { 270 | headers = {}; 271 | } 272 | if (body == null) { 273 | body = ''; 274 | } 275 | headers.destination = destination; 276 | return this._transmit("SEND", headers, body); 277 | }; 278 | 279 | Client.prototype.subscribe = function(destination, callback, headers) { 280 | if (headers == null) { 281 | headers = {}; 282 | } 283 | if (!headers.id) { 284 | headers.id = "sub-" + this.counter++; 285 | } 286 | headers.destination = destination; 287 | this.subscriptions[headers.id] = callback; 288 | this._transmit("SUBSCRIBE", headers); 289 | return headers.id; 290 | }; 291 | 292 | Client.prototype.unsubscribe = function(id) { 293 | delete this.subscriptions[id]; 294 | return this._transmit("UNSUBSCRIBE", { 295 | id: id 296 | }); 297 | }; 298 | 299 | Client.prototype.begin = function(transaction) { 300 | return this._transmit("BEGIN", { 301 | transaction: transaction 302 | }); 303 | }; 304 | 305 | Client.prototype.commit = function(transaction) { 306 | return this._transmit("COMMIT", { 307 | transaction: transaction 308 | }); 309 | }; 310 | 311 | Client.prototype.abort = function(transaction) { 312 | return this._transmit("ABORT", { 313 | transaction: transaction 314 | }); 315 | }; 316 | 317 | Client.prototype.ack = function(messageID, subscription, headers) { 318 | if (headers == null) { 319 | headers = {}; 320 | } 321 | headers["message-id"] = messageID; 322 | headers.subscription = subscription; 323 | return this._transmit("ACK", headers); 324 | }; 325 | 326 | Client.prototype.nack = function(messageID, subscription, headers) { 327 | if (headers == null) { 328 | headers = {}; 329 | } 330 | headers["message-id"] = messageID; 331 | headers.subscription = subscription; 332 | return this._transmit("NACK", headers); 333 | }; 334 | 335 | return Client; 336 | 337 | })(); 338 | 339 | Stomp = { 340 | libVersion: "2.0.0-next", 341 | VERSIONS: { 342 | V1_0: '1.0', 343 | V1_1: '1.1', 344 | V1_2: '1.2', 345 | supportedVersions: function() { 346 | return '1.1,1.0'; 347 | } 348 | }, 349 | client: function(url, protocols) { 350 | var klass, ws; 351 | if (protocols == null) { 352 | protocols = ['v10.stomp', 'v11.stomp']; 353 | } 354 | klass = Stomp.WebSocketClass || WebSocket; 355 | ws = new klass(url, protocols); 356 | return new Client(ws); 357 | }, 358 | over: function(ws) { 359 | return new Client(ws); 360 | }, 361 | Frame: Frame 362 | }; 363 | 364 | if (typeof window !== "undefined" && window !== null) { 365 | window.Stomp = Stomp; 366 | } else if (typeof exports !== "undefined" && exports !== null) { 367 | exports.Stomp = Stomp; 368 | Stomp.WebSocketClass = require('./test/server.mock.js').StompServerMock; 369 | } else { 370 | self.Stomp = Stomp; 371 | } 372 | 373 | }).call(this); 374 | -------------------------------------------------------------------------------- /tests/vendor/vertxbus.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2012 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | var vertx = vertx || {}; 18 | 19 | !function(factory) { 20 | if (typeof define === "function" && define.amd) { 21 | // Expose as an AMD module with SockJS dependency. 22 | // "vertxbus" and "sockjs" names are used because 23 | // AMD module names are derived from file names. 24 | define("vertxbus", ["sockjs"], factory); 25 | } else { 26 | // No AMD-compliant loader 27 | factory(SockJS); 28 | } 29 | }(function(SockJS) { 30 | 31 | vertx.EventBus = function(url, options) { 32 | 33 | var that = this; 34 | var sockJSConn = new SockJS(url, undefined, options); 35 | var handlerMap = {}; 36 | var replyHandlers = {}; 37 | var state = vertx.EventBus.CONNECTING; 38 | 39 | that.onopen = null; 40 | that.onclose = null; 41 | 42 | that.send = function(address, message, replyHandler) { 43 | sendOrPub("send", address, message, replyHandler) 44 | } 45 | 46 | that.publish = function(address, message, replyHandler) { 47 | sendOrPub("publish", address, message, replyHandler) 48 | } 49 | 50 | that.registerHandler = function(address, handler) { 51 | checkSpecified("address", 'string', address); 52 | checkSpecified("handler", 'function', handler); 53 | checkOpen(); 54 | var handlers = handlerMap[address]; 55 | if (!handlers) { 56 | handlers = [handler]; 57 | handlerMap[address] = handlers; 58 | // First handler for this address so we should register the connection 59 | var msg = { type : "register", 60 | address: address }; 61 | sockJSConn.send(JSON.stringify(msg)); 62 | } else { 63 | handlers[handlers.length] = handler; 64 | } 65 | } 66 | 67 | that.unregisterHandler = function(address, handler) { 68 | checkSpecified("address", 'string', address); 69 | checkSpecified("handler", 'function', handler); 70 | checkOpen(); 71 | var handlers = handlerMap[address]; 72 | if (handlers) { 73 | var idx = handlers.indexOf(handler); 74 | if (idx != -1) handlers.splice(idx, 1); 75 | if (handlers.length == 0) { 76 | // No more local handlers so we should unregister the connection 77 | 78 | var msg = { type : "unregister", 79 | address: address}; 80 | sockJSConn.send(JSON.stringify(msg)); 81 | delete handlerMap[address]; 82 | } 83 | } 84 | } 85 | 86 | that.close = function() { 87 | checkOpen(); 88 | state = vertx.EventBus.CLOSING; 89 | sockJSConn.close(); 90 | } 91 | 92 | that.readyState = function() { 93 | return state; 94 | } 95 | 96 | sockJSConn.onopen = function() { 97 | state = vertx.EventBus.OPEN; 98 | if (that.onopen) { 99 | that.onopen(); 100 | } 101 | }; 102 | 103 | sockJSConn.onclose = function() { 104 | state = vertx.EventBus.CLOSED; 105 | if (that.onclose) { 106 | that.onclose(); 107 | } 108 | }; 109 | 110 | sockJSConn.onmessage = function(e) { 111 | var msg = e.data; 112 | var json = JSON.parse(msg); 113 | var body = json.body; 114 | var replyAddress = json.replyAddress; 115 | var address = json.address; 116 | var replyHandler; 117 | if (replyAddress) { 118 | replyHandler = function(reply, replyHandler) { 119 | // Send back reply 120 | that.send(replyAddress, reply, replyHandler); 121 | }; 122 | } 123 | var handlers = handlerMap[address]; 124 | if (handlers) { 125 | // We make a copy since the handler might get unregistered from within the 126 | // handler itself, which would screw up our iteration 127 | var copy = handlers.slice(0); 128 | for (var i = 0; i < copy.length; i++) { 129 | copy[i](body, replyHandler); 130 | } 131 | } else { 132 | // Might be a reply message 133 | var handler = replyHandlers[address]; 134 | if (handler) { 135 | delete replyHandlers[replyAddress]; 136 | handler(body, replyHandler); 137 | } 138 | } 139 | } 140 | 141 | function sendOrPub(sendOrPub, address, message, replyHandler) { 142 | checkSpecified("address", 'string', address); 143 | checkSpecified("message", 'object', message); 144 | checkSpecified("replyHandler", 'function', replyHandler, true); 145 | checkOpen(); 146 | var envelope = { type : sendOrPub, 147 | address: address, 148 | body: message }; 149 | if (replyHandler) { 150 | var replyAddress = makeUUID(); 151 | envelope.replyAddress = replyAddress; 152 | replyHandlers[replyAddress] = replyHandler; 153 | } 154 | var str = JSON.stringify(envelope); 155 | sockJSConn.send(str); 156 | } 157 | 158 | function checkOpen() { 159 | if (state != vertx.EventBus.OPEN) { 160 | throw new Error('INVALID_STATE_ERR'); 161 | } 162 | } 163 | 164 | function checkSpecified(paramName, paramType, param, optional) { 165 | if (!optional && !param) { 166 | throw new Error("Parameter " + paramName + " must be specified"); 167 | } 168 | if (param && typeof param != paramType) { 169 | throw new Error("Parameter " + paramName + " must be of type " + paramType); 170 | } 171 | } 172 | 173 | function isFunction(obj) { 174 | return !!(obj && obj.constructor && obj.call && obj.apply); 175 | } 176 | 177 | function makeUUID(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx" 178 | .replace(/[xy]/g,function(a,b){return b=Math.random()*16,(a=="y"?b&3|8:b|0).toString(16)})} 179 | 180 | } 181 | 182 | vertx.EventBus.CONNECTING = 0; 183 | vertx.EventBus.OPEN = 1; 184 | vertx.EventBus.CLOSING = 2; 185 | vertx.EventBus.CLOSED = 3; 186 | 187 | return vertx.EventBus; 188 | 189 | }); 190 | --------------------------------------------------------------------------------