├── LICENSE-2.0.txt ├── apps ├── bingmaps │ ├── images │ │ ├── grabbutton.png │ │ ├── icon-clearMap.png │ │ ├── icon-dropPin.png │ │ ├── icon-traffic.png │ │ ├── map-type-bird-eye.png │ │ ├── map-type-road.png │ │ ├── map-type-satellite.png │ │ ├── menu-icon-bookmark.png │ │ ├── menu-icon-info.png │ │ ├── menu-icon-mylocation.png │ │ ├── mylocation.png │ │ ├── poi_search.png │ │ ├── search-input-search.png │ │ ├── sliding-shadow.png │ │ ├── topbar-direct-icon.png │ │ └── topbar-search-icon.png │ ├── index.html │ ├── maps │ │ ├── BingMap.css │ │ ├── BingMap.js │ │ ├── images │ │ │ └── poi_precise_location.png │ │ └── package.js │ ├── package.js │ └── source │ │ ├── BookmarkList.js │ │ ├── CurrentLocation.js │ │ ├── Infobox.js │ │ ├── LabeledItem.js │ │ ├── MapsApp.css │ │ ├── MapsApp.js │ │ ├── Pullout.js │ │ ├── SearchPlaces.js │ │ └── mockdata.js ├── cryptotweets │ ├── README.md │ ├── appinfo.json │ ├── cellTest.html │ ├── fonts │ │ └── lovely_eunike_hans.ttf │ ├── icon_32x32.png │ ├── icon_48x48.png │ ├── icon_64x64.png │ ├── index.html │ ├── source │ │ ├── App.js │ │ ├── Cell.js │ │ ├── Cryptogram.js │ │ ├── Cypher.js │ │ ├── Distribution.js │ │ ├── PickLetter.js │ │ ├── app.css │ │ ├── package.js │ │ └── utils.js │ └── tests │ │ ├── SpecRunner.html │ │ ├── cypher.js │ │ ├── findHashTags.js │ │ ├── jasmine │ │ ├── MIT.LICENSE │ │ ├── jasmine-html.js │ │ ├── jasmine.css │ │ ├── jasmine.js │ │ └── jasmine_favicon.png │ │ └── wrapStringAtSpace.js └── piratepig │ ├── images │ ├── background_tile.png │ ├── center_bottom.png │ ├── game_bear.png │ ├── game_bunny_02.png │ ├── game_carrot.png │ ├── game_lemon.png │ ├── game_panda.png │ ├── game_piratePig.png │ └── logo.png │ ├── index.html │ ├── sounds │ ├── 3.mp3 │ ├── 3.ogg │ ├── 3.wav │ ├── 4.mp3 │ ├── 4.ogg │ ├── 4.wav │ ├── 5.mp3 │ ├── 5.ogg │ ├── 5.wav │ ├── theme.mp3 │ ├── theme.ogg │ ├── theme.wav │ ├── whiff.mp3 │ ├── whiff.ogg │ └── whiff.wav │ └── source │ ├── App.js │ ├── Audio.js │ ├── Hat.js │ ├── Selector.js │ ├── app.css │ └── package.js └── examples ├── 0 - Hello World └── index.html ├── 1 - Making a Kind ├── HelloWorld.js └── index.html ├── 2 - Making a Package ├── index.html └── source │ ├── HelloWorld.css │ ├── HelloWorld.js │ └── package.js ├── 3 - Making Builds ├── index.html └── source │ ├── HelloWorld.css │ ├── HelloWorld.js │ ├── minify-include-enyo │ ├── minify.bat │ └── package.js │ ├── minify │ ├── minify.bat │ └── package.js │ └── package.js └── ui ├── index.html └── search.png /LICENSE-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /apps/bingmaps/images/grabbutton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/bingmaps/images/grabbutton.png -------------------------------------------------------------------------------- /apps/bingmaps/images/icon-clearMap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/bingmaps/images/icon-clearMap.png -------------------------------------------------------------------------------- /apps/bingmaps/images/icon-dropPin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/bingmaps/images/icon-dropPin.png -------------------------------------------------------------------------------- /apps/bingmaps/images/icon-traffic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/bingmaps/images/icon-traffic.png -------------------------------------------------------------------------------- /apps/bingmaps/images/map-type-bird-eye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/bingmaps/images/map-type-bird-eye.png -------------------------------------------------------------------------------- /apps/bingmaps/images/map-type-road.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/bingmaps/images/map-type-road.png -------------------------------------------------------------------------------- /apps/bingmaps/images/map-type-satellite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/bingmaps/images/map-type-satellite.png -------------------------------------------------------------------------------- /apps/bingmaps/images/menu-icon-bookmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/bingmaps/images/menu-icon-bookmark.png -------------------------------------------------------------------------------- /apps/bingmaps/images/menu-icon-info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/bingmaps/images/menu-icon-info.png -------------------------------------------------------------------------------- /apps/bingmaps/images/menu-icon-mylocation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/bingmaps/images/menu-icon-mylocation.png -------------------------------------------------------------------------------- /apps/bingmaps/images/mylocation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/bingmaps/images/mylocation.png -------------------------------------------------------------------------------- /apps/bingmaps/images/poi_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/bingmaps/images/poi_search.png -------------------------------------------------------------------------------- /apps/bingmaps/images/search-input-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/bingmaps/images/search-input-search.png -------------------------------------------------------------------------------- /apps/bingmaps/images/sliding-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/bingmaps/images/sliding-shadow.png -------------------------------------------------------------------------------- /apps/bingmaps/images/topbar-direct-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/bingmaps/images/topbar-direct-icon.png -------------------------------------------------------------------------------- /apps/bingmaps/images/topbar-search-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/bingmaps/images/topbar-search-icon.png -------------------------------------------------------------------------------- /apps/bingmaps/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Onyx Map (Bing) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /apps/bingmaps/maps/BingMap.css: -------------------------------------------------------------------------------- 1 | .enyo-bingmap { 2 | overflow: hidden; 3 | position: relative; 4 | } -------------------------------------------------------------------------------- /apps/bingmaps/maps/BingMap.js: -------------------------------------------------------------------------------- 1 | /** 2 | A map control which is a wrapper around Bing map control. 3 | 4 | To initialize a Map control: 5 | 6 | {name: "map", kind: "BingMap", credentials: "my_bing_app_id"} 7 | 8 | You can get a handle to the actual Bing map control uisng hasMap(), like this: 9 | 10 | var bingMap = this.$.map.hasMap(); 11 | 12 | */ 13 | enyo.kind({ 14 | name: "enyo.BingMap", 15 | classes: "enyo-bingmap", 16 | published: { 17 | //* The latitude of the location. 18 | latitude: 37.029043436050415, 19 | //* The longitde of the location. 20 | longitude: -101.55550763010979, 21 | //* The zoom level of the map view. 22 | zoom: 4, 23 | //* Show a pin at the center of the current map view. 24 | showPin: false, 25 | //* The map type of the map view. Valid map types are aerial, auto, birdseye, collinsBart, mercator, ordnanceSurvey and road. 26 | mapType: "road", 27 | //* The Bing Maps Key used to authenticate the application. 28 | credentials: "", 29 | //* Show traffic info 30 | showTraffic: false, 31 | /** 32 | Represents Bing options to customize the map that is displayed. Can only be set at create time. For example, 33 | {kind: "enyo.Map", options: {showDashboard: false, showCopyright: false}} 34 | */ 35 | options: "" 36 | }, 37 | events: { 38 | onPinClick: "", 39 | onLoaded: "", 40 | onLoadFailure: "" 41 | }, 42 | //* @protected 43 | rendered: function() { 44 | this.inherited(arguments); 45 | enyo.BingMap.loadScript(enyo.bind(this, "renderMap")); 46 | }, 47 | destroy: function() { 48 | this.inherited(arguments); 49 | if (this.pinClickEvent) { 50 | Microsoft.Maps.Events.removeHandler(this.pinClickEvent); 51 | } 52 | }, 53 | createMap: function() { 54 | var props = { 55 | credentials: this.credentials, 56 | disableKeyboardInput: true // make keyboard not to popout 57 | }; 58 | enyo.mixin(props, this.options); 59 | this.map = new Microsoft.Maps.Map(this.hasNode(), props); 60 | }, 61 | destroyMap: function() { 62 | this.map = null; 63 | }, 64 | renderMap: function() { 65 | this.destroyMap(); 66 | try { 67 | this.createMap(); 68 | } catch (e) { 69 | this.doLoadFailure({e: e}); 70 | return; 71 | } 72 | this.mapTypeChanged(); 73 | this.updateCenter(); 74 | this.zoomChanged(); 75 | this.showPinChanged(); 76 | this.doLoaded(); 77 | }, 78 | //* @public 79 | /** 80 | Returns the actual Bing map control. 81 | */ 82 | hasMap: function() { 83 | return this.map; 84 | }, 85 | /** 86 | Removes all entities from the map except the dropped pin and the pins in inExcludes. 87 | */ 88 | clearAll: function(inExcludes) { 89 | this.map.entities.clear(); 90 | if (this.showPin && this.pin) { 91 | this.map.entities.push(this.pin); 92 | } 93 | if (inExcludes) { 94 | for (var i=0, ex; (ex=inExcludes[i]); i++) { 95 | if (ex) { 96 | this.map.entities.push(ex); 97 | } 98 | } 99 | } 100 | }, 101 | /** 102 | Sets the location of the center of the map view. 103 | @param {number} inLatitude The latitude of the location. 104 | @param {number} inLongitude The longitude of the location. 105 | */ 106 | setCenter: function(inLatitude, inLongitude) { 107 | this.latitude = inLatitude; 108 | this.longitude = inLongitude; 109 | this.updateCenter(); 110 | }, 111 | //* @protected 112 | credentialsChanged: function() { 113 | this.renderMap(); 114 | }, 115 | latitudeChanged: function() { 116 | this.latitude = Number(this.latitude); 117 | this.updateCenter(); 118 | }, 119 | longitudeChanged: function() { 120 | this.longitude = Number(this.longitude); 121 | this.updateCenter(); 122 | }, 123 | updateCenter: function() { 124 | this.centerLoc = new Microsoft.Maps.Location(); 125 | this.centerLoc.latitude = this.latitude; 126 | this.centerLoc.longitude = this.longitude; 127 | this.map.setView({center: this.centerLoc}); 128 | }, 129 | zoomChanged: function() { 130 | this.zoom = Number(this.zoom); 131 | this.map.setView({zoom: this.zoom}); 132 | }, 133 | getZoom: function() { 134 | return this.map.getZoom(); 135 | }, 136 | showPinChanged: function() { 137 | if (this.showPin) { 138 | if (this.pin) { 139 | this.pin.setLocation(this.centerLoc); 140 | } else { 141 | this.pin = new Microsoft.Maps.Pushpin(this.centerLoc, {draggable: true}); 142 | this.pinClickEvent = Microsoft.Maps.Events.addHandler(this.pin, 'click', enyo.bind(this, "pinClick")); 143 | this.pin.setOptions({icon: enyo.path.rewrite("$maps/") + "images/poi_precise_location.png"}); 144 | } 145 | this.map.entities.push(this.pin); 146 | } else { 147 | if (this.pin) { 148 | this.map.entities.remove(this.pin); 149 | } 150 | } 151 | }, 152 | pinClick: function(e) { 153 | this.doPinClick(e); 154 | }, 155 | mapTypeChanged: function() { 156 | var id = Microsoft.Maps.MapTypeId[this.mapType] || Microsoft.Maps.MapTypeId.road; 157 | this.map.setView({mapTypeId: id}); 158 | }, 159 | showTrafficChanged: function() { 160 | if (this.trafficTileLayer) { 161 | this.map.entities.remove(this.trafficTileLayer); 162 | this.trafficTileLayer = null; 163 | } 164 | if (this.showTraffic) { 165 | var time = (new Date()).getTime(); 166 | var tileSource = new Microsoft.Maps.TileSource({uriConstructor: 'http://t0.tiles.virtualearth.net/tiles/t{quadkey}?tc=' + time}); 167 | // Construct the layer using the tile source 168 | this.trafficTileLayer = new Microsoft.Maps.TileLayer({mercator: tileSource, opacity: 0.7}); 169 | this.map.entities.push(this.trafficTileLayer); 170 | } 171 | }, 172 | //* @public 173 | createPushpin: function(inLatitude, inLongitude, inOptions, inProps) { 174 | return this.updatePushpin(null, inLatitude, inLongitude, inOptions, inProps); 175 | }, 176 | updatePushpin: function(inPushpin, inLatitude, inLongitude, inOptions, inProps) { 177 | var pushpin = inPushpin, location = new Microsoft.Maps.Location(inLatitude, inLongitude); 178 | if (!pushpin) { 179 | pushpin = new Microsoft.Maps.Pushpin(location, inOptions); 180 | this.map.entities.push(pushpin); 181 | } else { 182 | pushpin.setOptions(inOptions); 183 | pushpin.setLocation(location); 184 | } 185 | enyo.mixin(pushpin, inProps); 186 | pushpin.location = {latitude: inLatitude, longitude: inLongitude}; 187 | return pushpin; 188 | }, 189 | //* @protected 190 | statics: { 191 | scriptLoadedCbs: [], 192 | alreadyCalled: false, 193 | loadScript: function(inCallback) { 194 | if (window["Microsoft"] && window["Microsoft"]["Maps"]) { 195 | if (inCallback) inCallback(); 196 | } else { 197 | this.scriptLoadedCbs.push(inCallback); 198 | if (!this.alreadyCalled) { 199 | this.alreadyCalled = true; 200 | this.addScript(); 201 | } 202 | } 203 | }, 204 | addScript: function() { 205 | var script = document.createElement("script"); 206 | script.type = "text/javascript"; 207 | script.src = "http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0&onscriptload=enyo_BingMap_scriptLoaded"; 208 | document.body.appendChild(script); 209 | }, 210 | scriptLoaded: function() { 211 | for (var i=0, c; (c=enyo.BingMap.scriptLoadedCbs[i]); i++) { 212 | c(); 213 | } 214 | enyo.BingMap.scriptLoadedCbs = []; 215 | } 216 | } 217 | }); 218 | 219 | // onscriptload in Bing API doesn't work if "." is in the callback function name 220 | enyo_BingMap_scriptLoaded = enyo.BingMap.scriptLoaded; 221 | -------------------------------------------------------------------------------- /apps/bingmaps/maps/images/poi_precise_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/bingmaps/maps/images/poi_precise_location.png -------------------------------------------------------------------------------- /apps/bingmaps/maps/package.js: -------------------------------------------------------------------------------- 1 | enyo.depends( 2 | "BingMap.css", 3 | "BingMap.js" 4 | ); -------------------------------------------------------------------------------- /apps/bingmaps/package.js: -------------------------------------------------------------------------------- 1 | enyo.depends( 2 | "$lib/layout/fittable", 3 | "$lib/layout/slideable", 4 | "$lib/onyx", 5 | "maps", 6 | "source/mockdata.js", 7 | "source/CurrentLocation.js", 8 | "source/SearchPlaces.js", 9 | "source/LabeledItem.js", 10 | "source/BookmarkList.js", 11 | "source/Pullout.js", 12 | "source/Infobox.js", 13 | "source/MapsApp.css", 14 | "source/MapsApp.js" 15 | ); -------------------------------------------------------------------------------- /apps/bingmaps/source/BookmarkList.js: -------------------------------------------------------------------------------- 1 | enyo.kind({ 2 | name: "BookmarkList", 3 | events: { 4 | onItemSelect: "" 5 | }, 6 | create: function() { 7 | this.inherited(arguments); 8 | for (var i=0, b; (b=mock_bookmarks[i]); i++) { 9 | var c = this.createComponent({kind: "BookmarkItem", 10 | ontap: "itemTap" 11 | }, b); 12 | c.setTitle(b.Title); 13 | c.setDetails(b.Address); 14 | } 15 | }, 16 | itemTap: function(inSender) { 17 | if (this.activated) { 18 | this.activated.applyStyle("background", null); 19 | } 20 | this.activated = inSender; 21 | this.activated.applyStyle("background", "lightblue"); 22 | this.doItemSelect({item: inSender}); 23 | } 24 | }); 25 | 26 | enyo.kind({ 27 | name: "BookmarkItem", 28 | classes: "bookmark-item", 29 | published: { 30 | title: "", 31 | details: "" 32 | }, 33 | components: [ 34 | {name: "title", classes: "bookmark-item-title"}, 35 | {name: "details", classes: "bookmark-item-details"} 36 | ], 37 | create: function() { 38 | this.inherited(arguments); 39 | this.titleChanged(); 40 | this.detailsChanged(); 41 | }, 42 | titleChanged: function() { 43 | this.$.title.setContent(this.title); 44 | }, 45 | detailsChanged: function() { 46 | this.$.details.setContent(this.details); 47 | } 48 | }); 49 | -------------------------------------------------------------------------------- /apps/bingmaps/source/CurrentLocation.js: -------------------------------------------------------------------------------- 1 | enyo.kind({ 2 | name: "CurrentLocation", 3 | kind: "Component", 4 | events: { 5 | onSuccess: "", 6 | onFailure: "doFailure" 7 | }, 8 | doFailure: function(error) { 9 | // error 2 is "POSITION_UNAVAILABLE"; may happen on desktop if wifi is off or n/a 10 | console.log("Geolocation error #" + error.code + ": " + error.message); 11 | }, 12 | destroy: function() { 13 | this.stopTracking(); 14 | this.inherited(arguments); 15 | }, 16 | stopTracking: function() { 17 | if (this._watchId) { 18 | navigator.geolocation.clearWatch(this._watchId); 19 | } 20 | }, 21 | go: function() { 22 | if(!!navigator.geolocation) { 23 | this._watchId = navigator.geolocation.watchPosition( 24 | enyo.bind(this, "doSuccess"), 25 | enyo.bind(this, "doFailure"), 26 | {maximumAge: 600, timeout: 10000}); 27 | } else { 28 | console.log("Geolocation error: Browser does not support navigator.geolocation!"); 29 | } 30 | } 31 | }); 32 | -------------------------------------------------------------------------------- /apps/bingmaps/source/Infobox.js: -------------------------------------------------------------------------------- 1 | enyo.kind({ 2 | name: "Infobox", 3 | kind: "onyx.Popup", 4 | classes: "infobox", 5 | published: { 6 | title: "", 7 | details: "" 8 | }, 9 | floating: true, 10 | components: [ 11 | {name: "title"}, 12 | {name: "details", classes: "infobox-details"} 13 | ], 14 | create: function() { 15 | this.inherited(arguments); 16 | this.titleChanged(); 17 | this.detailsChanged(); 18 | }, 19 | titleChanged: function() { 20 | this.$.title.setContent(this.title); 21 | }, 22 | detailsChanged: function() { 23 | this.$.details.setContent(this.details); 24 | }, 25 | openAt: function(inTop, inLeft) { 26 | this.applyStyle("top", inTop + "px"); 27 | this.applyStyle("left", inLeft + "px"); 28 | this.show(); 29 | }, 30 | openWithItem: function(inItem, inTop, inLeft) { 31 | this.setTitle(inItem.Title); 32 | this.setDetails(inItem.Address); 33 | this.openAt(inTop, inLeft); 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /apps/bingmaps/source/LabeledItem.js: -------------------------------------------------------------------------------- 1 | /* an item that contains icon, label and any input control, e.g. onyx.Checkbox, onyx.ToggleButton */ 2 | enyo.kind({ 3 | name: "LabeledItem", 4 | published: { 5 | value: "", 6 | label: "", 7 | icon: "" 8 | }, 9 | components: [ 10 | {name: "icon", kind: "Image", classes: "labeled-item-icon"}, 11 | {name: "label", kind: "Control"}, 12 | {name: "input", classes: "label-item-input"} 13 | ], 14 | defaultKind: "onyx.Checkbox", 15 | create: function() { 16 | this.inherited(arguments); 17 | this.valueChanged(); 18 | this.labelChanged(); 19 | this.iconChanged(); 20 | }, 21 | labelChanged: function() { 22 | this.$.label.setContent(this.label); 23 | }, 24 | iconChanged: function() { 25 | this.$.icon.setSrc(this.icon); 26 | }, 27 | getValue: function() { 28 | return this.$.input.getValue(); 29 | }, 30 | valueChanged: function() { 31 | this.$.input.setValue(this.value); 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /apps/bingmaps/source/MapsApp.css: -------------------------------------------------------------------------------- 1 | .app { 2 | overflow: hidden; 3 | } 4 | 5 | .menu { 6 | display: block; 7 | margin: 0; 8 | float: right; 9 | } 10 | 11 | .pullout-menu { 12 | display: none; 13 | } 14 | 15 | .search-input { 16 | width: 180px; 17 | } 18 | 19 | /* adapt to smaller displays */ 20 | @media (max-width: 480px) { 21 | .menu { 22 | display: none; 23 | } 24 | 25 | .pullout-menu { 26 | display: block; 27 | margin: 0; 28 | } 29 | 30 | .search-input { 31 | width: 140px; 32 | } 33 | } 34 | 35 | .pullout { 36 | position: absolute; 37 | top: 57px; 38 | right: 0; 39 | bottom: 0; 40 | width: 320px; 41 | background: #D8D8D8; 42 | } 43 | 44 | .pullout-shadow { 45 | position: absolute; 46 | height: 100%; 47 | width: 20px; 48 | left: -20px; 49 | background: url(../images/sliding-shadow.png) repeat-y; 50 | background-size: 20px 2px; 51 | } 52 | 53 | .pullout-grabbutton { 54 | position: absolute; 55 | height: 54px; 56 | width: 50px; 57 | bottom: 20px; 58 | left: -46px; 59 | border-radius: 10px 0 0 10px; 60 | background-color: #D8D8D8; 61 | box-shadow: -4px 4px 4px rgba(0,0,0,0.3); 62 | } 63 | 64 | .pullout-toolbar { 65 | padding-top: 4px; 66 | text-align: center; 67 | } 68 | 69 | .settings { 70 | padding: 10px; 71 | } 72 | 73 | .settings > * { 74 | padding: 10px; 75 | min-height: 30px; 76 | line-height: 30px; 77 | } 78 | 79 | .settings > * > * { 80 | display: inline-block; 81 | } 82 | 83 | .labeled-item-icon { 84 | vertical-align: bottom; 85 | padding-right: 5px; 86 | } 87 | 88 | .label-item-input { 89 | float: right; 90 | } 91 | 92 | .bookmark-header { 93 | text-align: center; 94 | padding: 10px 0; 95 | } 96 | 97 | .bookmark-scroller { 98 | background-color: #fff; 99 | } 100 | 101 | .bookmark-item { 102 | padding: 10px; 103 | } 104 | 105 | .bookmark-item-title, .bookmark-item-details { 106 | white-space: nowrap; 107 | overflow: hidden; 108 | text-overflow: ellipsis; 109 | } 110 | 111 | .bookmark-item-title { 112 | margin-top: 10px; 113 | font-size: 16px; 114 | } 115 | 116 | .bookmark-item-details { 117 | font-size: 14px; 118 | color: #606060; 119 | } 120 | 121 | .infobox { 122 | position: absolute; 123 | padding: 10px; 124 | border-radius: 10px; 125 | background-color: #4C4C4C; 126 | color: #fff; 127 | font-size: 18px; 128 | } 129 | 130 | .infobox-details { 131 | font-size: 14px; 132 | } -------------------------------------------------------------------------------- /apps/bingmaps/source/MapsApp.js: -------------------------------------------------------------------------------- 1 | enyo.kind({ 2 | name: "MapsApp", 3 | classes: "app", 4 | components: [ 5 | {kind: "FittableRows", classes: "enyo-fit", components: [ 6 | {kind: "onyx.Toolbar", components: [ 7 | {name: "menu", classes: "menu", defaultKind: "onyx.IconButton", components: [ 8 | {src: "images/menu-icon-info.png", panel: "info", ontap: "togglePullout"}, 9 | {src: "images/menu-icon-bookmark.png", panel: "bookmark", ontap: "togglePullout"}, 10 | {src: "images/menu-icon-mylocation.png", ontap: "findCurrentLocation"} 11 | ]}, 12 | {kind: "Group", defaultKind: "onyx.IconButton", tag: null, components: [ 13 | {src: "images/topbar-search-icon.png", active: true}, 14 | {src: "images/topbar-direct-icon.png"} 15 | ]}, 16 | {kind: "onyx.InputDecorator", components: [ 17 | {name: "searchInput", classes: "search-input", kind: "onyx.Input", placeholder: "Search (e.g. Coffee)", onkeypress: "searchInputKeypress"}, 18 | {kind: "Image", src: "images/search-input-search.png", ontap: "search"} 19 | ]} 20 | ]}, 21 | {name: "map", kind: "BingMap", fit: true, onLoaded: "findCurrentLocation", 22 | options: {showDashboard: false, showScalebar: false}, 23 | credentials: "Ah2oavKf-raTJYVgMxnxJg9E2E2_53Wb3jz2lD4N415EFSKwFlhlMe9U2dJpMZyJ" 24 | } 25 | ]}, 26 | {kind: "Pullout", classes: "pullout", onDropPin: "dropPin", onShowTraffic: "showTraffic", onMapTypeSelect: "mapTypeSelect", onBookmarkSelect: "bookmarkSelect", components: [ 27 | {classes: "pullout-menu", defaultKind: "onyx.IconButton", components: [ 28 | {src: "images/menu-icon-info.png", panel: "info", ontap: "togglePullout"}, 29 | {src: "images/menu-icon-bookmark.png", panel: "bookmark", ontap: "togglePullout"}, 30 | {src: "images/menu-icon-mylocation.png", ontap: "findCurrentLocation"} 31 | ]} 32 | ]}, 33 | {kind: "Infobox"}, 34 | {kind: "CurrentLocation", onSuccess: "currentLocationSuccess"}, 35 | {kind: "SearchPlaces", credentials: "D7819411124F1FE57C72D3DB61E03F47470F143A", onResults: "searchResults"} 36 | ], 37 | togglePullout: function(inSender) { 38 | this.$.pullout.toggle(inSender.panel); 39 | }, 40 | mapTypeSelect: function(inSender, inEvent) { 41 | this.$.map.setMapType(inEvent.mapType); 42 | }, 43 | bookmarkSelect: function(inSender, inEvent) { 44 | var isNew = !this.bookmarkPin; 45 | var inItem = inEvent.item; 46 | this.bookmarkPin = this.$.map.updatePushpin(this.bookmarkPin, inItem.Latitude, inItem.Longitude, 47 | {icon: "images/poi_search.png", height: 48, width: 48}); 48 | this.bookmarkPin.item = inItem; 49 | if (isNew) { 50 | Microsoft.Maps.Events.addHandler(this.bookmarkPin, "mouseup", enyo.bind(this, "openInfobox")); 51 | } 52 | this.$.map.setCenter(inItem.Latitude, inItem.Longitude); 53 | }, 54 | openInfobox: function(e) { 55 | var pix = this.$.map.hasMap().tryLocationToPixel(e.target.getLocation(), Microsoft.Maps.PixelReference.control); 56 | if (e.originalEvent && e.originalEvent.stopPropagation) { 57 | e.originalEvent.stopPropagation(); 58 | } 59 | this.$.infobox.openWithItem(e.target.item, pix.y, pix.x + 18); 60 | }, 61 | dropPin: function(inSender, inEvent) { 62 | var map = this.$.map.hasMap(); 63 | if (!map) return; 64 | var loc = map.getCenter(); 65 | this.$.map.setCenter(loc.latitude, loc.longitude); 66 | this.$.map.setShowPin(inEvent.value); 67 | }, 68 | showTraffic: function(inSender, inEvent) { 69 | this.$.map.setShowTraffic(inEvent.value); 70 | }, 71 | findCurrentLocation: function() { 72 | this.$.currentLocation.go(); 73 | }, 74 | currentLocationSuccess: function(inSender, inData) { 75 | var c = inData.coords; 76 | this.$.map.setCenter(c.latitude, c.longitude); 77 | this.$.map.setZoom(14); 78 | inSender.stopTracking(); 79 | this.currentLocationPin = this.$.map.updatePushpin(this.currentLocationPin, c.latitude, c.longitude, 80 | {icon: "images/mylocation.png", height: 48, width: 48, anchor: new Microsoft.Maps.Point(24, 24)}); 81 | }, 82 | searchInputKeypress: function(inSender, inEvent) { 83 | if (inEvent.keyCode == 13) { 84 | this.search(); 85 | inSender.hasNode().blur(); 86 | } 87 | }, 88 | clearAll: function() { 89 | this.$.map.clearAll([this.currentLocationPin]); 90 | this.bookmarkPin = null; 91 | }, 92 | search: function() { 93 | this.clearAll(); 94 | var v = this.$.searchInput.getValue(); 95 | if (v) { 96 | var c = this.$.map.hasMap().getCenter(); 97 | this.$.searchPlaces.search({ 98 | Latitude: c.latitude, 99 | Longitude: c.longitude, 100 | Query: v 101 | }); 102 | } 103 | }, 104 | searchResults: function(inSender, inResponse) { 105 | var r = inResponse.results; 106 | for (var i=0, item; (item=r[i]); i++) { 107 | var p = this.$.map.createPushpin(item.Latitude, item.Longitude, { 108 | icon: "images/poi_search.png", height: 48, width: 48, text: String(i+1), 109 | textOffset: new Microsoft.Maps.Point(0, 7)}); 110 | p.item = item; 111 | Microsoft.Maps.Events.addHandler(p, 'mouseup', enyo.bind(this, "openInfobox")); 112 | if (!i) { 113 | this.$.map.setCenter(item.Latitude, item.Longitude); 114 | } 115 | } 116 | } 117 | }); 118 | -------------------------------------------------------------------------------- /apps/bingmaps/source/Pullout.js: -------------------------------------------------------------------------------- 1 | enyo.kind({ 2 | name: "Pullout", 3 | kind: "enyo.Slideable", 4 | events: { 5 | onDropPin: "", 6 | onShowTraffic: "", 7 | onMapTypeSelect: "", 8 | onBookmarkSelect: "" 9 | }, 10 | components: [ 11 | {name: "shadow", classes: "pullout-shadow"}, 12 | {kind: "onyx.Grabber", classes: "pullout-grabbutton"}, 13 | {kind: "FittableRows", classes: "enyo-fit", components: [ 14 | {name: "client", classes: "pullout-toolbar"}, 15 | {fit: true, style: "position: relative;", components: [ 16 | {name: "info", kind: "Scroller", classes: "enyo-fit", components: [ 17 | {kind: "onyx.Groupbox", classes: "settings", components: [ 18 | {kind: "onyx.GroupboxHeader", content: "General"}, 19 | {kind: "LabeledItem", label: "Show Drop Pin", icon: "images/icon-dropPin.png", defaultKind: "onyx.ToggleButton", onChange: "dropPinChange"}, 20 | {kind: "LabeledItem", label: "Show Traffic", icon: "images/icon-traffic.png", defaultKind: "onyx.ToggleButton", onChange: "showTrafficChange"} 21 | ]}, 22 | {name: "mapType", kind: "Group", classes: "onyx-groupbox settings", highlander: true, onchange: "mapTypeChange", components: [ 23 | {kind: "onyx.GroupboxHeader", content: "Map Type"}, 24 | {kind: "LabeledItem", label: "Road", mapType: "road", icon: "images/map-type-road.png", value: true}, 25 | {kind: "LabeledItem", label: "Satellite", mapType: "aerial", icon: "images/map-type-satellite.png"}, 26 | {kind: "LabeledItem", label: "Bird's Eye", mapType: "birdseye", icon: "images/map-type-bird-eye.png"} 27 | ]} 28 | ]}, 29 | {name: "bookmark", kind: "FittableRows", showing: false, classes: "enyo-fit", components: [ 30 | {kind: "onyx.RadioGroup", classes: "bookmark-header", components: [ 31 | {content: "Saved", active: true}, 32 | {content: "Recents"} 33 | ]}, 34 | {fit: true, kind: "Scroller", classes: "bookmark-scroller", components: [ 35 | {kind: "BookmarkList", onItemSelect: "itemSelect"} 36 | ]} 37 | ]} 38 | ]} 39 | ]} 40 | ], 41 | max: 100, 42 | value: 100, 43 | unit: "%", 44 | toggle: function(inPanelName) { 45 | var t = this.$[inPanelName]; 46 | if (t.showing && this.isAtMin()) { 47 | this.animateToMax(); 48 | } else { 49 | this.animateToMin(); 50 | this.$.info.hide(); 51 | this.$.bookmark.hide(); 52 | t.show(); 53 | t.resized(); 54 | } 55 | }, 56 | valueChanged: function() { 57 | this.inherited(arguments); 58 | this.$.shadow.setShowing(this.value !== this.max); 59 | }, 60 | dropPinChange: function(inSender) { 61 | this.doDropPin({value: inSender.getValue()}); 62 | }, 63 | showTrafficChange: function(inSender) { 64 | this.doShowTraffic({value: inSender.getValue()}); 65 | }, 66 | mapTypeChange: function(inSender, inEvent) { 67 | var o = inEvent.originator; 68 | this.doMapTypeSelect({mapType: o.parent.mapType}); 69 | }, 70 | itemSelect: function(inSender, inEvent) { 71 | this.doBookmarkSelect({item: inEvent.item}); 72 | } 73 | }); -------------------------------------------------------------------------------- /apps/bingmaps/source/SearchPlaces.js: -------------------------------------------------------------------------------- 1 | enyo.kind({ 2 | name: "SearchPlaces", 3 | kind: "Component", 4 | published: { 5 | credentials: "" 6 | }, 7 | events: { 8 | onResults: "" 9 | }, 10 | url: "http://api.bing.net/json.aspx", 11 | params: { 12 | Version: "2.0", 13 | Market: "en-US", 14 | c: "en-US", 15 | Options: "EnableHighlighting", 16 | Sources: "PhoneBook", 17 | "Phonebook.Count": 10, 18 | JsonType: "callback" 19 | }, 20 | search: function(inParams) { 21 | var params = enyo.clone(this.params); 22 | params.AppId = this.credentials; 23 | enyo.mixin(params, inParams); 24 | return new enyo.JsonpRequest({ 25 | url: this.url, 26 | callbackName: "JsonCallback" 27 | }) 28 | .response(enyo.bind(this, "processResponse")) 29 | .go(params); 30 | }, 31 | processResponse: function(inSender, inResponse) { 32 | var p = inResponse.SearchResponse.Phonebook; 33 | this.doResults({results: p && p.Results || []}); 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /apps/bingmaps/source/mockdata.js: -------------------------------------------------------------------------------- 1 | mock_bookmarks = [ 2 | {Title: "Blue Bottle Cafe", Address: "66 Mint Street, San Francisco, CA", Latitude: 37.782442, Longitude: -122.407467}, 3 | {Title: "Thirsty Bear Brewing", Address: "661 Howard Street, San Francisco, CA", Latitude: 37.785485, Longitude: -122.399698}, 4 | {Title: "Golden Boy Pizza", Address: "542 Green Street, San Francisco, CA", Latitude: 37.799632, Longitude: -122.407921}, 5 | {Title: "South Park Cafe", Address: "108 South Park Street, San Francisco, CA", Latitude: 37.781561, Longitude: -122.394318} 6 | ]; -------------------------------------------------------------------------------- /apps/cryptotweets/README.md: -------------------------------------------------------------------------------- 1 | ![CryptoNews icon](https://github.com/enyojs/support/raw/master/apps/cryptotweets/icon_64x64.png) 2 | 3 | This is a simple cryptogram game written in the enyojs project's Enyo + Onyx + 4 | Layout client-side JavaScript libraries. 5 | 6 | To play, go to . 7 | 8 | In order to get all of my "cells" to cooperate, I'm making pretty heavy use of 9 | Enyo custom events. Hovering the mouse over a cell causes it to tell its 10 | parent to waterfallDown a hover event with the encrypted letter, then all the 11 | cells use that event to decide if they should change their CSS class. I also 12 | use a "startGuess" event to let you trigger guesses either by tapping on a 13 | cell or by hitting a key on the keyboard. 14 | 15 | Let me know what you think! 16 | 17 | ## LICENSE 18 | 19 | This is released under the Apache-2.0 license, the same as the rest of the 20 | Enyo project. 21 | 22 | ## TODOs for CryptoTweets 23 | 24 | * Wrap cypher display at word boundaries 25 | * Add toggle button to turn statistics display on/off or 26 | make statistics a slidable -------------------------------------------------------------------------------- /apps/cryptotweets/appinfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "com.enyojs.sample.cryptonews", 3 | "version": "0.1.0", 4 | "vendor": "Enyo JS Framework", 5 | "type": "web", 6 | "main": "index.html", 7 | "title": "CryptoNews", 8 | "icon": "icon_48x48.png" 9 | } 10 | -------------------------------------------------------------------------------- /apps/cryptotweets/cellTest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CryptoTweet Test 5 | 34 | 35 | 36 |
37 |
T

X
38 |
H

F
39 |
E

Q
40 |
S

B
41 |
E

Q
42 |
 
 
 
43 |
H

F
44 |
A

P
45 |
N

V
46 |
D

E
47 |
S

B
48 |
49 |
50 | 51 | 52 | -------------------------------------------------------------------------------- /apps/cryptotweets/fonts/lovely_eunike_hans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/cryptotweets/fonts/lovely_eunike_hans.ttf -------------------------------------------------------------------------------- /apps/cryptotweets/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/cryptotweets/icon_32x32.png -------------------------------------------------------------------------------- /apps/cryptotweets/icon_48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/cryptotweets/icon_48x48.png -------------------------------------------------------------------------------- /apps/cryptotweets/icon_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/cryptotweets/icon_64x64.png -------------------------------------------------------------------------------- /apps/cryptotweets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | CryptoNews 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /apps/cryptotweets/source/App.js: -------------------------------------------------------------------------------- 1 | /* CryptoTweets, a game built using Enyo 2.0 */ 2 | 3 | /* custom events: 4 | * 5 | * /onGuess/ 6 | * sent from app down into cells when a guess is made, 7 | * either by the user or by the hint button. There are three 8 | * cypher: the encrypted letter 9 | * guess: what letter was guessed 10 | * hint: if true, this was a hint given by the system 11 | * 12 | * /onStartGuess/ 13 | * sent from a cell or from the keyboard handler, it signals 14 | * that the app should popup the guess UI. 15 | * cypher: what letter was picked to start the guess 16 | * 17 | * /onFinishGuess/ 18 | * sent from popup when second key is hit. 19 | * cypher: what letter was picked to start the guess 20 | * guess: what letter was picked to start the guess 21 | * 22 | * /resetGuess/ 23 | * sent to cells to have them revert to their original form 24 | */ 25 | 26 | enyo.kind({ 27 | name: "App", 28 | kind: enyo.Application, 29 | view: "MainView" 30 | }); 31 | 32 | enyo.kind({ 33 | name: "MainView", 34 | kind: enyo.FittableRows, 35 | classes: "onyx", 36 | handlers: { 37 | onStartGuess: "startGuess", 38 | onFinishGuess: "finishGuess", 39 | onkeypress: "handleKeyPress", 40 | onkeydown: "handleKeyDown" 41 | }, 42 | components: [ 43 | { kind: onyx.MoreToolbar, style: "background: #444F70;", 44 | movedClass: "toolbar-fixed-width", 45 | components: [ 46 | { content: "CryptoNews (powered by USA Today)", style: "padding-right: 10px" }, 47 | { kind: enyo.Group, highlander: false, components: [ 48 | {kind: onyx.Button, content: "Hint", onclick: "giveHint"}, 49 | {kind: onyx.Button, content: "Reset", onclick: "restart"}, 50 | {kind: onyx.Button, content: "Next", onclick: "nextTweet"} 51 | ]} 52 | ]}, 53 | // these all use the automatic name feature since they're unique kinds 54 | { kind: enyo.Scroller, fit: true, components: [ 55 | { kind: Cryptogram } 56 | ]}, 57 | { kind: PickLetterPopup }, 58 | { kind: "onyx.Popup", name: "loadingPopup", centered: true, modal: true, floating: true, components: [ 59 | { content: "Loading headlines..." } 60 | ]} 61 | ], 62 | create: function() { 63 | this.inherited(arguments); 64 | this.tweets = []; 65 | this.nextTweetIndex = 0; 66 | }, 67 | rendered: function() { 68 | this.inherited(arguments); 69 | this.fetchNews(); 70 | }, 71 | fetchNews: function(inSender) { 72 | this.$.loadingPopup.show(); 73 | var request = new enyo.JsonpRequest({ 74 | url: "http://api.usatoday.com/open/articles/topnews/home", 75 | callbackName: "jsoncallbackmethod" 76 | }); 77 | request.response(enyo.bind(this, "handleUSATodayResults")); 78 | request.go({ 79 | encoding: "json", 80 | api_key: "wcucm5mftznpwrembw824np3", 81 | count: 15 82 | }); 83 | }, 84 | handleUSATodayResults: function(inRequest, inResponse) { 85 | this.$.loadingPopup.hide(); 86 | this.tweets = []; 87 | enyo.forEach(inResponse.stories, function(t) { 88 | this.tweets.push(t.title); 89 | }, this); 90 | this.nextTweetIndex = 0; 91 | this.nextTweet(); 92 | }, 93 | giveHint: function() { 94 | this.$.cryptogram.giveHint(); 95 | }, 96 | restart: function() { 97 | this.$.cryptogram.restart(); 98 | }, 99 | nextTweet: function() { 100 | this.$.cryptogram.resetCypher(); 101 | this.$.cryptogram.setText(this.tweets[this.nextTweetIndex++]); 102 | if (this.nextTweetIndex >= this.tweets.length) { 103 | this.nextTweetIndex = 0; 104 | } 105 | }, 106 | handleKeyDown: function(inSender, inEvent) { 107 | // prevent backspace from changing page 108 | if (inEvent.keyCode === 8) return true; 109 | }, 110 | handleKeyPress: function(inSender, inEvent) { 111 | var key = String.fromCharCode(inEvent.charCode).toUpperCase(); 112 | if (key >= "A" && key <= "Z") { 113 | this.waterfall("onStartGuess", { cypher: key }); 114 | } 115 | else if (key === "?") { 116 | this.giveHint(); 117 | } 118 | }, 119 | startGuess: function(inSender, inEvent) { 120 | // clear hover before showing popup 121 | this.waterfallDown("onHoverCell", { cypher: null }); 122 | this.$.pickLetterPopup.show(inEvent.cypher); 123 | return true; 124 | }, 125 | finishGuess: function(inSender, inEvent) { 126 | this.$.cryptogram.guessLetter(inEvent.cypher, inEvent.guess, false); 127 | } 128 | }); 129 | 130 | enyo.ready(function() { 131 | new App(); 132 | }); -------------------------------------------------------------------------------- /apps/cryptotweets/source/Cell.js: -------------------------------------------------------------------------------- 1 | /* CryptoTweets, a game built using Enyo 2.0 */ 2 | 3 | enyo.kind({ 4 | name: "Cell", 5 | classes: "cell", 6 | published: { 7 | top: "", 8 | bottom: "", 9 | mutable: true, 10 | encrypted: false 11 | }, 12 | events: { 13 | onHoverCell: "" 14 | }, 15 | components: [ 16 | { name: "top", classes: "top" }, 17 | { name: "middle", classes: "middle", content: Unicode.nbsp }, 18 | { name: "bottom", classes: "bottom" } 19 | ], 20 | bindings: [ 21 | { from: ".top", to: ".$.top.content", transform: "xfrmBlankToNBSP" }, 22 | { from: ".bottom", to: ".$.bottom.content", transform: "xfrmBlankToNBSP" } 23 | ], 24 | handlers: { 25 | onGuess: "guess", 26 | onResetGuess: "reset", 27 | onmouseover: "hoverStart", 28 | onmouseout: "hoverEnd", 29 | onUpdateHoverState: "updateHoverState" 30 | }, 31 | create: function () { 32 | this.inherited(arguments); 33 | this.encryptedChanged(); 34 | this.mutableChanged(); 35 | }, 36 | reset: function () { 37 | // go back to "unguessed" state 38 | if (this.encrypted) { 39 | this.setMutable(true); 40 | this.setTop(""); 41 | } 42 | }, 43 | updateMiddle: function() { 44 | if (this.encrypted && this.mutable) { 45 | this.$.middle.setContent(Unicode.mdash); 46 | } 47 | else { 48 | this.$.middle.setContent(Unicode.nbsp); 49 | } 50 | }, 51 | encryptedChanged: function() { 52 | this.addRemoveClass("letter", this.encrypted); 53 | this.updateMiddle(); 54 | }, 55 | mutableChanged: function() { 56 | this.updateMiddle(); 57 | }, 58 | xfrmBlankToNBSP: function(str) { 59 | return str || Unicode.nbsp; 60 | }, 61 | guess: function(inSender, inEvent) { 62 | if (this.mutable) { 63 | if (inEvent.cypher === this.bottom) { 64 | this.setTop(inEvent.guess); 65 | // hints change letters to be unchangable 66 | if (inEvent.hint) { 67 | this.setMutable(false); 68 | } 69 | } else if (inEvent.guess == this.top) { 70 | // clear any cells that had that guess 71 | this.setTop(Unicode.nbsp); 72 | } 73 | } 74 | }, 75 | tap: function() { 76 | // when tapping on a mutable cell, start a guess dialog 77 | if (this.mutable) { 78 | this.bubble("onStartGuess", { cypher: this.bottom }); 79 | } 80 | }, 81 | // notify owner about mouse enter/leaves 82 | hoverStart: function() { 83 | if (this.encrypted && this.mutable) { 84 | this.doHoverCell({ cypher: this.bottom }); 85 | } 86 | }, 87 | hoverEnd: function() { 88 | this.doHoverCell({ cypher: null }); 89 | }, 90 | // handle event sent down the tree to change my visible hover state 91 | updateHoverState: function(inSender, inEvent) { 92 | if (this.encrypted && this.mutable) { 93 | this.addRemoveClass("selectedCell", this.bottom === inEvent.cypher); 94 | } 95 | } 96 | }); -------------------------------------------------------------------------------- /apps/cryptotweets/source/Cryptogram.js: -------------------------------------------------------------------------------- 1 | /* CryptoTweets, a game built using Enyo 2.0 */ 2 | 3 | enyo.kind({ 4 | name: "Cryptogram", 5 | allowHtml: true, 6 | classes: "cryptogram", 7 | components: [ 8 | { name: "cells", kind: enyo.Repeater, onSetupItem: "setupCell", 9 | components: [ { kind: Cell } ] }, 10 | { tag: "br", attributes: { clear: "all" } }, 11 | { kind: Distribution } 12 | ], 13 | published: { 14 | text: "" 15 | }, 16 | handlers: { 17 | onHoverCell: "broadcastHover" 18 | }, 19 | // private properties 20 | // hash mapping clear text letters to guessed letter 21 | guesses: null, 22 | resetCypher: function(text) { 23 | this.cypher = new Cypher(); 24 | this.$.distribution.setCypher(this.cypher); 25 | }, 26 | setGuess: function(clearLetter, guessLetter) { 27 | // clear out any old guess with the same letter 28 | forEachLetter(this, function(ch) { 29 | if (this.guesses[ch] === guessLetter) { 30 | this.guesses[ch] = null; 31 | } 32 | }); 33 | // set the new guess 34 | this.guesses[clearLetter] = guessLetter; 35 | }, 36 | create: function() { 37 | this.inherited(arguments); 38 | this.resetCypher(); 39 | this.textChanged(); 40 | }, 41 | markUnencrypted: function(regex) { 42 | var result; 43 | regex.lastIndex = 0; 44 | while (result = regex.exec(this.text)) { 45 | for (var i = result.index; i < regex.lastIndex; ++i) { 46 | this.isEncrypted[i] = false; 47 | } 48 | } 49 | }, 50 | findUnencryptedRuns: function() { 51 | // put all characters into cells with default isEncrypted value 52 | this.isEncrypted = []; 53 | for (var i = 0; i < this.text.length; ++i) { 54 | this.isEncrypted[i] = (this.text[i] >= 'A') && (this.text[i] <= 'Z'); 55 | } 56 | // find specific strings to "unencrypt" - URLs, RT, hashtags, @names 57 | this.markUnencrypted(/^RT\s/g); 58 | this.markUnencrypted(/HTTPS?:\/\/\S*/g); 59 | this.markUnencrypted(/@\w+/g); 60 | this.markUnencrypted(/#\w+/g); 61 | }, 62 | generateDistribution: function() { 63 | var distribution = {}; 64 | forEachLetter(this, function(ch) { 65 | distribution[ch] = 0; 66 | }); 67 | for (var i = this.text.length - 1; i >= 0; --i) { 68 | var ch = this.text[i]; 69 | if (this.isEncrypted[i]) { 70 | distribution[ch]++; 71 | } 72 | } 73 | this.$.distribution.setDistribution(distribution); 74 | }, 75 | textChanged: function() { 76 | // start by forcing uppercase and trimming white space 77 | this.text = this.text.toUpperCase(); 78 | this.text = this.text.replace(/^\s+|\s+$/g,''); 79 | this.text = this.text.replace(/\s+/g, Unicode.nbsp); 80 | this.findUnencryptedRuns(); 81 | this.generateDistribution(); 82 | this.guesses = {}; 83 | this.$.cells.setCount(this.text.length); 84 | }, 85 | setupCell: function(inSender, inEvent) { 86 | var ch = this.text[inEvent.index]; 87 | var isEncrypted = this.isEncrypted[inEvent.index]; 88 | var cell = inEvent.item.$.cell; 89 | cell.setEncrypted(isEncrypted); 90 | cell.setMutable(isEncrypted); 91 | if (isEncrypted) { 92 | cell.setTop(Unicode.nbsp); 93 | cell.setBottom(this.cypher.clearToCypher(ch)); 94 | } 95 | else { 96 | cell.setTop(ch); 97 | cell.setBottom(ch); 98 | } 99 | }, 100 | // give a hint using the given encrypted letter 101 | giveHint: function(pick) { 102 | if (pick) { 103 | pick = this.cypher.cypherToClear(pick); 104 | } 105 | else if (!pick) { 106 | // if no argument, pick a unguessed letter to reveal 107 | var unguessed = []; 108 | forEachLetter(this, function(ch) { 109 | if (!this.guesses[ch] && this.$.distribution.getCount(ch) > 0) { 110 | unguessed.push(ch); 111 | } 112 | }); 113 | if (unguessed.length > 0) { 114 | pick = unguessed[enyo.irand(unguessed.length)]; 115 | } 116 | } 117 | if (pick) { 118 | this.guessLetter(this.cypher.clearToCypher(pick), pick, true); 119 | } 120 | }, 121 | guessLetter: function(cypher, guess, hint) { 122 | var clear = this.cypher.cypherToClear(cypher); 123 | this.setGuess(clear, guess); 124 | this.waterfall("onGuess", { 125 | cypher: cypher, 126 | guess: guess, 127 | hint: hint 128 | }); 129 | }, 130 | restart: function() { 131 | this.guesses = {}; 132 | this.waterfall("onResetGuess"); 133 | }, 134 | broadcastHover: function(inSender, inEvent) { 135 | // turn onHoverCell into state update message for all the cells 136 | this.waterfallDown("onUpdateHoverState", { cypher: inEvent.cypher }); 137 | } 138 | }); -------------------------------------------------------------------------------- /apps/cryptotweets/source/Cypher.js: -------------------------------------------------------------------------------- 1 | /* CryptoTweets, a game built using Enyo 2.0 */ 2 | 3 | /** This kind encapsulates the substitution cypher needed for the game, 4 | but doesn't include cyphertext management. */ 5 | enyo.kind({ 6 | name: "Cypher", 7 | kind: enyo.Object, 8 | shuffleAlphabet: function() { 9 | // take the shuffled alphabet and assign it to our hashes 10 | var alpha = generateCypherAlphabet(); 11 | this.cypher = {}; 12 | this.reverseCypher = {}; 13 | enyo.forEach(alpha, function(val,idx,arr) { 14 | var key = String.fromCharCode(idx + 65); 15 | this.cypher[key] = val; 16 | this.reverseCypher[val] = key; 17 | }, this); 18 | }, 19 | constructor: function() { 20 | this.inherited(arguments); 21 | this.shuffleAlphabet(); 22 | }, 23 | clearToCypher: function(clearLetter) { 24 | return this.cypher[clearLetter]; 25 | }, 26 | cypherToClear: function(cypherLetter) { 27 | return this.reverseCypher[cypherLetter]; 28 | } 29 | }); -------------------------------------------------------------------------------- /apps/cryptotweets/source/Distribution.js: -------------------------------------------------------------------------------- 1 | /* CryptoTweets, a game built using Enyo 2.0 */ 2 | 3 | enyo.kind({ 4 | name: "Distribution", 5 | classes: "distribution", 6 | allowHtml: true, 7 | published: { 8 | cypher: null, 9 | distribution: null 10 | }, 11 | distributionChanged: function() { 12 | // short circuit the case where no distribution has been set 13 | var distribution = this.distribution; 14 | if (!distribution) { 15 | this.setContent(""); 16 | return; 17 | } 18 | // save a sorted list of letters in highest-first order 19 | this.sortedDistribtionKeys = enyo.keys(distribution); 20 | this.sortedDistribtionKeys.sort(function(a, b) { 21 | return distribution[b] - distribution[a]; 22 | }); 23 | // regenerate content 24 | var content = ""; 25 | enyo.forEach(this.sortedDistribtionKeys, function(ch) { 26 | if (distribution[ch] > 0) { 27 | content += "" + this.cypher.clearToCypher(ch) + 28 | ": " + distribution[ch] + ""; 29 | } 30 | }, this); 31 | this.setContent(content); 32 | }, 33 | getCount: function(ch) { 34 | return this.distribution[ch]; 35 | }, 36 | create: function() { 37 | this.inherited(arguments); 38 | this.distributionChanged(); 39 | } 40 | }); 41 | -------------------------------------------------------------------------------- /apps/cryptotweets/source/PickLetter.js: -------------------------------------------------------------------------------- 1 | /* CryptoTweets, a game built using Enyo 2.0 */ 2 | 3 | enyo.kind({ 4 | name: "PickLetterPopup", 5 | kind: onyx.Popup, 6 | centered: true, 7 | modal: true, 8 | floating: true, 9 | style: "padding: 10px; width: 274px;", 10 | handlers: { 11 | onkeypress: "keypress" 12 | }, 13 | components: [ 14 | { name: "prompt", style: "font-size: 30px; padding-bottom: 6px; text-align: center;" }, 15 | { name: "keyboard", kind: enyo.Repeater, count: 26, onSetupItem: "setupKeys", 16 | components: [ { kind: onyx.Button, ontap: "tappedKeys", 17 | style: "margin: 3px 3px; width: 36px; font-size: 14px; padding: 6px 10px;" } ] } 18 | ], 19 | setupKeys: function(inSender, inEvent) { 20 | var ch = String.fromCharCode(inEvent.index + 65); 21 | inEvent.item.$.button.setContent(String.fromCharCode(inEvent.index + 65)); 22 | if (ch === this.cypherLetter) { 23 | inEvent.item.$.button.setDisabled(true); 24 | } 25 | return true; 26 | }, 27 | tappedKeys: function(inSender, inEvent) { 28 | this.guess(String.fromCharCode(inEvent.index + 65)); 29 | return true; 30 | }, 31 | setPrompt: function(guess) { 32 | this.$.prompt.setContent(this.cypherLetter + " " + 33 | Unicode.rightwardArrow + " " + guess); 34 | }, 35 | show: function(cypherLetter) { 36 | this.cypherLetter = cypherLetter; 37 | // rebuild keyboard to disable cypherletter button 38 | this.$.keyboard.build(); 39 | this.setPrompt("?"); 40 | this.guessed = false; 41 | this.inherited(arguments); 42 | }, 43 | guess: function(key) { 44 | this.setPrompt(key); 45 | this.guessed = true; 46 | enyo.job("clearGuessPopup", enyo.bind(this, this.hide), 1000); 47 | this.bubble("onFinishGuess", { 48 | cypher: this.cypherLetter, 49 | guess: key 50 | }); 51 | }, 52 | keypress: function(inSender, inEvent) { 53 | var key = String.fromCharCode(inEvent.charCode).toUpperCase(); 54 | // after guess, letters restart guess to allow quick entry, otherwise ignore 55 | if (this.guessed) { 56 | if (key >= "A" && key <= "Z") { 57 | enyo.job.stop("clearGuessPopup"); 58 | this.bubble("onStartGuess", { cypher: key }); 59 | } 60 | return true; 61 | } 62 | // allow backspace or space to clear a cell 63 | if (inEvent.charCode === 8 || inEvent.charCode === 32) { 64 | this.setPrompt(Unicode.nbsp); 65 | this.guessed = true; 66 | enyo.job("clearGuessPopup", enyo.bind(this, this.hide), 1000); 67 | this.bubble("onFinishGuess", { 68 | cypher: this.cypherLetter, 69 | guess: null 70 | }); 71 | return true; 72 | } 73 | // otherwise, accept letter as guess 74 | if (key >= "A" && key <= "Z") { 75 | this.guess(key); 76 | return true; 77 | } 78 | } 79 | }); -------------------------------------------------------------------------------- /apps/cryptotweets/source/app.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: lovely_eunike_hans; 3 | src: url(../fonts/lovely_eunike_hans.ttf); 4 | } 5 | 6 | * { 7 | -moz-box-sizing: border-box; 8 | -webkit-box-sizing: border-box; 9 | box-sizing: border-box; 10 | } 11 | 12 | body { 13 | margin: 10px; 14 | background-color: #333333; 15 | } 16 | 17 | .btn { 18 | margin-left: 10px; 19 | } 20 | 21 | .cryptogram { 22 | margin: 10px; 23 | padding: 20px 50px; 24 | background-color: #BBBBBB; 25 | } 26 | 27 | .cell { 28 | float: left; 29 | width: 24px; 30 | padding: 10px 5px 3px; 31 | margin: 2px; 32 | font: 18px "Courier New", Courier, monospace; 33 | text-align: center; 34 | line-height: 80%; 35 | } 36 | 37 | .cell > .top { 38 | font-family: lovely_eunike_hans, "Courier New", Courier, monospace; 39 | color: navy; 40 | } 41 | 42 | .selectedCell { 43 | background-color: green; 44 | color: white; 45 | cursor: pointer; 46 | } 47 | 48 | .selectedCell > .top { 49 | color: white; 50 | } 51 | 52 | .topButtons { 53 | margin: 10px; 54 | } 55 | 56 | .distribution { 57 | font: 14px "Courier New"; 58 | color: black; 59 | border: 1px solid; 60 | margin-top: 20px; 61 | padding: 5px; 62 | } 63 | 64 | .distroLetter:after { 65 | content: ", "; 66 | } 67 | 68 | .distroLetter:last-child:after { 69 | content: ""; 70 | } 71 | 72 | .toolbar-fixed-width > .onyx-button { 73 | width: 80px; 74 | } -------------------------------------------------------------------------------- /apps/cryptotweets/source/package.js: -------------------------------------------------------------------------------- 1 | enyo.depends( 2 | "$lib/layout/fittable", 3 | "$lib/onyx", 4 | "app.css", 5 | "utils.js", 6 | "Cypher.js", 7 | "Cell.js", 8 | "Distribution.js", 9 | "Cryptogram.js", 10 | "PickLetter.js", 11 | "App.js" 12 | ); 13 | -------------------------------------------------------------------------------- /apps/cryptotweets/source/utils.js: -------------------------------------------------------------------------------- 1 | /* CryptoTweets, a game built using Enyo 2.0 */ 2 | 3 | /* common Unicode strings for use directly in content */ 4 | var Unicode = { 5 | nbsp: "\u00A0", 6 | mdash: "\u2014", 7 | leftwardArrow: "\u2190", 8 | upwardArrow: "\u2191", 9 | rightwardArrow: "\u2192", 10 | downwardArrow: "\u2193" 11 | }; 12 | 13 | /** swap two elements of an array or object */ 14 | function swapElements(obj, idx1, idx2) { 15 | var hold = obj[idx1]; 16 | obj[idx1] = obj[idx2]; 17 | obj[idx2] = hold; 18 | } 19 | /** call a function for each letter in the range 'A' to 'Z'. 20 | Additional arguments are passed into the callback. */ 21 | function forEachLetter(that, fn) { 22 | // copy arguments into new array with space for letter in front */ 23 | var args = enyo.cloneArray(arguments, 2); 24 | args.unshift(""); 25 | 26 | var aCode = 'A'.charCodeAt(0); 27 | var zCode = 'Z'.charCodeAt(0); 28 | for (var chCode = aCode; chCode <= zCode; ++chCode) { 29 | args[0] = String.fromCharCode(chCode); 30 | fn.apply(that, args); 31 | } 32 | } 33 | 34 | /** takes a string, returns an array where the 0 element is the 35 | string truncated at the last space before length and the 36 | second string is the remainder after spaces are trimmed. 37 | remainder is null if there's nothing left. */ 38 | function wrapStringAtSpace(str, length) { 39 | str = str.replace(/ +/g, " ").trim(); 40 | if (str.length <= length) 41 | return [str, null]; 42 | 43 | /* chop string at length bytes */ 44 | var trimmed = str.slice(0, length); 45 | /* find last space in string */ 46 | var lastSpace = trimmed.lastIndexOf(' '); 47 | /* chop off spaces and left over characters */ 48 | if (lastSpace !== -1) { 49 | trimmed = trimmed.slice(0, lastSpace).trim(); 50 | } 51 | /* prepare remainder using offsets */ 52 | remainder = str.slice(trimmed.length).trim(); 53 | return [trimmed, remainder]; 54 | } 55 | 56 | /** identify hashtags in a string and save start & end 57 | positions to an array for use in determining if they should be 58 | encrypted or not */ 59 | function findHashTags(str) { 60 | var hashTagRE = /#[a-zA-Z0-9_]+/g; 61 | var results = []; 62 | var match; 63 | while ((match = hashTagRE.exec(str))) { 64 | results.push(match.index, match.index + match[0].length - 1); 65 | } 66 | return results; 67 | } 68 | 69 | /** generate a new cypher alphabet for the letters A-Z where no 70 | encrypted letter maps to the original one. The distribution is 71 | probably not uniform, but it works well enough for game purposes */ 72 | function generateCypherAlphabet() { 73 | var alpha = [ 74 | "A","B","C","D","E","F","G","H","I", 75 | "J","K","L","M","N","O","P","Q","R", 76 | "S","T","U","V","W","X","Y","Z"]; 77 | // we'll go through alphabet and randomly swap each letter with 78 | // another letter in the string, rejecting swaps that would put a 79 | // letter back in its original position. 80 | for (var i = 0; i < 26; ++i) { 81 | var swapPos; 82 | do { 83 | swapPos = enyo.irand(26); 84 | // and skip over a swap that puts the letter 85 | // back in its original position 86 | } while (alpha[swapPos] === String.fromCharCode(65 + i) || 87 | alpha[i] === String.fromCharCode(65 + swapPos)); 88 | swapElements(alpha, i, swapPos); 89 | } 90 | return alpha; 91 | } -------------------------------------------------------------------------------- /apps/cryptotweets/tests/SpecRunner.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Jasmine Spec Runner 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /apps/cryptotweets/tests/cypher.js: -------------------------------------------------------------------------------- 1 | beforeEach(function() { 2 | this.addMatchers({ 3 | toBeValidCypher: function() { 4 | var cypher = this.actual; 5 | var message = "everything is fine"; 6 | this.message = function() { return message; }; 7 | 8 | for (var i = 0; i < 26; ++i) { 9 | var ch = String.fromCharCode(i + 65); 10 | if (cypher[i] === ch) { 11 | message = "Expected cypher[" + i + "] to not contain " + ch; 12 | return false; 13 | } 14 | } 15 | return true; 16 | } 17 | }); 18 | }); 19 | 20 | describe('generateCypherAlphabet',function(){ 21 | it('is valid cypher x1000',function(){ 22 | for (var i = 0; i < 1000; ++i) { 23 | expect(generateCypherAlphabet()).toBeValidCypher(); 24 | } 25 | }); 26 | }); -------------------------------------------------------------------------------- /apps/cryptotweets/tests/findHashTags.js: -------------------------------------------------------------------------------- 1 | describe('findHashTags',function(){ 2 | it('string with none',function(){ 3 | expect(findHashTags("")). 4 | toEqual([]); 5 | }); 6 | it('string with one',function(){ 7 | expect(findHashTags("Are we #there yet?")). 8 | toEqual([7,12]); 9 | }); 10 | it('string with three',function(){ 11 | expect(findHashTags("#up to the #Minute in #2011")). 12 | toEqual([0,2, 11,17, 22,26]); 13 | }); 14 | it('abbreviated hashtags',function(){ 15 | expect(findHashTags("#one! #two? #three-#four#five")). 16 | toEqual([0,3, 6,9, 12,17, 19,23, 24,28]); 17 | }); 18 | }); -------------------------------------------------------------------------------- /apps/cryptotweets/tests/jasmine/MIT.LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2011 Pivotal Labs 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /apps/cryptotweets/tests/jasmine/jasmine-html.js: -------------------------------------------------------------------------------- 1 | jasmine.TrivialReporter = function(doc) { 2 | this.document = doc || document; 3 | this.suiteDivs = {}; 4 | this.logRunningSpecs = false; 5 | }; 6 | 7 | jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) { 8 | var el = document.createElement(type); 9 | 10 | for (var i = 2; i < arguments.length; i++) { 11 | var child = arguments[i]; 12 | 13 | if (typeof child === 'string') { 14 | el.appendChild(document.createTextNode(child)); 15 | } else { 16 | if (child) { el.appendChild(child); } 17 | } 18 | } 19 | 20 | for (var attr in attrs) { 21 | if (attr == "className") { 22 | el[attr] = attrs[attr]; 23 | } else { 24 | el.setAttribute(attr, attrs[attr]); 25 | } 26 | } 27 | 28 | return el; 29 | }; 30 | 31 | jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { 32 | var showPassed, showSkipped; 33 | 34 | this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' }, 35 | this.createDom('div', { className: 'banner' }, 36 | this.createDom('div', { className: 'logo' }, 37 | this.createDom('span', { className: 'title' }, "Jasmine"), 38 | this.createDom('span', { className: 'version' }, runner.env.versionString())), 39 | this.createDom('div', { className: 'options' }, 40 | "Show ", 41 | showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }), 42 | this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "), 43 | showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }), 44 | this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped") 45 | ) 46 | ), 47 | 48 | this.runnerDiv = this.createDom('div', { className: 'runner running' }, 49 | this.createDom('a', { className: 'run_spec', href: '?' }, "run all"), 50 | this.runnerMessageSpan = this.createDom('span', {}, "Running..."), 51 | this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, "")) 52 | ); 53 | 54 | this.document.body.appendChild(this.outerDiv); 55 | 56 | var suites = runner.suites(); 57 | for (var i = 0; i < suites.length; i++) { 58 | var suite = suites[i]; 59 | var suiteDiv = this.createDom('div', { className: 'suite' }, 60 | this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"), 61 | this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description)); 62 | this.suiteDivs[suite.id] = suiteDiv; 63 | var parentDiv = this.outerDiv; 64 | if (suite.parentSuite) { 65 | parentDiv = this.suiteDivs[suite.parentSuite.id]; 66 | } 67 | parentDiv.appendChild(suiteDiv); 68 | } 69 | 70 | this.startedAt = new Date(); 71 | 72 | var self = this; 73 | showPassed.onclick = function(evt) { 74 | if (showPassed.checked) { 75 | self.outerDiv.className += ' show-passed'; 76 | } else { 77 | self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, ''); 78 | } 79 | }; 80 | 81 | showSkipped.onclick = function(evt) { 82 | if (showSkipped.checked) { 83 | self.outerDiv.className += ' show-skipped'; 84 | } else { 85 | self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, ''); 86 | } 87 | }; 88 | }; 89 | 90 | jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) { 91 | var results = runner.results(); 92 | var className = (results.failedCount > 0) ? "runner failed" : "runner passed"; 93 | this.runnerDiv.setAttribute("class", className); 94 | //do it twice for IE 95 | this.runnerDiv.setAttribute("className", className); 96 | var specs = runner.specs(); 97 | var specCount = 0; 98 | for (var i = 0; i < specs.length; i++) { 99 | if (this.specFilter(specs[i])) { 100 | specCount++; 101 | } 102 | } 103 | var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s"); 104 | message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"; 105 | this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild); 106 | 107 | this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString())); 108 | }; 109 | 110 | jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) { 111 | var results = suite.results(); 112 | var status = results.passed() ? 'passed' : 'failed'; 113 | if (results.totalCount === 0) { // todo: change this to check results.skipped 114 | status = 'skipped'; 115 | } 116 | this.suiteDivs[suite.id].className += " " + status; 117 | }; 118 | 119 | jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) { 120 | if (this.logRunningSpecs) { 121 | this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); 122 | } 123 | }; 124 | 125 | jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) { 126 | var results = spec.results(); 127 | var status = results.passed() ? 'passed' : 'failed'; 128 | if (results.skipped) { 129 | status = 'skipped'; 130 | } 131 | var specDiv = this.createDom('div', { className: 'spec ' + status }, 132 | this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"), 133 | this.createDom('a', { 134 | className: 'description', 135 | href: '?spec=' + encodeURIComponent(spec.getFullName()), 136 | title: spec.getFullName() 137 | }, spec.description)); 138 | 139 | 140 | var resultItems = results.getItems(); 141 | var messagesDiv = this.createDom('div', { className: 'messages' }); 142 | for (var i = 0; i < resultItems.length; i++) { 143 | var result = resultItems[i]; 144 | 145 | if (result.type == 'log') { 146 | messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); 147 | } else if (result.type == 'expect' && result.passed && !result.passed()) { 148 | messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); 149 | 150 | if (result.trace.stack) { 151 | messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); 152 | } 153 | } 154 | } 155 | 156 | if (messagesDiv.childNodes.length > 0) { 157 | specDiv.appendChild(messagesDiv); 158 | } 159 | 160 | this.suiteDivs[spec.suite.id].appendChild(specDiv); 161 | }; 162 | 163 | jasmine.TrivialReporter.prototype.log = function() { 164 | var console = jasmine.getGlobal().console; 165 | if (console && console.log) { 166 | if (console.log.apply) { 167 | console.log.apply(console, arguments); 168 | } else { 169 | console.log(arguments); // ie fix: console.log.apply doesn't exist on ie 170 | } 171 | } 172 | }; 173 | 174 | jasmine.TrivialReporter.prototype.getLocation = function() { 175 | return this.document.location; 176 | }; 177 | 178 | jasmine.TrivialReporter.prototype.specFilter = function(spec) { 179 | var paramMap = {}; 180 | var params = this.getLocation().search.substring(1).split('&'); 181 | for (var i = 0; i < params.length; i++) { 182 | var p = params[i].split('='); 183 | paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); 184 | } 185 | 186 | if (!paramMap.spec) { 187 | return true; 188 | } 189 | return spec.getFullName().indexOf(paramMap.spec) === 0; 190 | }; 191 | -------------------------------------------------------------------------------- /apps/cryptotweets/tests/jasmine/jasmine.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; 3 | } 4 | 5 | 6 | .jasmine_reporter a:visited, .jasmine_reporter a { 7 | color: #303; 8 | } 9 | 10 | .jasmine_reporter a:hover, .jasmine_reporter a:active { 11 | color: blue; 12 | } 13 | 14 | .run_spec { 15 | float:right; 16 | padding-right: 5px; 17 | font-size: .8em; 18 | text-decoration: none; 19 | } 20 | 21 | .jasmine_reporter { 22 | margin: 0 5px; 23 | } 24 | 25 | .banner { 26 | color: #303; 27 | background-color: #fef; 28 | padding: 5px; 29 | } 30 | 31 | .logo { 32 | float: left; 33 | font-size: 1.1em; 34 | padding-left: 5px; 35 | } 36 | 37 | .logo .version { 38 | font-size: .6em; 39 | padding-left: 1em; 40 | } 41 | 42 | .runner.running { 43 | background-color: yellow; 44 | } 45 | 46 | 47 | .options { 48 | text-align: right; 49 | font-size: .8em; 50 | } 51 | 52 | 53 | 54 | 55 | .suite { 56 | border: 1px outset gray; 57 | margin: 5px 0; 58 | padding-left: 1em; 59 | } 60 | 61 | .suite .suite { 62 | margin: 5px; 63 | } 64 | 65 | .suite.passed { 66 | background-color: #dfd; 67 | } 68 | 69 | .suite.failed { 70 | background-color: #fdd; 71 | } 72 | 73 | .spec { 74 | margin: 5px; 75 | padding-left: 1em; 76 | clear: both; 77 | } 78 | 79 | .spec.failed, .spec.passed, .spec.skipped { 80 | padding-bottom: 5px; 81 | border: 1px solid gray; 82 | } 83 | 84 | .spec.failed { 85 | background-color: #fbb; 86 | border-color: red; 87 | } 88 | 89 | .spec.passed { 90 | background-color: #bfb; 91 | border-color: green; 92 | } 93 | 94 | .spec.skipped { 95 | background-color: #bbb; 96 | } 97 | 98 | .messages { 99 | border-left: 1px dashed gray; 100 | padding-left: 1em; 101 | padding-right: 1em; 102 | } 103 | 104 | .passed { 105 | background-color: #cfc; 106 | display: none; 107 | } 108 | 109 | .failed { 110 | background-color: #fbb; 111 | } 112 | 113 | .skipped { 114 | color: #777; 115 | background-color: #eee; 116 | display: none; 117 | } 118 | 119 | 120 | /*.resultMessage {*/ 121 | /*white-space: pre;*/ 122 | /*}*/ 123 | 124 | .resultMessage span.result { 125 | display: block; 126 | line-height: 2em; 127 | color: black; 128 | } 129 | 130 | .resultMessage .mismatch { 131 | color: black; 132 | } 133 | 134 | .stackTrace { 135 | white-space: pre; 136 | font-size: .8em; 137 | margin-left: 10px; 138 | max-height: 5em; 139 | overflow: auto; 140 | border: 1px inset red; 141 | padding: 1em; 142 | background: #eef; 143 | } 144 | 145 | .finished-at { 146 | padding-left: 1em; 147 | font-size: .6em; 148 | } 149 | 150 | .show-passed .passed, 151 | .show-skipped .skipped { 152 | display: block; 153 | } 154 | 155 | 156 | #jasmine_content { 157 | position:fixed; 158 | right: 100%; 159 | } 160 | 161 | .runner { 162 | border: 1px solid gray; 163 | display: block; 164 | margin: 5px 0; 165 | padding: 2px 0 2px 10px; 166 | } 167 | -------------------------------------------------------------------------------- /apps/cryptotweets/tests/jasmine/jasmine.js: -------------------------------------------------------------------------------- 1 | var isCommonJS = typeof window == "undefined"; 2 | 3 | /** 4 | * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. 5 | * 6 | * @namespace 7 | */ 8 | var jasmine = {}; 9 | if (isCommonJS) exports.jasmine = jasmine; 10 | /** 11 | * @private 12 | */ 13 | jasmine.unimplementedMethod_ = function() { 14 | throw new Error("unimplemented method"); 15 | }; 16 | 17 | /** 18 | * Use jasmine.undefined instead of undefined, since undefined is just 19 | * a plain old variable and may be redefined by somebody else. 20 | * 21 | * @private 22 | */ 23 | jasmine.undefined = jasmine.___undefined___; 24 | 25 | /** 26 | * Show diagnostic messages in the console if set to true 27 | * 28 | */ 29 | jasmine.VERBOSE = false; 30 | 31 | /** 32 | * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed. 33 | * 34 | */ 35 | jasmine.DEFAULT_UPDATE_INTERVAL = 250; 36 | 37 | /** 38 | * Default timeout interval in milliseconds for waitsFor() blocks. 39 | */ 40 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; 41 | 42 | jasmine.getGlobal = function() { 43 | function getGlobal() { 44 | return this; 45 | } 46 | 47 | return getGlobal(); 48 | }; 49 | 50 | /** 51 | * Allows for bound functions to be compared. Internal use only. 52 | * 53 | * @ignore 54 | * @private 55 | * @param base {Object} bound 'this' for the function 56 | * @param name {Function} function to find 57 | */ 58 | jasmine.bindOriginal_ = function(base, name) { 59 | var original = base[name]; 60 | if (original.apply) { 61 | return function() { 62 | return original.apply(base, arguments); 63 | }; 64 | } else { 65 | // IE support 66 | return jasmine.getGlobal()[name]; 67 | } 68 | }; 69 | 70 | jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); 71 | jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); 72 | jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); 73 | jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); 74 | 75 | jasmine.MessageResult = function(values) { 76 | this.type = 'log'; 77 | this.values = values; 78 | this.trace = new Error(); // todo: test better 79 | }; 80 | 81 | jasmine.MessageResult.prototype.toString = function() { 82 | var text = ""; 83 | for (var i = 0; i < this.values.length; i++) { 84 | if (i > 0) text += " "; 85 | if (jasmine.isString_(this.values[i])) { 86 | text += this.values[i]; 87 | } else { 88 | text += jasmine.pp(this.values[i]); 89 | } 90 | } 91 | return text; 92 | }; 93 | 94 | jasmine.ExpectationResult = function(params) { 95 | this.type = 'expect'; 96 | this.matcherName = params.matcherName; 97 | this.passed_ = params.passed; 98 | this.expected = params.expected; 99 | this.actual = params.actual; 100 | this.message = this.passed_ ? 'Passed.' : params.message; 101 | 102 | var trace = (params.trace || new Error(this.message)); 103 | this.trace = this.passed_ ? '' : trace; 104 | }; 105 | 106 | jasmine.ExpectationResult.prototype.toString = function () { 107 | return this.message; 108 | }; 109 | 110 | jasmine.ExpectationResult.prototype.passed = function () { 111 | return this.passed_; 112 | }; 113 | 114 | /** 115 | * Getter for the Jasmine environment. Ensures one gets created 116 | */ 117 | jasmine.getEnv = function() { 118 | var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); 119 | return env; 120 | }; 121 | 122 | /** 123 | * @ignore 124 | * @private 125 | * @param value 126 | * @returns {Boolean} 127 | */ 128 | jasmine.isArray_ = function(value) { 129 | return jasmine.isA_("Array", value); 130 | }; 131 | 132 | /** 133 | * @ignore 134 | * @private 135 | * @param value 136 | * @returns {Boolean} 137 | */ 138 | jasmine.isString_ = function(value) { 139 | return jasmine.isA_("String", value); 140 | }; 141 | 142 | /** 143 | * @ignore 144 | * @private 145 | * @param value 146 | * @returns {Boolean} 147 | */ 148 | jasmine.isNumber_ = function(value) { 149 | return jasmine.isA_("Number", value); 150 | }; 151 | 152 | /** 153 | * @ignore 154 | * @private 155 | * @param {String} typeName 156 | * @param value 157 | * @returns {Boolean} 158 | */ 159 | jasmine.isA_ = function(typeName, value) { 160 | return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; 161 | }; 162 | 163 | /** 164 | * Pretty printer for expecations. Takes any object and turns it into a human-readable string. 165 | * 166 | * @param value {Object} an object to be outputted 167 | * @returns {String} 168 | */ 169 | jasmine.pp = function(value) { 170 | var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); 171 | stringPrettyPrinter.format(value); 172 | return stringPrettyPrinter.string; 173 | }; 174 | 175 | /** 176 | * Returns true if the object is a DOM Node. 177 | * 178 | * @param {Object} obj object to check 179 | * @returns {Boolean} 180 | */ 181 | jasmine.isDomNode = function(obj) { 182 | return obj.nodeType > 0; 183 | }; 184 | 185 | /** 186 | * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. 187 | * 188 | * @example 189 | * // don't care about which function is passed in, as long as it's a function 190 | * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function)); 191 | * 192 | * @param {Class} clazz 193 | * @returns matchable object of the type clazz 194 | */ 195 | jasmine.any = function(clazz) { 196 | return new jasmine.Matchers.Any(clazz); 197 | }; 198 | 199 | /** 200 | * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. 201 | * 202 | * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine 203 | * expectation syntax. Spies can be checked if they were called or not and what the calling params were. 204 | * 205 | * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs). 206 | * 207 | * Spies are torn down at the end of every spec. 208 | * 209 | * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. 210 | * 211 | * @example 212 | * // a stub 213 | * var myStub = jasmine.createSpy('myStub'); // can be used anywhere 214 | * 215 | * // spy example 216 | * var foo = { 217 | * not: function(bool) { return !bool; } 218 | * } 219 | * 220 | * // actual foo.not will not be called, execution stops 221 | * spyOn(foo, 'not'); 222 | 223 | // foo.not spied upon, execution will continue to implementation 224 | * spyOn(foo, 'not').andCallThrough(); 225 | * 226 | * // fake example 227 | * var foo = { 228 | * not: function(bool) { return !bool; } 229 | * } 230 | * 231 | * // foo.not(val) will return val 232 | * spyOn(foo, 'not').andCallFake(function(value) {return value;}); 233 | * 234 | * // mock example 235 | * foo.not(7 == 7); 236 | * expect(foo.not).toHaveBeenCalled(); 237 | * expect(foo.not).toHaveBeenCalledWith(true); 238 | * 239 | * @constructor 240 | * @see spyOn, jasmine.createSpy, jasmine.createSpyObj 241 | * @param {String} name 242 | */ 243 | jasmine.Spy = function(name) { 244 | /** 245 | * The name of the spy, if provided. 246 | */ 247 | this.identity = name || 'unknown'; 248 | /** 249 | * Is this Object a spy? 250 | */ 251 | this.isSpy = true; 252 | /** 253 | * The actual function this spy stubs. 254 | */ 255 | this.plan = function() { 256 | }; 257 | /** 258 | * Tracking of the most recent call to the spy. 259 | * @example 260 | * var mySpy = jasmine.createSpy('foo'); 261 | * mySpy(1, 2); 262 | * mySpy.mostRecentCall.args = [1, 2]; 263 | */ 264 | this.mostRecentCall = {}; 265 | 266 | /** 267 | * Holds arguments for each call to the spy, indexed by call count 268 | * @example 269 | * var mySpy = jasmine.createSpy('foo'); 270 | * mySpy(1, 2); 271 | * mySpy(7, 8); 272 | * mySpy.mostRecentCall.args = [7, 8]; 273 | * mySpy.argsForCall[0] = [1, 2]; 274 | * mySpy.argsForCall[1] = [7, 8]; 275 | */ 276 | this.argsForCall = []; 277 | this.calls = []; 278 | }; 279 | 280 | /** 281 | * Tells a spy to call through to the actual implemenatation. 282 | * 283 | * @example 284 | * var foo = { 285 | * bar: function() { // do some stuff } 286 | * } 287 | * 288 | * // defining a spy on an existing property: foo.bar 289 | * spyOn(foo, 'bar').andCallThrough(); 290 | */ 291 | jasmine.Spy.prototype.andCallThrough = function() { 292 | this.plan = this.originalValue; 293 | return this; 294 | }; 295 | 296 | /** 297 | * For setting the return value of a spy. 298 | * 299 | * @example 300 | * // defining a spy from scratch: foo() returns 'baz' 301 | * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); 302 | * 303 | * // defining a spy on an existing property: foo.bar() returns 'baz' 304 | * spyOn(foo, 'bar').andReturn('baz'); 305 | * 306 | * @param {Object} value 307 | */ 308 | jasmine.Spy.prototype.andReturn = function(value) { 309 | this.plan = function() { 310 | return value; 311 | }; 312 | return this; 313 | }; 314 | 315 | /** 316 | * For throwing an exception when a spy is called. 317 | * 318 | * @example 319 | * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' 320 | * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); 321 | * 322 | * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' 323 | * spyOn(foo, 'bar').andThrow('baz'); 324 | * 325 | * @param {String} exceptionMsg 326 | */ 327 | jasmine.Spy.prototype.andThrow = function(exceptionMsg) { 328 | this.plan = function() { 329 | throw exceptionMsg; 330 | }; 331 | return this; 332 | }; 333 | 334 | /** 335 | * Calls an alternate implementation when a spy is called. 336 | * 337 | * @example 338 | * var baz = function() { 339 | * // do some stuff, return something 340 | * } 341 | * // defining a spy from scratch: foo() calls the function baz 342 | * var foo = jasmine.createSpy('spy on foo').andCall(baz); 343 | * 344 | * // defining a spy on an existing property: foo.bar() calls an anonymnous function 345 | * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); 346 | * 347 | * @param {Function} fakeFunc 348 | */ 349 | jasmine.Spy.prototype.andCallFake = function(fakeFunc) { 350 | this.plan = fakeFunc; 351 | return this; 352 | }; 353 | 354 | /** 355 | * Resets all of a spy's the tracking variables so that it can be used again. 356 | * 357 | * @example 358 | * spyOn(foo, 'bar'); 359 | * 360 | * foo.bar(); 361 | * 362 | * expect(foo.bar.callCount).toEqual(1); 363 | * 364 | * foo.bar.reset(); 365 | * 366 | * expect(foo.bar.callCount).toEqual(0); 367 | */ 368 | jasmine.Spy.prototype.reset = function() { 369 | this.wasCalled = false; 370 | this.callCount = 0; 371 | this.argsForCall = []; 372 | this.calls = []; 373 | this.mostRecentCall = {}; 374 | }; 375 | 376 | jasmine.createSpy = function(name) { 377 | 378 | var spyObj = function() { 379 | spyObj.wasCalled = true; 380 | spyObj.callCount++; 381 | var args = jasmine.util.argsToArray(arguments); 382 | spyObj.mostRecentCall.object = this; 383 | spyObj.mostRecentCall.args = args; 384 | spyObj.argsForCall.push(args); 385 | spyObj.calls.push({object: this, args: args}); 386 | return spyObj.plan.apply(this, arguments); 387 | }; 388 | 389 | var spy = new jasmine.Spy(name); 390 | 391 | for (var prop in spy) { 392 | spyObj[prop] = spy[prop]; 393 | } 394 | 395 | spyObj.reset(); 396 | 397 | return spyObj; 398 | }; 399 | 400 | /** 401 | * Determines whether an object is a spy. 402 | * 403 | * @param {jasmine.Spy|Object} putativeSpy 404 | * @returns {Boolean} 405 | */ 406 | jasmine.isSpy = function(putativeSpy) { 407 | return putativeSpy && putativeSpy.isSpy; 408 | }; 409 | 410 | /** 411 | * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something 412 | * large in one call. 413 | * 414 | * @param {String} baseName name of spy class 415 | * @param {Array} methodNames array of names of methods to make spies 416 | */ 417 | jasmine.createSpyObj = function(baseName, methodNames) { 418 | if (!jasmine.isArray_(methodNames) || methodNames.length === 0) { 419 | throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); 420 | } 421 | var obj = {}; 422 | for (var i = 0; i < methodNames.length; i++) { 423 | obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); 424 | } 425 | return obj; 426 | }; 427 | 428 | /** 429 | * All parameters are pretty-printed and concatenated together, then written to the current spec's output. 430 | * 431 | * Be careful not to leave calls to jasmine.log in production code. 432 | */ 433 | jasmine.log = function() { 434 | var spec = jasmine.getEnv().currentSpec; 435 | spec.log.apply(spec, arguments); 436 | }; 437 | 438 | /** 439 | * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. 440 | * 441 | * @example 442 | * // spy example 443 | * var foo = { 444 | * not: function(bool) { return !bool; } 445 | * } 446 | * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops 447 | * 448 | * @see jasmine.createSpy 449 | * @param obj 450 | * @param methodName 451 | * @returns a Jasmine spy that can be chained with all spy methods 452 | */ 453 | var spyOn = function(obj, methodName) { 454 | return jasmine.getEnv().currentSpec.spyOn(obj, methodName); 455 | }; 456 | if (isCommonJS) exports.spyOn = spyOn; 457 | 458 | /** 459 | * Creates a Jasmine spec that will be added to the current suite. 460 | * 461 | * // TODO: pending tests 462 | * 463 | * @example 464 | * it('should be true', function() { 465 | * expect(true).toEqual(true); 466 | * }); 467 | * 468 | * @param {String} desc description of this specification 469 | * @param {Function} func defines the preconditions and expectations of the spec 470 | */ 471 | var it = function(desc, func) { 472 | return jasmine.getEnv().it(desc, func); 473 | }; 474 | if (isCommonJS) exports.it = it; 475 | 476 | /** 477 | * Creates a disabled Jasmine spec. 478 | * 479 | * A convenience method that allows existing specs to be disabled temporarily during development. 480 | * 481 | * @param {String} desc description of this specification 482 | * @param {Function} func defines the preconditions and expectations of the spec 483 | */ 484 | var xit = function(desc, func) { 485 | return jasmine.getEnv().xit(desc, func); 486 | }; 487 | if (isCommonJS) exports.xit = xit; 488 | 489 | /** 490 | * Starts a chain for a Jasmine expectation. 491 | * 492 | * It is passed an Object that is the actual value and should chain to one of the many 493 | * jasmine.Matchers functions. 494 | * 495 | * @param {Object} actual Actual value to test against and expected value 496 | */ 497 | var expect = function(actual) { 498 | return jasmine.getEnv().currentSpec.expect(actual); 499 | }; 500 | if (isCommonJS) exports.expect = expect; 501 | 502 | /** 503 | * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. 504 | * 505 | * @param {Function} func Function that defines part of a jasmine spec. 506 | */ 507 | var runs = function(func) { 508 | jasmine.getEnv().currentSpec.runs(func); 509 | }; 510 | if (isCommonJS) exports.runs = runs; 511 | 512 | /** 513 | * Waits a fixed time period before moving to the next block. 514 | * 515 | * @deprecated Use waitsFor() instead 516 | * @param {Number} timeout milliseconds to wait 517 | */ 518 | var waits = function(timeout) { 519 | jasmine.getEnv().currentSpec.waits(timeout); 520 | }; 521 | if (isCommonJS) exports.waits = waits; 522 | 523 | /** 524 | * Waits for the latchFunction to return true before proceeding to the next block. 525 | * 526 | * @param {Function} latchFunction 527 | * @param {String} optional_timeoutMessage 528 | * @param {Number} optional_timeout 529 | */ 530 | var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { 531 | jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); 532 | }; 533 | if (isCommonJS) exports.waitsFor = waitsFor; 534 | 535 | /** 536 | * A function that is called before each spec in a suite. 537 | * 538 | * Used for spec setup, including validating assumptions. 539 | * 540 | * @param {Function} beforeEachFunction 541 | */ 542 | var beforeEach = function(beforeEachFunction) { 543 | jasmine.getEnv().beforeEach(beforeEachFunction); 544 | }; 545 | if (isCommonJS) exports.beforeEach = beforeEach; 546 | 547 | /** 548 | * A function that is called after each spec in a suite. 549 | * 550 | * Used for restoring any state that is hijacked during spec execution. 551 | * 552 | * @param {Function} afterEachFunction 553 | */ 554 | var afterEach = function(afterEachFunction) { 555 | jasmine.getEnv().afterEach(afterEachFunction); 556 | }; 557 | if (isCommonJS) exports.afterEach = afterEach; 558 | 559 | /** 560 | * Defines a suite of specifications. 561 | * 562 | * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared 563 | * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization 564 | * of setup in some tests. 565 | * 566 | * @example 567 | * // TODO: a simple suite 568 | * 569 | * // TODO: a simple suite with a nested describe block 570 | * 571 | * @param {String} description A string, usually the class under test. 572 | * @param {Function} specDefinitions function that defines several specs. 573 | */ 574 | var describe = function(description, specDefinitions) { 575 | return jasmine.getEnv().describe(description, specDefinitions); 576 | }; 577 | if (isCommonJS) exports.describe = describe; 578 | 579 | /** 580 | * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. 581 | * 582 | * @param {String} description A string, usually the class under test. 583 | * @param {Function} specDefinitions function that defines several specs. 584 | */ 585 | var xdescribe = function(description, specDefinitions) { 586 | return jasmine.getEnv().xdescribe(description, specDefinitions); 587 | }; 588 | if (isCommonJS) exports.xdescribe = xdescribe; 589 | 590 | 591 | // Provide the XMLHttpRequest class for IE 5.x-6.x: 592 | jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { 593 | function tryIt(f) { 594 | try { 595 | return f(); 596 | } catch(e) { 597 | } 598 | return null; 599 | } 600 | 601 | var xhr = tryIt(function() { 602 | return new ActiveXObject("Msxml2.XMLHTTP.6.0"); 603 | }) || 604 | tryIt(function() { 605 | return new ActiveXObject("Msxml2.XMLHTTP.3.0"); 606 | }) || 607 | tryIt(function() { 608 | return new ActiveXObject("Msxml2.XMLHTTP"); 609 | }) || 610 | tryIt(function() { 611 | return new ActiveXObject("Microsoft.XMLHTTP"); 612 | }); 613 | 614 | if (!xhr) throw new Error("This browser does not support XMLHttpRequest."); 615 | 616 | return xhr; 617 | } : XMLHttpRequest; 618 | /** 619 | * @namespace 620 | */ 621 | jasmine.util = {}; 622 | 623 | /** 624 | * Declare that a child class inherit it's prototype from the parent class. 625 | * 626 | * @private 627 | * @param {Function} childClass 628 | * @param {Function} parentClass 629 | */ 630 | jasmine.util.inherit = function(childClass, parentClass) { 631 | /** 632 | * @private 633 | */ 634 | var subclass = function() { 635 | }; 636 | subclass.prototype = parentClass.prototype; 637 | childClass.prototype = new subclass(); 638 | }; 639 | 640 | jasmine.util.formatException = function(e) { 641 | var lineNumber; 642 | if (e.line) { 643 | lineNumber = e.line; 644 | } 645 | else if (e.lineNumber) { 646 | lineNumber = e.lineNumber; 647 | } 648 | 649 | var file; 650 | 651 | if (e.sourceURL) { 652 | file = e.sourceURL; 653 | } 654 | else if (e.fileName) { 655 | file = e.fileName; 656 | } 657 | 658 | var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); 659 | 660 | if (file && lineNumber) { 661 | message += ' in ' + file + ' (line ' + lineNumber + ')'; 662 | } 663 | 664 | return message; 665 | }; 666 | 667 | jasmine.util.htmlEscape = function(str) { 668 | if (!str) return str; 669 | return str.replace(/&/g, '&') 670 | .replace(//g, '>'); 672 | }; 673 | 674 | jasmine.util.argsToArray = function(args) { 675 | var arrayOfArgs = []; 676 | for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); 677 | return arrayOfArgs; 678 | }; 679 | 680 | jasmine.util.extend = function(destination, source) { 681 | for (var property in source) destination[property] = source[property]; 682 | return destination; 683 | }; 684 | 685 | /** 686 | * Environment for Jasmine 687 | * 688 | * @constructor 689 | */ 690 | jasmine.Env = function() { 691 | this.currentSpec = null; 692 | this.currentSuite = null; 693 | this.currentRunner_ = new jasmine.Runner(this); 694 | 695 | this.reporter = new jasmine.MultiReporter(); 696 | 697 | this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; 698 | this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; 699 | this.lastUpdate = 0; 700 | this.specFilter = function() { 701 | return true; 702 | }; 703 | 704 | this.nextSpecId_ = 0; 705 | this.nextSuiteId_ = 0; 706 | this.equalityTesters_ = []; 707 | 708 | // wrap matchers 709 | this.matchersClass = function() { 710 | jasmine.Matchers.apply(this, arguments); 711 | }; 712 | jasmine.util.inherit(this.matchersClass, jasmine.Matchers); 713 | 714 | jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); 715 | }; 716 | 717 | 718 | jasmine.Env.prototype.setTimeout = jasmine.setTimeout; 719 | jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; 720 | jasmine.Env.prototype.setInterval = jasmine.setInterval; 721 | jasmine.Env.prototype.clearInterval = jasmine.clearInterval; 722 | 723 | /** 724 | * @returns an object containing jasmine version build info, if set. 725 | */ 726 | jasmine.Env.prototype.version = function () { 727 | if (jasmine.version_) { 728 | return jasmine.version_; 729 | } else { 730 | throw new Error('Version not set'); 731 | } 732 | }; 733 | 734 | /** 735 | * @returns string containing jasmine version build info, if set. 736 | */ 737 | jasmine.Env.prototype.versionString = function() { 738 | if (!jasmine.version_) { 739 | return "version unknown"; 740 | } 741 | 742 | var version = this.version(); 743 | var versionString = version.major + "." + version.minor + "." + version.build; 744 | if (version.release_candidate) { 745 | versionString += ".rc" + version.release_candidate; 746 | } 747 | versionString += " revision " + version.revision; 748 | return versionString; 749 | }; 750 | 751 | /** 752 | * @returns a sequential integer starting at 0 753 | */ 754 | jasmine.Env.prototype.nextSpecId = function () { 755 | return this.nextSpecId_++; 756 | }; 757 | 758 | /** 759 | * @returns a sequential integer starting at 0 760 | */ 761 | jasmine.Env.prototype.nextSuiteId = function () { 762 | return this.nextSuiteId_++; 763 | }; 764 | 765 | /** 766 | * Register a reporter to receive status updates from Jasmine. 767 | * @param {jasmine.Reporter} reporter An object which will receive status updates. 768 | */ 769 | jasmine.Env.prototype.addReporter = function(reporter) { 770 | this.reporter.addReporter(reporter); 771 | }; 772 | 773 | jasmine.Env.prototype.execute = function() { 774 | this.currentRunner_.execute(); 775 | }; 776 | 777 | jasmine.Env.prototype.describe = function(description, specDefinitions) { 778 | var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); 779 | 780 | var parentSuite = this.currentSuite; 781 | if (parentSuite) { 782 | parentSuite.add(suite); 783 | } else { 784 | this.currentRunner_.add(suite); 785 | } 786 | 787 | this.currentSuite = suite; 788 | 789 | var declarationError = null; 790 | try { 791 | specDefinitions.call(suite); 792 | } catch(e) { 793 | declarationError = e; 794 | } 795 | 796 | if (declarationError) { 797 | this.it("encountered a declaration exception", function() { 798 | throw declarationError; 799 | }); 800 | } 801 | 802 | this.currentSuite = parentSuite; 803 | 804 | return suite; 805 | }; 806 | 807 | jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { 808 | if (this.currentSuite) { 809 | this.currentSuite.beforeEach(beforeEachFunction); 810 | } else { 811 | this.currentRunner_.beforeEach(beforeEachFunction); 812 | } 813 | }; 814 | 815 | jasmine.Env.prototype.currentRunner = function () { 816 | return this.currentRunner_; 817 | }; 818 | 819 | jasmine.Env.prototype.afterEach = function(afterEachFunction) { 820 | if (this.currentSuite) { 821 | this.currentSuite.afterEach(afterEachFunction); 822 | } else { 823 | this.currentRunner_.afterEach(afterEachFunction); 824 | } 825 | 826 | }; 827 | 828 | jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { 829 | return { 830 | execute: function() { 831 | } 832 | }; 833 | }; 834 | 835 | jasmine.Env.prototype.it = function(description, func) { 836 | var spec = new jasmine.Spec(this, this.currentSuite, description); 837 | this.currentSuite.add(spec); 838 | this.currentSpec = spec; 839 | 840 | if (func) { 841 | spec.runs(func); 842 | } 843 | 844 | return spec; 845 | }; 846 | 847 | jasmine.Env.prototype.xit = function(desc, func) { 848 | return { 849 | id: this.nextSpecId(), 850 | runs: function() { 851 | } 852 | }; 853 | }; 854 | 855 | jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { 856 | if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { 857 | return true; 858 | } 859 | 860 | a.__Jasmine_been_here_before__ = b; 861 | b.__Jasmine_been_here_before__ = a; 862 | 863 | var hasKey = function(obj, keyName) { 864 | return obj !== null && obj[keyName] !== jasmine.undefined; 865 | }; 866 | 867 | for (var property in b) { 868 | if (!hasKey(a, property) && hasKey(b, property)) { 869 | mismatchKeys.push("expected has key '" + property + "', but missing from actual."); 870 | } 871 | } 872 | for (property in a) { 873 | if (!hasKey(b, property) && hasKey(a, property)) { 874 | mismatchKeys.push("expected missing key '" + property + "', but present in actual."); 875 | } 876 | } 877 | for (property in b) { 878 | if (property == '__Jasmine_been_here_before__') continue; 879 | if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { 880 | mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); 881 | } 882 | } 883 | 884 | if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { 885 | mismatchValues.push("arrays were not the same length"); 886 | } 887 | 888 | delete a.__Jasmine_been_here_before__; 889 | delete b.__Jasmine_been_here_before__; 890 | return (mismatchKeys.length === 0 && mismatchValues.length === 0); 891 | }; 892 | 893 | jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { 894 | mismatchKeys = mismatchKeys || []; 895 | mismatchValues = mismatchValues || []; 896 | 897 | for (var i = 0; i < this.equalityTesters_.length; i++) { 898 | var equalityTester = this.equalityTesters_[i]; 899 | var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); 900 | if (result !== jasmine.undefined) return result; 901 | } 902 | 903 | if (a === b) return true; 904 | 905 | if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { 906 | return (a == jasmine.undefined && b == jasmine.undefined); 907 | } 908 | 909 | if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { 910 | return a === b; 911 | } 912 | 913 | if (a instanceof Date && b instanceof Date) { 914 | return a.getTime() == b.getTime(); 915 | } 916 | 917 | if (a instanceof jasmine.Matchers.Any) { 918 | return a.matches(b); 919 | } 920 | 921 | if (b instanceof jasmine.Matchers.Any) { 922 | return b.matches(a); 923 | } 924 | 925 | if (jasmine.isString_(a) && jasmine.isString_(b)) { 926 | return (a == b); 927 | } 928 | 929 | if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { 930 | return (a == b); 931 | } 932 | 933 | if (typeof a === "object" && typeof b === "object") { 934 | return this.compareObjects_(a, b, mismatchKeys, mismatchValues); 935 | } 936 | 937 | //Straight check 938 | return (a === b); 939 | }; 940 | 941 | jasmine.Env.prototype.contains_ = function(haystack, needle) { 942 | if (jasmine.isArray_(haystack)) { 943 | for (var i = 0; i < haystack.length; i++) { 944 | if (this.equals_(haystack[i], needle)) return true; 945 | } 946 | return false; 947 | } 948 | return haystack.indexOf(needle) >= 0; 949 | }; 950 | 951 | jasmine.Env.prototype.addEqualityTester = function(equalityTester) { 952 | this.equalityTesters_.push(equalityTester); 953 | }; 954 | /** No-op base class for Jasmine reporters. 955 | * 956 | * @constructor 957 | */ 958 | jasmine.Reporter = function() { 959 | }; 960 | 961 | //noinspection JSUnusedLocalSymbols 962 | jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { 963 | }; 964 | 965 | //noinspection JSUnusedLocalSymbols 966 | jasmine.Reporter.prototype.reportRunnerResults = function(runner) { 967 | }; 968 | 969 | //noinspection JSUnusedLocalSymbols 970 | jasmine.Reporter.prototype.reportSuiteResults = function(suite) { 971 | }; 972 | 973 | //noinspection JSUnusedLocalSymbols 974 | jasmine.Reporter.prototype.reportSpecStarting = function(spec) { 975 | }; 976 | 977 | //noinspection JSUnusedLocalSymbols 978 | jasmine.Reporter.prototype.reportSpecResults = function(spec) { 979 | }; 980 | 981 | //noinspection JSUnusedLocalSymbols 982 | jasmine.Reporter.prototype.log = function(str) { 983 | }; 984 | 985 | /** 986 | * Blocks are functions with executable code that make up a spec. 987 | * 988 | * @constructor 989 | * @param {jasmine.Env} env 990 | * @param {Function} func 991 | * @param {jasmine.Spec} spec 992 | */ 993 | jasmine.Block = function(env, func, spec) { 994 | this.env = env; 995 | this.func = func; 996 | this.spec = spec; 997 | }; 998 | 999 | jasmine.Block.prototype.execute = function(onComplete) { 1000 | try { 1001 | this.func.apply(this.spec); 1002 | } catch (e) { 1003 | this.spec.fail(e); 1004 | } 1005 | onComplete(); 1006 | }; 1007 | /** JavaScript API reporter. 1008 | * 1009 | * @constructor 1010 | */ 1011 | jasmine.JsApiReporter = function() { 1012 | this.started = false; 1013 | this.finished = false; 1014 | this.suites_ = []; 1015 | this.results_ = {}; 1016 | }; 1017 | 1018 | jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { 1019 | this.started = true; 1020 | var suites = runner.topLevelSuites(); 1021 | for (var i = 0; i < suites.length; i++) { 1022 | var suite = suites[i]; 1023 | this.suites_.push(this.summarize_(suite)); 1024 | } 1025 | }; 1026 | 1027 | jasmine.JsApiReporter.prototype.suites = function() { 1028 | return this.suites_; 1029 | }; 1030 | 1031 | jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { 1032 | var isSuite = suiteOrSpec instanceof jasmine.Suite; 1033 | var summary = { 1034 | id: suiteOrSpec.id, 1035 | name: suiteOrSpec.description, 1036 | type: isSuite ? 'suite' : 'spec', 1037 | children: [] 1038 | }; 1039 | 1040 | if (isSuite) { 1041 | var children = suiteOrSpec.children(); 1042 | for (var i = 0; i < children.length; i++) { 1043 | summary.children.push(this.summarize_(children[i])); 1044 | } 1045 | } 1046 | return summary; 1047 | }; 1048 | 1049 | jasmine.JsApiReporter.prototype.results = function() { 1050 | return this.results_; 1051 | }; 1052 | 1053 | jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { 1054 | return this.results_[specId]; 1055 | }; 1056 | 1057 | //noinspection JSUnusedLocalSymbols 1058 | jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { 1059 | this.finished = true; 1060 | }; 1061 | 1062 | //noinspection JSUnusedLocalSymbols 1063 | jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { 1064 | }; 1065 | 1066 | //noinspection JSUnusedLocalSymbols 1067 | jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { 1068 | this.results_[spec.id] = { 1069 | messages: spec.results().getItems(), 1070 | result: spec.results().failedCount > 0 ? "failed" : "passed" 1071 | }; 1072 | }; 1073 | 1074 | //noinspection JSUnusedLocalSymbols 1075 | jasmine.JsApiReporter.prototype.log = function(str) { 1076 | }; 1077 | 1078 | jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ 1079 | var results = {}; 1080 | for (var i = 0; i < specIds.length; i++) { 1081 | var specId = specIds[i]; 1082 | results[specId] = this.summarizeResult_(this.results_[specId]); 1083 | } 1084 | return results; 1085 | }; 1086 | 1087 | jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ 1088 | var summaryMessages = []; 1089 | var messagesLength = result.messages.length; 1090 | for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { 1091 | var resultMessage = result.messages[messageIndex]; 1092 | summaryMessages.push({ 1093 | text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, 1094 | passed: resultMessage.passed ? resultMessage.passed() : true, 1095 | type: resultMessage.type, 1096 | message: resultMessage.message, 1097 | trace: { 1098 | stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined 1099 | } 1100 | }); 1101 | } 1102 | 1103 | return { 1104 | result : result.result, 1105 | messages : summaryMessages 1106 | }; 1107 | }; 1108 | 1109 | /** 1110 | * @constructor 1111 | * @param {jasmine.Env} env 1112 | * @param actual 1113 | * @param {jasmine.Spec} spec 1114 | */ 1115 | jasmine.Matchers = function(env, actual, spec, opt_isNot) { 1116 | this.env = env; 1117 | this.actual = actual; 1118 | this.spec = spec; 1119 | this.isNot = opt_isNot || false; 1120 | this.reportWasCalled_ = false; 1121 | }; 1122 | 1123 | // todo: @deprecated as of Jasmine 0.11, remove soon [xw] 1124 | jasmine.Matchers.pp = function(str) { 1125 | throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); 1126 | }; 1127 | 1128 | // todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] 1129 | jasmine.Matchers.prototype.report = function(result, failing_message, details) { 1130 | throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); 1131 | }; 1132 | 1133 | jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { 1134 | for (var methodName in prototype) { 1135 | if (methodName == 'report') continue; 1136 | var orig = prototype[methodName]; 1137 | matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); 1138 | } 1139 | }; 1140 | 1141 | jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { 1142 | return function() { 1143 | var matcherArgs = jasmine.util.argsToArray(arguments); 1144 | var result = matcherFunction.apply(this, arguments); 1145 | 1146 | if (this.isNot) { 1147 | result = !result; 1148 | } 1149 | 1150 | if (this.reportWasCalled_) return result; 1151 | 1152 | var message; 1153 | if (!result) { 1154 | if (this.message) { 1155 | message = this.message.apply(this, arguments); 1156 | if (jasmine.isArray_(message)) { 1157 | message = message[this.isNot ? 1 : 0]; 1158 | } 1159 | } else { 1160 | var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); 1161 | message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; 1162 | if (matcherArgs.length > 0) { 1163 | for (var i = 0; i < matcherArgs.length; i++) { 1164 | if (i > 0) message += ","; 1165 | message += " " + jasmine.pp(matcherArgs[i]); 1166 | } 1167 | } 1168 | message += "."; 1169 | } 1170 | } 1171 | var expectationResult = new jasmine.ExpectationResult({ 1172 | matcherName: matcherName, 1173 | passed: result, 1174 | expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], 1175 | actual: this.actual, 1176 | message: message 1177 | }); 1178 | this.spec.addMatcherResult(expectationResult); 1179 | return jasmine.undefined; 1180 | }; 1181 | }; 1182 | 1183 | 1184 | 1185 | 1186 | /** 1187 | * toBe: compares the actual to the expected using === 1188 | * @param expected 1189 | */ 1190 | jasmine.Matchers.prototype.toBe = function(expected) { 1191 | return this.actual === expected; 1192 | }; 1193 | 1194 | /** 1195 | * toNotBe: compares the actual to the expected using !== 1196 | * @param expected 1197 | * @deprecated as of 1.0. Use not.toBe() instead. 1198 | */ 1199 | jasmine.Matchers.prototype.toNotBe = function(expected) { 1200 | return this.actual !== expected; 1201 | }; 1202 | 1203 | /** 1204 | * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. 1205 | * 1206 | * @param expected 1207 | */ 1208 | jasmine.Matchers.prototype.toEqual = function(expected) { 1209 | return this.env.equals_(this.actual, expected); 1210 | }; 1211 | 1212 | /** 1213 | * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual 1214 | * @param expected 1215 | * @deprecated as of 1.0. Use not.toNotEqual() instead. 1216 | */ 1217 | jasmine.Matchers.prototype.toNotEqual = function(expected) { 1218 | return !this.env.equals_(this.actual, expected); 1219 | }; 1220 | 1221 | /** 1222 | * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes 1223 | * a pattern or a String. 1224 | * 1225 | * @param expected 1226 | */ 1227 | jasmine.Matchers.prototype.toMatch = function(expected) { 1228 | return new RegExp(expected).test(this.actual); 1229 | }; 1230 | 1231 | /** 1232 | * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch 1233 | * @param expected 1234 | * @deprecated as of 1.0. Use not.toMatch() instead. 1235 | */ 1236 | jasmine.Matchers.prototype.toNotMatch = function(expected) { 1237 | return !(new RegExp(expected).test(this.actual)); 1238 | }; 1239 | 1240 | /** 1241 | * Matcher that compares the actual to jasmine.undefined. 1242 | */ 1243 | jasmine.Matchers.prototype.toBeDefined = function() { 1244 | return (this.actual !== jasmine.undefined); 1245 | }; 1246 | 1247 | /** 1248 | * Matcher that compares the actual to jasmine.undefined. 1249 | */ 1250 | jasmine.Matchers.prototype.toBeUndefined = function() { 1251 | return (this.actual === jasmine.undefined); 1252 | }; 1253 | 1254 | /** 1255 | * Matcher that compares the actual to null. 1256 | */ 1257 | jasmine.Matchers.prototype.toBeNull = function() { 1258 | return (this.actual === null); 1259 | }; 1260 | 1261 | /** 1262 | * Matcher that boolean not-nots the actual. 1263 | */ 1264 | jasmine.Matchers.prototype.toBeTruthy = function() { 1265 | return !!this.actual; 1266 | }; 1267 | 1268 | 1269 | /** 1270 | * Matcher that boolean nots the actual. 1271 | */ 1272 | jasmine.Matchers.prototype.toBeFalsy = function() { 1273 | return !this.actual; 1274 | }; 1275 | 1276 | 1277 | /** 1278 | * Matcher that checks to see if the actual, a Jasmine spy, was called. 1279 | */ 1280 | jasmine.Matchers.prototype.toHaveBeenCalled = function() { 1281 | if (arguments.length > 0) { 1282 | throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); 1283 | } 1284 | 1285 | if (!jasmine.isSpy(this.actual)) { 1286 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 1287 | } 1288 | 1289 | this.message = function() { 1290 | return [ 1291 | "Expected spy " + this.actual.identity + " to have been called.", 1292 | "Expected spy " + this.actual.identity + " not to have been called." 1293 | ]; 1294 | }; 1295 | 1296 | return this.actual.wasCalled; 1297 | }; 1298 | 1299 | /** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ 1300 | jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; 1301 | 1302 | /** 1303 | * Matcher that checks to see if the actual, a Jasmine spy, was not called. 1304 | * 1305 | * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead 1306 | */ 1307 | jasmine.Matchers.prototype.wasNotCalled = function() { 1308 | if (arguments.length > 0) { 1309 | throw new Error('wasNotCalled does not take arguments'); 1310 | } 1311 | 1312 | if (!jasmine.isSpy(this.actual)) { 1313 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 1314 | } 1315 | 1316 | this.message = function() { 1317 | return [ 1318 | "Expected spy " + this.actual.identity + " to not have been called.", 1319 | "Expected spy " + this.actual.identity + " to have been called." 1320 | ]; 1321 | }; 1322 | 1323 | return !this.actual.wasCalled; 1324 | }; 1325 | 1326 | /** 1327 | * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. 1328 | * 1329 | * @example 1330 | * 1331 | */ 1332 | jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { 1333 | var expectedArgs = jasmine.util.argsToArray(arguments); 1334 | if (!jasmine.isSpy(this.actual)) { 1335 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 1336 | } 1337 | this.message = function() { 1338 | if (this.actual.callCount === 0) { 1339 | // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw] 1340 | return [ 1341 | "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.", 1342 | "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was." 1343 | ]; 1344 | } else { 1345 | return [ 1346 | "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall), 1347 | "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall) 1348 | ]; 1349 | } 1350 | }; 1351 | 1352 | return this.env.contains_(this.actual.argsForCall, expectedArgs); 1353 | }; 1354 | 1355 | /** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ 1356 | jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; 1357 | 1358 | /** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ 1359 | jasmine.Matchers.prototype.wasNotCalledWith = function() { 1360 | var expectedArgs = jasmine.util.argsToArray(arguments); 1361 | if (!jasmine.isSpy(this.actual)) { 1362 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 1363 | } 1364 | 1365 | this.message = function() { 1366 | return [ 1367 | "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", 1368 | "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" 1369 | ]; 1370 | }; 1371 | 1372 | return !this.env.contains_(this.actual.argsForCall, expectedArgs); 1373 | }; 1374 | 1375 | /** 1376 | * Matcher that checks that the expected item is an element in the actual Array. 1377 | * 1378 | * @param {Object} expected 1379 | */ 1380 | jasmine.Matchers.prototype.toContain = function(expected) { 1381 | return this.env.contains_(this.actual, expected); 1382 | }; 1383 | 1384 | /** 1385 | * Matcher that checks that the expected item is NOT an element in the actual Array. 1386 | * 1387 | * @param {Object} expected 1388 | * @deprecated as of 1.0. Use not.toNotContain() instead. 1389 | */ 1390 | jasmine.Matchers.prototype.toNotContain = function(expected) { 1391 | return !this.env.contains_(this.actual, expected); 1392 | }; 1393 | 1394 | jasmine.Matchers.prototype.toBeLessThan = function(expected) { 1395 | return this.actual < expected; 1396 | }; 1397 | 1398 | jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { 1399 | return this.actual > expected; 1400 | }; 1401 | 1402 | /** 1403 | * Matcher that checks that the expected item is equal to the actual item 1404 | * up to a given level of decimal precision (default 2). 1405 | * 1406 | * @param {Number} expected 1407 | * @param {Number} precision 1408 | */ 1409 | jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) { 1410 | if (!(precision === 0)) { 1411 | precision = precision || 2; 1412 | } 1413 | var multiplier = Math.pow(10, precision); 1414 | var actual = Math.round(this.actual * multiplier); 1415 | expected = Math.round(expected * multiplier); 1416 | return expected == actual; 1417 | }; 1418 | 1419 | /** 1420 | * Matcher that checks that the expected exception was thrown by the actual. 1421 | * 1422 | * @param {String} expected 1423 | */ 1424 | jasmine.Matchers.prototype.toThrow = function(expected) { 1425 | var result = false; 1426 | var exception; 1427 | if (typeof this.actual != 'function') { 1428 | throw new Error('Actual is not a function'); 1429 | } 1430 | try { 1431 | this.actual(); 1432 | } catch (e) { 1433 | exception = e; 1434 | } 1435 | if (exception) { 1436 | result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); 1437 | } 1438 | 1439 | var not = this.isNot ? "not " : ""; 1440 | 1441 | this.message = function() { 1442 | if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { 1443 | return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' '); 1444 | } else { 1445 | return "Expected function to throw an exception."; 1446 | } 1447 | }; 1448 | 1449 | return result; 1450 | }; 1451 | 1452 | jasmine.Matchers.Any = function(expectedClass) { 1453 | this.expectedClass = expectedClass; 1454 | }; 1455 | 1456 | jasmine.Matchers.Any.prototype.matches = function(other) { 1457 | if (this.expectedClass == String) { 1458 | return typeof other == 'string' || other instanceof String; 1459 | } 1460 | 1461 | if (this.expectedClass == Number) { 1462 | return typeof other == 'number' || other instanceof Number; 1463 | } 1464 | 1465 | if (this.expectedClass == Function) { 1466 | return typeof other == 'function' || other instanceof Function; 1467 | } 1468 | 1469 | if (this.expectedClass == Object) { 1470 | return typeof other == 'object'; 1471 | } 1472 | 1473 | return other instanceof this.expectedClass; 1474 | }; 1475 | 1476 | jasmine.Matchers.Any.prototype.toString = function() { 1477 | return ''; 1478 | }; 1479 | 1480 | /** 1481 | * @constructor 1482 | */ 1483 | jasmine.MultiReporter = function() { 1484 | this.subReporters_ = []; 1485 | }; 1486 | jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); 1487 | 1488 | jasmine.MultiReporter.prototype.addReporter = function(reporter) { 1489 | this.subReporters_.push(reporter); 1490 | }; 1491 | 1492 | (function() { 1493 | var functionNames = [ 1494 | "reportRunnerStarting", 1495 | "reportRunnerResults", 1496 | "reportSuiteResults", 1497 | "reportSpecStarting", 1498 | "reportSpecResults", 1499 | "log" 1500 | ]; 1501 | for (var i = 0; i < functionNames.length; i++) { 1502 | var functionName = functionNames[i]; 1503 | jasmine.MultiReporter.prototype[functionName] = (function(functionName) { 1504 | return function() { 1505 | for (var j = 0; j < this.subReporters_.length; j++) { 1506 | var subReporter = this.subReporters_[j]; 1507 | if (subReporter[functionName]) { 1508 | subReporter[functionName].apply(subReporter, arguments); 1509 | } 1510 | } 1511 | }; 1512 | })(functionName); 1513 | } 1514 | })(); 1515 | /** 1516 | * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults 1517 | * 1518 | * @constructor 1519 | */ 1520 | jasmine.NestedResults = function() { 1521 | /** 1522 | * The total count of results 1523 | */ 1524 | this.totalCount = 0; 1525 | /** 1526 | * Number of passed results 1527 | */ 1528 | this.passedCount = 0; 1529 | /** 1530 | * Number of failed results 1531 | */ 1532 | this.failedCount = 0; 1533 | /** 1534 | * Was this suite/spec skipped? 1535 | */ 1536 | this.skipped = false; 1537 | /** 1538 | * @ignore 1539 | */ 1540 | this.items_ = []; 1541 | }; 1542 | 1543 | /** 1544 | * Roll up the result counts. 1545 | * 1546 | * @param result 1547 | */ 1548 | jasmine.NestedResults.prototype.rollupCounts = function(result) { 1549 | this.totalCount += result.totalCount; 1550 | this.passedCount += result.passedCount; 1551 | this.failedCount += result.failedCount; 1552 | }; 1553 | 1554 | /** 1555 | * Adds a log message. 1556 | * @param values Array of message parts which will be concatenated later. 1557 | */ 1558 | jasmine.NestedResults.prototype.log = function(values) { 1559 | this.items_.push(new jasmine.MessageResult(values)); 1560 | }; 1561 | 1562 | /** 1563 | * Getter for the results: message & results. 1564 | */ 1565 | jasmine.NestedResults.prototype.getItems = function() { 1566 | return this.items_; 1567 | }; 1568 | 1569 | /** 1570 | * Adds a result, tracking counts (total, passed, & failed) 1571 | * @param {jasmine.ExpectationResult|jasmine.NestedResults} result 1572 | */ 1573 | jasmine.NestedResults.prototype.addResult = function(result) { 1574 | if (result.type != 'log') { 1575 | if (result.items_) { 1576 | this.rollupCounts(result); 1577 | } else { 1578 | this.totalCount++; 1579 | if (result.passed()) { 1580 | this.passedCount++; 1581 | } else { 1582 | this.failedCount++; 1583 | } 1584 | } 1585 | } 1586 | this.items_.push(result); 1587 | }; 1588 | 1589 | /** 1590 | * @returns {Boolean} True if everything below passed 1591 | */ 1592 | jasmine.NestedResults.prototype.passed = function() { 1593 | return this.passedCount === this.totalCount; 1594 | }; 1595 | /** 1596 | * Base class for pretty printing for expectation results. 1597 | */ 1598 | jasmine.PrettyPrinter = function() { 1599 | this.ppNestLevel_ = 0; 1600 | }; 1601 | 1602 | /** 1603 | * Formats a value in a nice, human-readable string. 1604 | * 1605 | * @param value 1606 | */ 1607 | jasmine.PrettyPrinter.prototype.format = function(value) { 1608 | if (this.ppNestLevel_ > 40) { 1609 | throw new Error('jasmine.PrettyPrinter: format() nested too deeply!'); 1610 | } 1611 | 1612 | this.ppNestLevel_++; 1613 | try { 1614 | if (value === jasmine.undefined) { 1615 | this.emitScalar('undefined'); 1616 | } else if (value === null) { 1617 | this.emitScalar('null'); 1618 | } else if (value === jasmine.getGlobal()) { 1619 | this.emitScalar(''); 1620 | } else if (value instanceof jasmine.Matchers.Any) { 1621 | this.emitScalar(value.toString()); 1622 | } else if (typeof value === 'string') { 1623 | this.emitString(value); 1624 | } else if (jasmine.isSpy(value)) { 1625 | this.emitScalar("spy on " + value.identity); 1626 | } else if (value instanceof RegExp) { 1627 | this.emitScalar(value.toString()); 1628 | } else if (typeof value === 'function') { 1629 | this.emitScalar('Function'); 1630 | } else if (typeof value.nodeType === 'number') { 1631 | this.emitScalar('HTMLNode'); 1632 | } else if (value instanceof Date) { 1633 | this.emitScalar('Date(' + value + ')'); 1634 | } else if (value.__Jasmine_been_here_before__) { 1635 | this.emitScalar(''); 1636 | } else if (jasmine.isArray_(value) || typeof value == 'object') { 1637 | value.__Jasmine_been_here_before__ = true; 1638 | if (jasmine.isArray_(value)) { 1639 | this.emitArray(value); 1640 | } else { 1641 | this.emitObject(value); 1642 | } 1643 | delete value.__Jasmine_been_here_before__; 1644 | } else { 1645 | this.emitScalar(value.toString()); 1646 | } 1647 | } finally { 1648 | this.ppNestLevel_--; 1649 | } 1650 | }; 1651 | 1652 | jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { 1653 | for (var property in obj) { 1654 | if (property == '__Jasmine_been_here_before__') continue; 1655 | fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && 1656 | obj.__lookupGetter__(property) !== null) : false); 1657 | } 1658 | }; 1659 | 1660 | jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; 1661 | jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; 1662 | jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; 1663 | jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; 1664 | 1665 | jasmine.StringPrettyPrinter = function() { 1666 | jasmine.PrettyPrinter.call(this); 1667 | 1668 | this.string = ''; 1669 | }; 1670 | jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); 1671 | 1672 | jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { 1673 | this.append(value); 1674 | }; 1675 | 1676 | jasmine.StringPrettyPrinter.prototype.emitString = function(value) { 1677 | this.append("'" + value + "'"); 1678 | }; 1679 | 1680 | jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { 1681 | this.append('[ '); 1682 | for (var i = 0; i < array.length; i++) { 1683 | if (i > 0) { 1684 | this.append(', '); 1685 | } 1686 | this.format(array[i]); 1687 | } 1688 | this.append(' ]'); 1689 | }; 1690 | 1691 | jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { 1692 | var self = this; 1693 | this.append('{ '); 1694 | var first = true; 1695 | 1696 | this.iterateObject(obj, function(property, isGetter) { 1697 | if (first) { 1698 | first = false; 1699 | } else { 1700 | self.append(', '); 1701 | } 1702 | 1703 | self.append(property); 1704 | self.append(' : '); 1705 | if (isGetter) { 1706 | self.append(''); 1707 | } else { 1708 | self.format(obj[property]); 1709 | } 1710 | }); 1711 | 1712 | this.append(' }'); 1713 | }; 1714 | 1715 | jasmine.StringPrettyPrinter.prototype.append = function(value) { 1716 | this.string += value; 1717 | }; 1718 | jasmine.Queue = function(env) { 1719 | this.env = env; 1720 | this.blocks = []; 1721 | this.running = false; 1722 | this.index = 0; 1723 | this.offset = 0; 1724 | this.abort = false; 1725 | }; 1726 | 1727 | jasmine.Queue.prototype.addBefore = function(block) { 1728 | this.blocks.unshift(block); 1729 | }; 1730 | 1731 | jasmine.Queue.prototype.add = function(block) { 1732 | this.blocks.push(block); 1733 | }; 1734 | 1735 | jasmine.Queue.prototype.insertNext = function(block) { 1736 | this.blocks.splice((this.index + this.offset + 1), 0, block); 1737 | this.offset++; 1738 | }; 1739 | 1740 | jasmine.Queue.prototype.start = function(onComplete) { 1741 | this.running = true; 1742 | this.onComplete = onComplete; 1743 | this.next_(); 1744 | }; 1745 | 1746 | jasmine.Queue.prototype.isRunning = function() { 1747 | return this.running; 1748 | }; 1749 | 1750 | jasmine.Queue.LOOP_DONT_RECURSE = true; 1751 | 1752 | jasmine.Queue.prototype.next_ = function() { 1753 | var self = this; 1754 | var goAgain = true; 1755 | 1756 | while (goAgain) { 1757 | goAgain = false; 1758 | 1759 | if (self.index < self.blocks.length && !this.abort) { 1760 | var calledSynchronously = true; 1761 | var completedSynchronously = false; 1762 | 1763 | var onComplete = function () { 1764 | if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { 1765 | completedSynchronously = true; 1766 | return; 1767 | } 1768 | 1769 | if (self.blocks[self.index].abort) { 1770 | self.abort = true; 1771 | } 1772 | 1773 | self.offset = 0; 1774 | self.index++; 1775 | 1776 | var now = new Date().getTime(); 1777 | if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { 1778 | self.env.lastUpdate = now; 1779 | self.env.setTimeout(function() { 1780 | self.next_(); 1781 | }, 0); 1782 | } else { 1783 | if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { 1784 | goAgain = true; 1785 | } else { 1786 | self.next_(); 1787 | } 1788 | } 1789 | }; 1790 | self.blocks[self.index].execute(onComplete); 1791 | 1792 | calledSynchronously = false; 1793 | if (completedSynchronously) { 1794 | onComplete(); 1795 | } 1796 | 1797 | } else { 1798 | self.running = false; 1799 | if (self.onComplete) { 1800 | self.onComplete(); 1801 | } 1802 | } 1803 | } 1804 | }; 1805 | 1806 | jasmine.Queue.prototype.results = function() { 1807 | var results = new jasmine.NestedResults(); 1808 | for (var i = 0; i < this.blocks.length; i++) { 1809 | if (this.blocks[i].results) { 1810 | results.addResult(this.blocks[i].results()); 1811 | } 1812 | } 1813 | return results; 1814 | }; 1815 | 1816 | 1817 | /** 1818 | * Runner 1819 | * 1820 | * @constructor 1821 | * @param {jasmine.Env} env 1822 | */ 1823 | jasmine.Runner = function(env) { 1824 | var self = this; 1825 | self.env = env; 1826 | self.queue = new jasmine.Queue(env); 1827 | self.before_ = []; 1828 | self.after_ = []; 1829 | self.suites_ = []; 1830 | }; 1831 | 1832 | jasmine.Runner.prototype.execute = function() { 1833 | var self = this; 1834 | if (self.env.reporter.reportRunnerStarting) { 1835 | self.env.reporter.reportRunnerStarting(this); 1836 | } 1837 | self.queue.start(function () { 1838 | self.finishCallback(); 1839 | }); 1840 | }; 1841 | 1842 | jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { 1843 | beforeEachFunction.typeName = 'beforeEach'; 1844 | this.before_.splice(0,0,beforeEachFunction); 1845 | }; 1846 | 1847 | jasmine.Runner.prototype.afterEach = function(afterEachFunction) { 1848 | afterEachFunction.typeName = 'afterEach'; 1849 | this.after_.splice(0,0,afterEachFunction); 1850 | }; 1851 | 1852 | 1853 | jasmine.Runner.prototype.finishCallback = function() { 1854 | this.env.reporter.reportRunnerResults(this); 1855 | }; 1856 | 1857 | jasmine.Runner.prototype.addSuite = function(suite) { 1858 | this.suites_.push(suite); 1859 | }; 1860 | 1861 | jasmine.Runner.prototype.add = function(block) { 1862 | if (block instanceof jasmine.Suite) { 1863 | this.addSuite(block); 1864 | } 1865 | this.queue.add(block); 1866 | }; 1867 | 1868 | jasmine.Runner.prototype.specs = function () { 1869 | var suites = this.suites(); 1870 | var specs = []; 1871 | for (var i = 0; i < suites.length; i++) { 1872 | specs = specs.concat(suites[i].specs()); 1873 | } 1874 | return specs; 1875 | }; 1876 | 1877 | jasmine.Runner.prototype.suites = function() { 1878 | return this.suites_; 1879 | }; 1880 | 1881 | jasmine.Runner.prototype.topLevelSuites = function() { 1882 | var topLevelSuites = []; 1883 | for (var i = 0; i < this.suites_.length; i++) { 1884 | if (!this.suites_[i].parentSuite) { 1885 | topLevelSuites.push(this.suites_[i]); 1886 | } 1887 | } 1888 | return topLevelSuites; 1889 | }; 1890 | 1891 | jasmine.Runner.prototype.results = function() { 1892 | return this.queue.results(); 1893 | }; 1894 | /** 1895 | * Internal representation of a Jasmine specification, or test. 1896 | * 1897 | * @constructor 1898 | * @param {jasmine.Env} env 1899 | * @param {jasmine.Suite} suite 1900 | * @param {String} description 1901 | */ 1902 | jasmine.Spec = function(env, suite, description) { 1903 | if (!env) { 1904 | throw new Error('jasmine.Env() required'); 1905 | } 1906 | if (!suite) { 1907 | throw new Error('jasmine.Suite() required'); 1908 | } 1909 | var spec = this; 1910 | spec.id = env.nextSpecId ? env.nextSpecId() : null; 1911 | spec.env = env; 1912 | spec.suite = suite; 1913 | spec.description = description; 1914 | spec.queue = new jasmine.Queue(env); 1915 | 1916 | spec.afterCallbacks = []; 1917 | spec.spies_ = []; 1918 | 1919 | spec.results_ = new jasmine.NestedResults(); 1920 | spec.results_.description = description; 1921 | spec.matchersClass = null; 1922 | }; 1923 | 1924 | jasmine.Spec.prototype.getFullName = function() { 1925 | return this.suite.getFullName() + ' ' + this.description + '.'; 1926 | }; 1927 | 1928 | 1929 | jasmine.Spec.prototype.results = function() { 1930 | return this.results_; 1931 | }; 1932 | 1933 | /** 1934 | * All parameters are pretty-printed and concatenated together, then written to the spec's output. 1935 | * 1936 | * Be careful not to leave calls to jasmine.log in production code. 1937 | */ 1938 | jasmine.Spec.prototype.log = function() { 1939 | return this.results_.log(arguments); 1940 | }; 1941 | 1942 | jasmine.Spec.prototype.runs = function (func) { 1943 | var block = new jasmine.Block(this.env, func, this); 1944 | this.addToQueue(block); 1945 | return this; 1946 | }; 1947 | 1948 | jasmine.Spec.prototype.addToQueue = function (block) { 1949 | if (this.queue.isRunning()) { 1950 | this.queue.insertNext(block); 1951 | } else { 1952 | this.queue.add(block); 1953 | } 1954 | }; 1955 | 1956 | /** 1957 | * @param {jasmine.ExpectationResult} result 1958 | */ 1959 | jasmine.Spec.prototype.addMatcherResult = function(result) { 1960 | this.results_.addResult(result); 1961 | }; 1962 | 1963 | jasmine.Spec.prototype.expect = function(actual) { 1964 | var positive = new (this.getMatchersClass_())(this.env, actual, this); 1965 | positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); 1966 | return positive; 1967 | }; 1968 | 1969 | /** 1970 | * Waits a fixed time period before moving to the next block. 1971 | * 1972 | * @deprecated Use waitsFor() instead 1973 | * @param {Number} timeout milliseconds to wait 1974 | */ 1975 | jasmine.Spec.prototype.waits = function(timeout) { 1976 | var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); 1977 | this.addToQueue(waitsFunc); 1978 | return this; 1979 | }; 1980 | 1981 | /** 1982 | * Waits for the latchFunction to return true before proceeding to the next block. 1983 | * 1984 | * @param {Function} latchFunction 1985 | * @param {String} optional_timeoutMessage 1986 | * @param {Number} optional_timeout 1987 | */ 1988 | jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { 1989 | var latchFunction_ = null; 1990 | var optional_timeoutMessage_ = null; 1991 | var optional_timeout_ = null; 1992 | 1993 | for (var i = 0; i < arguments.length; i++) { 1994 | var arg = arguments[i]; 1995 | switch (typeof arg) { 1996 | case 'function': 1997 | latchFunction_ = arg; 1998 | break; 1999 | case 'string': 2000 | optional_timeoutMessage_ = arg; 2001 | break; 2002 | case 'number': 2003 | optional_timeout_ = arg; 2004 | break; 2005 | } 2006 | } 2007 | 2008 | var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); 2009 | this.addToQueue(waitsForFunc); 2010 | return this; 2011 | }; 2012 | 2013 | jasmine.Spec.prototype.fail = function (e) { 2014 | var expectationResult = new jasmine.ExpectationResult({ 2015 | passed: false, 2016 | message: e ? jasmine.util.formatException(e) : 'Exception', 2017 | trace: { stack: e.stack } 2018 | }); 2019 | this.results_.addResult(expectationResult); 2020 | }; 2021 | 2022 | jasmine.Spec.prototype.getMatchersClass_ = function() { 2023 | return this.matchersClass || this.env.matchersClass; 2024 | }; 2025 | 2026 | jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { 2027 | var parent = this.getMatchersClass_(); 2028 | var newMatchersClass = function() { 2029 | parent.apply(this, arguments); 2030 | }; 2031 | jasmine.util.inherit(newMatchersClass, parent); 2032 | jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); 2033 | this.matchersClass = newMatchersClass; 2034 | }; 2035 | 2036 | jasmine.Spec.prototype.finishCallback = function() { 2037 | this.env.reporter.reportSpecResults(this); 2038 | }; 2039 | 2040 | jasmine.Spec.prototype.finish = function(onComplete) { 2041 | this.removeAllSpies(); 2042 | this.finishCallback(); 2043 | if (onComplete) { 2044 | onComplete(); 2045 | } 2046 | }; 2047 | 2048 | jasmine.Spec.prototype.after = function(doAfter) { 2049 | if (this.queue.isRunning()) { 2050 | this.queue.add(new jasmine.Block(this.env, doAfter, this)); 2051 | } else { 2052 | this.afterCallbacks.unshift(doAfter); 2053 | } 2054 | }; 2055 | 2056 | jasmine.Spec.prototype.execute = function(onComplete) { 2057 | var spec = this; 2058 | if (!spec.env.specFilter(spec)) { 2059 | spec.results_.skipped = true; 2060 | spec.finish(onComplete); 2061 | return; 2062 | } 2063 | 2064 | this.env.reporter.reportSpecStarting(this); 2065 | 2066 | spec.env.currentSpec = spec; 2067 | 2068 | spec.addBeforesAndAftersToQueue(); 2069 | 2070 | spec.queue.start(function () { 2071 | spec.finish(onComplete); 2072 | }); 2073 | }; 2074 | 2075 | jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { 2076 | var runner = this.env.currentRunner(); 2077 | var i; 2078 | 2079 | for (var suite = this.suite; suite; suite = suite.parentSuite) { 2080 | for (i = 0; i < suite.before_.length; i++) { 2081 | this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); 2082 | } 2083 | } 2084 | for (i = 0; i < runner.before_.length; i++) { 2085 | this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); 2086 | } 2087 | for (i = 0; i < this.afterCallbacks.length; i++) { 2088 | this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this)); 2089 | } 2090 | for (suite = this.suite; suite; suite = suite.parentSuite) { 2091 | for (i = 0; i < suite.after_.length; i++) { 2092 | this.queue.add(new jasmine.Block(this.env, suite.after_[i], this)); 2093 | } 2094 | } 2095 | for (i = 0; i < runner.after_.length; i++) { 2096 | this.queue.add(new jasmine.Block(this.env, runner.after_[i], this)); 2097 | } 2098 | }; 2099 | 2100 | jasmine.Spec.prototype.explodes = function() { 2101 | throw 'explodes function should not have been called'; 2102 | }; 2103 | 2104 | jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { 2105 | if (obj == jasmine.undefined) { 2106 | throw "spyOn could not find an object to spy upon for " + methodName + "()"; 2107 | } 2108 | 2109 | if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { 2110 | throw methodName + '() method does not exist'; 2111 | } 2112 | 2113 | if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { 2114 | throw new Error(methodName + ' has already been spied upon'); 2115 | } 2116 | 2117 | var spyObj = jasmine.createSpy(methodName); 2118 | 2119 | this.spies_.push(spyObj); 2120 | spyObj.baseObj = obj; 2121 | spyObj.methodName = methodName; 2122 | spyObj.originalValue = obj[methodName]; 2123 | 2124 | obj[methodName] = spyObj; 2125 | 2126 | return spyObj; 2127 | }; 2128 | 2129 | jasmine.Spec.prototype.removeAllSpies = function() { 2130 | for (var i = 0; i < this.spies_.length; i++) { 2131 | var spy = this.spies_[i]; 2132 | spy.baseObj[spy.methodName] = spy.originalValue; 2133 | } 2134 | this.spies_ = []; 2135 | }; 2136 | 2137 | /** 2138 | * Internal representation of a Jasmine suite. 2139 | * 2140 | * @constructor 2141 | * @param {jasmine.Env} env 2142 | * @param {String} description 2143 | * @param {Function} specDefinitions 2144 | * @param {jasmine.Suite} parentSuite 2145 | */ 2146 | jasmine.Suite = function(env, description, specDefinitions, parentSuite) { 2147 | var self = this; 2148 | self.id = env.nextSuiteId ? env.nextSuiteId() : null; 2149 | self.description = description; 2150 | self.queue = new jasmine.Queue(env); 2151 | self.parentSuite = parentSuite; 2152 | self.env = env; 2153 | self.before_ = []; 2154 | self.after_ = []; 2155 | self.children_ = []; 2156 | self.suites_ = []; 2157 | self.specs_ = []; 2158 | }; 2159 | 2160 | jasmine.Suite.prototype.getFullName = function() { 2161 | var fullName = this.description; 2162 | for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { 2163 | fullName = parentSuite.description + ' ' + fullName; 2164 | } 2165 | return fullName; 2166 | }; 2167 | 2168 | jasmine.Suite.prototype.finish = function(onComplete) { 2169 | this.env.reporter.reportSuiteResults(this); 2170 | this.finished = true; 2171 | if (typeof(onComplete) == 'function') { 2172 | onComplete(); 2173 | } 2174 | }; 2175 | 2176 | jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { 2177 | beforeEachFunction.typeName = 'beforeEach'; 2178 | this.before_.unshift(beforeEachFunction); 2179 | }; 2180 | 2181 | jasmine.Suite.prototype.afterEach = function(afterEachFunction) { 2182 | afterEachFunction.typeName = 'afterEach'; 2183 | this.after_.unshift(afterEachFunction); 2184 | }; 2185 | 2186 | jasmine.Suite.prototype.results = function() { 2187 | return this.queue.results(); 2188 | }; 2189 | 2190 | jasmine.Suite.prototype.add = function(suiteOrSpec) { 2191 | this.children_.push(suiteOrSpec); 2192 | if (suiteOrSpec instanceof jasmine.Suite) { 2193 | this.suites_.push(suiteOrSpec); 2194 | this.env.currentRunner().addSuite(suiteOrSpec); 2195 | } else { 2196 | this.specs_.push(suiteOrSpec); 2197 | } 2198 | this.queue.add(suiteOrSpec); 2199 | }; 2200 | 2201 | jasmine.Suite.prototype.specs = function() { 2202 | return this.specs_; 2203 | }; 2204 | 2205 | jasmine.Suite.prototype.suites = function() { 2206 | return this.suites_; 2207 | }; 2208 | 2209 | jasmine.Suite.prototype.children = function() { 2210 | return this.children_; 2211 | }; 2212 | 2213 | jasmine.Suite.prototype.execute = function(onComplete) { 2214 | var self = this; 2215 | this.queue.start(function () { 2216 | self.finish(onComplete); 2217 | }); 2218 | }; 2219 | jasmine.WaitsBlock = function(env, timeout, spec) { 2220 | this.timeout = timeout; 2221 | jasmine.Block.call(this, env, null, spec); 2222 | }; 2223 | 2224 | jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); 2225 | 2226 | jasmine.WaitsBlock.prototype.execute = function (onComplete) { 2227 | if (jasmine.VERBOSE) { 2228 | this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); 2229 | } 2230 | this.env.setTimeout(function () { 2231 | onComplete(); 2232 | }, this.timeout); 2233 | }; 2234 | /** 2235 | * A block which waits for some condition to become true, with timeout. 2236 | * 2237 | * @constructor 2238 | * @extends jasmine.Block 2239 | * @param {jasmine.Env} env The Jasmine environment. 2240 | * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. 2241 | * @param {Function} latchFunction A function which returns true when the desired condition has been met. 2242 | * @param {String} message The message to display if the desired condition hasn't been met within the given time period. 2243 | * @param {jasmine.Spec} spec The Jasmine spec. 2244 | */ 2245 | jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { 2246 | this.timeout = timeout || env.defaultTimeoutInterval; 2247 | this.latchFunction = latchFunction; 2248 | this.message = message; 2249 | this.totalTimeSpentWaitingForLatch = 0; 2250 | jasmine.Block.call(this, env, null, spec); 2251 | }; 2252 | jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); 2253 | 2254 | jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; 2255 | 2256 | jasmine.WaitsForBlock.prototype.execute = function(onComplete) { 2257 | if (jasmine.VERBOSE) { 2258 | this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); 2259 | } 2260 | var latchFunctionResult; 2261 | try { 2262 | latchFunctionResult = this.latchFunction.apply(this.spec); 2263 | } catch (e) { 2264 | this.spec.fail(e); 2265 | onComplete(); 2266 | return; 2267 | } 2268 | 2269 | if (latchFunctionResult) { 2270 | onComplete(); 2271 | } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { 2272 | var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); 2273 | this.spec.fail({ 2274 | name: 'timeout', 2275 | message: message 2276 | }); 2277 | 2278 | this.abort = true; 2279 | onComplete(); 2280 | } else { 2281 | this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; 2282 | var self = this; 2283 | this.env.setTimeout(function() { 2284 | self.execute(onComplete); 2285 | }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); 2286 | } 2287 | }; 2288 | // Mock setTimeout, clearTimeout 2289 | // Contributed by Pivotal Computer Systems, www.pivotalsf.com 2290 | 2291 | jasmine.FakeTimer = function() { 2292 | this.reset(); 2293 | 2294 | var self = this; 2295 | self.setTimeout = function(funcToCall, millis) { 2296 | self.timeoutsMade++; 2297 | self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); 2298 | return self.timeoutsMade; 2299 | }; 2300 | 2301 | self.setInterval = function(funcToCall, millis) { 2302 | self.timeoutsMade++; 2303 | self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); 2304 | return self.timeoutsMade; 2305 | }; 2306 | 2307 | self.clearTimeout = function(timeoutKey) { 2308 | self.scheduledFunctions[timeoutKey] = jasmine.undefined; 2309 | }; 2310 | 2311 | self.clearInterval = function(timeoutKey) { 2312 | self.scheduledFunctions[timeoutKey] = jasmine.undefined; 2313 | }; 2314 | 2315 | }; 2316 | 2317 | jasmine.FakeTimer.prototype.reset = function() { 2318 | this.timeoutsMade = 0; 2319 | this.scheduledFunctions = {}; 2320 | this.nowMillis = 0; 2321 | }; 2322 | 2323 | jasmine.FakeTimer.prototype.tick = function(millis) { 2324 | var oldMillis = this.nowMillis; 2325 | var newMillis = oldMillis + millis; 2326 | this.runFunctionsWithinRange(oldMillis, newMillis); 2327 | this.nowMillis = newMillis; 2328 | }; 2329 | 2330 | jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { 2331 | var scheduledFunc; 2332 | var funcsToRun = []; 2333 | for (var timeoutKey in this.scheduledFunctions) { 2334 | scheduledFunc = this.scheduledFunctions[timeoutKey]; 2335 | if (scheduledFunc != jasmine.undefined && 2336 | scheduledFunc.runAtMillis >= oldMillis && 2337 | scheduledFunc.runAtMillis <= nowMillis) { 2338 | funcsToRun.push(scheduledFunc); 2339 | this.scheduledFunctions[timeoutKey] = jasmine.undefined; 2340 | } 2341 | } 2342 | 2343 | if (funcsToRun.length > 0) { 2344 | funcsToRun.sort(function(a, b) { 2345 | return a.runAtMillis - b.runAtMillis; 2346 | }); 2347 | for (var i = 0; i < funcsToRun.length; ++i) { 2348 | try { 2349 | var funcToRun = funcsToRun[i]; 2350 | this.nowMillis = funcToRun.runAtMillis; 2351 | funcToRun.funcToCall(); 2352 | if (funcToRun.recurring) { 2353 | this.scheduleFunction(funcToRun.timeoutKey, 2354 | funcToRun.funcToCall, 2355 | funcToRun.millis, 2356 | true); 2357 | } 2358 | } catch(e) { 2359 | } 2360 | } 2361 | this.runFunctionsWithinRange(oldMillis, nowMillis); 2362 | } 2363 | }; 2364 | 2365 | jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { 2366 | this.scheduledFunctions[timeoutKey] = { 2367 | runAtMillis: this.nowMillis + millis, 2368 | funcToCall: funcToCall, 2369 | recurring: recurring, 2370 | timeoutKey: timeoutKey, 2371 | millis: millis 2372 | }; 2373 | }; 2374 | 2375 | /** 2376 | * @namespace 2377 | */ 2378 | jasmine.Clock = { 2379 | defaultFakeTimer: new jasmine.FakeTimer(), 2380 | 2381 | reset: function() { 2382 | jasmine.Clock.assertInstalled(); 2383 | jasmine.Clock.defaultFakeTimer.reset(); 2384 | }, 2385 | 2386 | tick: function(millis) { 2387 | jasmine.Clock.assertInstalled(); 2388 | jasmine.Clock.defaultFakeTimer.tick(millis); 2389 | }, 2390 | 2391 | runFunctionsWithinRange: function(oldMillis, nowMillis) { 2392 | jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); 2393 | }, 2394 | 2395 | scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { 2396 | jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); 2397 | }, 2398 | 2399 | useMock: function() { 2400 | if (!jasmine.Clock.isInstalled()) { 2401 | var spec = jasmine.getEnv().currentSpec; 2402 | spec.after(jasmine.Clock.uninstallMock); 2403 | 2404 | jasmine.Clock.installMock(); 2405 | } 2406 | }, 2407 | 2408 | installMock: function() { 2409 | jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; 2410 | }, 2411 | 2412 | uninstallMock: function() { 2413 | jasmine.Clock.assertInstalled(); 2414 | jasmine.Clock.installed = jasmine.Clock.real; 2415 | }, 2416 | 2417 | real: { 2418 | setTimeout: jasmine.getGlobal().setTimeout, 2419 | clearTimeout: jasmine.getGlobal().clearTimeout, 2420 | setInterval: jasmine.getGlobal().setInterval, 2421 | clearInterval: jasmine.getGlobal().clearInterval 2422 | }, 2423 | 2424 | assertInstalled: function() { 2425 | if (!jasmine.Clock.isInstalled()) { 2426 | throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); 2427 | } 2428 | }, 2429 | 2430 | isInstalled: function() { 2431 | return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; 2432 | }, 2433 | 2434 | installed: null 2435 | }; 2436 | jasmine.Clock.installed = jasmine.Clock.real; 2437 | 2438 | //else for IE support 2439 | jasmine.getGlobal().setTimeout = function(funcToCall, millis) { 2440 | if (jasmine.Clock.installed.setTimeout.apply) { 2441 | return jasmine.Clock.installed.setTimeout.apply(this, arguments); 2442 | } else { 2443 | return jasmine.Clock.installed.setTimeout(funcToCall, millis); 2444 | } 2445 | }; 2446 | 2447 | jasmine.getGlobal().setInterval = function(funcToCall, millis) { 2448 | if (jasmine.Clock.installed.setInterval.apply) { 2449 | return jasmine.Clock.installed.setInterval.apply(this, arguments); 2450 | } else { 2451 | return jasmine.Clock.installed.setInterval(funcToCall, millis); 2452 | } 2453 | }; 2454 | 2455 | jasmine.getGlobal().clearTimeout = function(timeoutKey) { 2456 | if (jasmine.Clock.installed.clearTimeout.apply) { 2457 | return jasmine.Clock.installed.clearTimeout.apply(this, arguments); 2458 | } else { 2459 | return jasmine.Clock.installed.clearTimeout(timeoutKey); 2460 | } 2461 | }; 2462 | 2463 | jasmine.getGlobal().clearInterval = function(timeoutKey) { 2464 | if (jasmine.Clock.installed.clearTimeout.apply) { 2465 | return jasmine.Clock.installed.clearInterval.apply(this, arguments); 2466 | } else { 2467 | return jasmine.Clock.installed.clearInterval(timeoutKey); 2468 | } 2469 | }; 2470 | 2471 | jasmine.version_= { 2472 | "major": 1, 2473 | "minor": 1, 2474 | "build": 0, 2475 | "revision": 1315677058 2476 | }; 2477 | -------------------------------------------------------------------------------- /apps/cryptotweets/tests/jasmine/jasmine_favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/cryptotweets/tests/jasmine/jasmine_favicon.png -------------------------------------------------------------------------------- /apps/cryptotweets/tests/wrapStringAtSpace.js: -------------------------------------------------------------------------------- 1 | describe('wrapStringAtSpace',function(){ 2 | it('wrap at word end',function(){ 3 | expect(wrapStringAtSpace("foo bar", 3)). 4 | toEqual(["foo","bar"]); 5 | }); 6 | it('wrap inside word',function(){ 7 | expect(wrapStringAtSpace("foo", 2)). 8 | toEqual(["fo", "o"]); 9 | }); 10 | it('handle zero length string',function(){ 11 | expect(wrapStringAtSpace("", 40)). 12 | toEqual(["", null]); 13 | }); 14 | it('handle short string',function(){ 15 | expect(wrapStringAtSpace("foo bar", 10)). 16 | toEqual(["foo bar", null]); 17 | }); 18 | it('trim spaces', function() { 19 | expect(wrapStringAtSpace(" foo bar ", 6)). 20 | toEqual(["foo", "bar"]); 21 | }); 22 | it('trim spaces #2', function() { 23 | expect(wrapStringAtSpace(" foo bar ", 7)). 24 | toEqual(["foo bar", null]); 25 | }); 26 | it('no spaces', function() { 27 | expect(wrapStringAtSpace("foobarbaz", 6)). 28 | toEqual(["foobar", "baz"]); 29 | }); 30 | }); -------------------------------------------------------------------------------- /apps/piratepig/images/background_tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/images/background_tile.png -------------------------------------------------------------------------------- /apps/piratepig/images/center_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/images/center_bottom.png -------------------------------------------------------------------------------- /apps/piratepig/images/game_bear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/images/game_bear.png -------------------------------------------------------------------------------- /apps/piratepig/images/game_bunny_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/images/game_bunny_02.png -------------------------------------------------------------------------------- /apps/piratepig/images/game_carrot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/images/game_carrot.png -------------------------------------------------------------------------------- /apps/piratepig/images/game_lemon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/images/game_lemon.png -------------------------------------------------------------------------------- /apps/piratepig/images/game_panda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/images/game_panda.png -------------------------------------------------------------------------------- /apps/piratepig/images/game_piratePig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/images/game_piratePig.png -------------------------------------------------------------------------------- /apps/piratepig/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/images/logo.png -------------------------------------------------------------------------------- /apps/piratepig/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Pirate Pig 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /apps/piratepig/sounds/3.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/sounds/3.mp3 -------------------------------------------------------------------------------- /apps/piratepig/sounds/3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/sounds/3.ogg -------------------------------------------------------------------------------- /apps/piratepig/sounds/3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/sounds/3.wav -------------------------------------------------------------------------------- /apps/piratepig/sounds/4.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/sounds/4.mp3 -------------------------------------------------------------------------------- /apps/piratepig/sounds/4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/sounds/4.ogg -------------------------------------------------------------------------------- /apps/piratepig/sounds/4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/sounds/4.wav -------------------------------------------------------------------------------- /apps/piratepig/sounds/5.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/sounds/5.mp3 -------------------------------------------------------------------------------- /apps/piratepig/sounds/5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/sounds/5.ogg -------------------------------------------------------------------------------- /apps/piratepig/sounds/5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/sounds/5.wav -------------------------------------------------------------------------------- /apps/piratepig/sounds/theme.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/sounds/theme.mp3 -------------------------------------------------------------------------------- /apps/piratepig/sounds/theme.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/sounds/theme.ogg -------------------------------------------------------------------------------- /apps/piratepig/sounds/theme.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/sounds/theme.wav -------------------------------------------------------------------------------- /apps/piratepig/sounds/whiff.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/sounds/whiff.mp3 -------------------------------------------------------------------------------- /apps/piratepig/sounds/whiff.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/sounds/whiff.ogg -------------------------------------------------------------------------------- /apps/piratepig/sounds/whiff.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/apps/piratepig/sounds/whiff.wav -------------------------------------------------------------------------------- /apps/piratepig/source/App.js: -------------------------------------------------------------------------------- 1 | enyo.kind({ 2 | name:"App", 3 | kind:"Control", 4 | hatsize: 48, 5 | hatmargin: 8, 6 | rows: 10, 7 | score: 0, 8 | canplay: true, 9 | moves: [], 10 | components:[ 11 | {name:"bottom", classes:"overlap center bottom"}, 12 | {name:"hats", style:"display: none;", components: [ 13 | {kind:"Image", onload:"loadHandler", src:"images/game_bear.png"}, 14 | {kind:"Image", onload:"loadHandler", src:"images/game_bunny_02.png"}, 15 | {kind:"Image", onload:"loadHandler", src:"images/game_carrot.png"}, 16 | {kind:"Image", onload:"loadHandler", src:"images/game_lemon.png"}, 17 | {kind:"Image", onload:"loadHandler", src:"images/game_panda.png"}, 18 | {kind:"Image", onload:"loadHandler", src:"images/game_piratePig.png"}, 19 | ]}, 20 | // why can't there just be one standard that isn't wav? 21 | {name:"sounds", style:"display: none;", components:[ 22 | {name:"three", kind:"GameAudio", src:["sounds/3.ogg", "sounds/3.mp3", "sounds/3.wav"], onEnded: "endedHandler"}, 23 | {name:"four", kind:"GameAudio", src:["sounds/4.ogg", "sounds/4.mp3", "sounds/4.wav"], onEnded: "endedHandler"}, 24 | {name:"fiveplus", kind:"GameAudio", src:["sounds/5.ogg", "sounds/5.mp3", "sounds/5.wav"], onEnded: "endedHandler"}, 25 | {name:"whiff", kind:"GameAudio", src:["sounds/whiff.ogg", "sounds/whiff.mp3", "sounds/whiff.wav"], onEnded:"endedHandler"}, 26 | {name:"theme", kind:"GameAudio", src:["sounds/theme.ogg", "sounds/theme.mp3", "sounds/theme.wav"], onEnded:"endedHandler"} 27 | ]}, 28 | {name:"title", classes:"hcenter banner", components:[ 29 | {name:"score", classes:"score", content:"0"}, 30 | ]}, 31 | {name:"backgroundbox", kind:"Canvas", attributes:{height:"560px", width:"560px"}, classes:"overlap hcenter", components:[ 32 | {name:"background", kind:"canvas.Rectangle", bounds:{t:0, l:0, w:560, h:560}, color:"rgba(255,255,255,0.9)"}, 33 | ]}, 34 | {name:"selectbox", kind:"Canvas", attributes:{height:"560px", width:"560px"}, classes:"overlap hcenter", components:[ 35 | {name:"selector", kind:"Selector", color:"red"} 36 | ]}, 37 | {name:"hatbox", kind:"Canvas", attributes:{height:"560px", width:"560px"}, classes:"overlap hcenter border", ontap:"hatTap"} 38 | ], 39 | create: function() { 40 | this.inherited(arguments); 41 | this.addClass("bg"); 42 | // sounds only occur on user actions on ios 43 | if (navigator.userAgent.match(/i(?:pad|phone)/i)) { 44 | this.ios = true; 45 | } 46 | }, 47 | loadHandler: function() { 48 | this.loaded = (this.loaded || 0) + 1; 49 | if (this.loaded == this.$.hats.getClientControls().length) { 50 | this.initHats(); 51 | } 52 | }, 53 | endedHandler: function() { 54 | enyo.job.stop(this.id + "enableSound"); 55 | this.canplay = true; 56 | }, 57 | getImage: function(inIndex) { 58 | return this.$.hats.getClientControls()[inIndex].hasNode(); 59 | }, 60 | getType: function() { 61 | return enyo.irand(this.$.hats.getClientControls().length); 62 | }, 63 | scale: function() { 64 | var b = this.getBounds(); 65 | var h = b.height; 66 | var w = b.width; 67 | var i, c; 68 | if (w < this.rows * 36 || h < (this.rows+1) * 36) { 69 | this.hatsize = 28; 70 | this.hatmargin = 4; 71 | } else if (w < this.rows * 56 || h < (this.rows+1) * 56) { 72 | this.hatsize = 32; 73 | this.hatmargin = 4; 74 | } else if (w > this.rows * 100 && h > (this.rows+1) * 100) { 75 | this.hatsize = 62; 76 | this.hatmargin = 10; 77 | } else { 78 | this.hatsize = 48; 79 | this.hatmargin = 8; 80 | } 81 | var s = (this.hatsize + this.hatmargin) * this.rows; 82 | this.$.title.applyStyle("width", s + "px"); 83 | this.$.title.applyStyle("height", s/10 + "px"); 84 | this.$.score.applyStyle("line-height", s/10 + "px"); 85 | this.$.background.bounds.w = s; 86 | this.$.background.bounds.h = s; 87 | this.$.selector.setSize(this.hatsize); 88 | this.$.selector.setMargin(this.hatmargin); 89 | var hc = this.$.hatbox.getClientControls(); 90 | for (i = 0; (h = hc[i]); i++) { 91 | h.setSize(this.hatsize); 92 | h.setMargin(this.hatmargin); 93 | h.iChanged(); 94 | h.jChanged(); 95 | } 96 | for (i = 0; (c = ["background", "select", "hat"][i]); i++) { 97 | this.$[c+"box"].setAttribute("width", s); 98 | this.$[c+"box"].setAttribute("height", s); 99 | this.$[c+"box"].update(); 100 | } 101 | }, 102 | initHats: function() { 103 | this.scale(); 104 | this.$.hatbox.destroyClientControls(); 105 | var col; 106 | this.map = []; 107 | var src, t, l; 108 | for (var i = 0; i < this.rows; i++) { 109 | // i = which column = x = left 110 | col = this.map[i] = []; 111 | for (var j = 0; j < this.rows; j++) { 112 | // j = which row = y = top 113 | var nhat = this.newHat(i,j); 114 | col.push(nhat); 115 | } 116 | } 117 | this.playSound(-1); 118 | this.start(); 119 | this.splatMatches(this.checkMatches()); 120 | }, 121 | start: function() { 122 | this.pulse = setInterval(enyo.bind(this, "heartbeat"), 30); 123 | }, 124 | stop: function() { 125 | clearInterval(this.pulse); 126 | }, 127 | playSound: function(inNum, inUserEvent) { 128 | var iosplay = this.ios ? inUserEvent : true; 129 | if (this.canplay && iosplay) { 130 | this.canplay = false; 131 | var n; 132 | var delay = 0; 133 | if (inNum >= 5) { 134 | n = this.$.fiveplus; 135 | delay = 2800; 136 | } else if (inNum == 4) { 137 | n = this.$.four; 138 | delay = 800; 139 | } else if (inNum <= 3 && inNum > 0) { 140 | n = this.$.three; 141 | delay = 100; 142 | } else if (inNum == -1) { 143 | n = this.$.theme; 144 | delay = 6000; 145 | } else { 146 | n = this.$.whiff; 147 | delay = 2000; 148 | } 149 | n.play(); 150 | enyo.job(this.id+"enableSound", enyo.bind(this,"endedHandler"), delay); 151 | } 152 | }, 153 | hatTap: function(inSender, inEvent) { 154 | var t = inEvent.target; 155 | var x = inEvent.pageX - t.offsetLeft; 156 | var y = inEvent.pageY - t.offsetTop; 157 | var col = Math.floor(x / (this.hatsize + this.hatmargin)); 158 | var row = Math.floor(y / (this.hatsize + this.hatmargin)); 159 | var oh = this.map[col][row]; 160 | var matches = 0; 161 | if (oh.animating) { 162 | return; 163 | } 164 | if (!this.selected) { 165 | this.selected = oh; 166 | this.$.selector.show(col, row); 167 | } else { 168 | var d = this.selected.distance(oh); 169 | // manhattan distance 170 | var md = Math.abs(d.di) + Math.abs(d.dj); 171 | if (md == 1) { 172 | matches = this.exchange(this.selected, oh); 173 | } 174 | // don't play sound if you tap the selected square again 175 | if (md !== 0) { 176 | this.playSound(matches, true); 177 | } 178 | this.selected = null; 179 | this.$.selector.hide(); 180 | } 181 | this.$.selectbox.update(); 182 | }, 183 | distance: function(inHatA, inHatB) { 184 | return Math.abs(inHatA.i - inHatB.i) + Math.abs(inHatA.j - inHatB.j); 185 | }, 186 | exchange: function(inA, inB) { 187 | var d = inA.distance(inB); 188 | var na = {i: inA.i + d.di, j: inA.j + d.dj}; 189 | var nb = {i: inB.i - d.di, j: inB.j - d.dj}; 190 | this.map[na.i][na.j] = inA; 191 | this.map[nb.i][nb.j] = inB; 192 | var matches = this.checkMatches(); 193 | if (matches.length) { 194 | if (d.di) { 195 | this.moves.push(inA.moveX(d.di)); 196 | this.moves.push(inB.moveX(-d.di)); 197 | } else { 198 | this.moves.push(inA.moveY(d.dj)); 199 | this.moves.push(inB.moveY(-d.dj)); 200 | } 201 | } 202 | this.map[inA.i][inA.j] = inA; 203 | this.map[inB.i][inB.j] = inB; 204 | return matches.length; 205 | }, 206 | checkMatches: function() { 207 | var matches = []; 208 | this._checkMatches( 209 | enyo.bind(this,"fget"), 210 | enyo.bind(this,"fmake"), 211 | matches 212 | ); 213 | this._checkMatches( 214 | enyo.bind(this,"rget"), 215 | enyo.bind(this,"rmake"), 216 | matches 217 | ); 218 | return matches; 219 | }, 220 | fget: function(a,b) { 221 | return this.map[a][b].type; 222 | }, 223 | rget: function(a,b) { 224 | return this.map[b][a].type; 225 | }, 226 | fmake: function(a,b) { 227 | return {i:a,j:b}; 228 | }, 229 | rmake: function(a,b) { 230 | return {i:b,j:a}; 231 | }, 232 | _checkMatches: function(get, make, matches) { 233 | for (var i=0, t, c, k0; i= 2) { 248 | if (j == this.rows-1 && tt == t) { 249 | k0++; 250 | } 251 | this.score += Math.pow(c,2) * 50; 252 | for (var k=k0; c>-1; k++,c--) { 253 | matches.push(make(i, k)); 254 | } 255 | } 256 | c = 0; 257 | t = tt; 258 | } 259 | } 260 | } 261 | }, 262 | splatMatches: function(inMatches) { 263 | for (var i=0, m; (m=inMatches[i]); i++) { 264 | this.splat(m.i, m.j); 265 | } 266 | if (inMatches.length) { 267 | this.playSound(inMatches.length); 268 | } 269 | this.$.score.setContent(this.score); 270 | }, 271 | splat: function(i, j) { 272 | var hat0 = this.map[i][j]; 273 | if (hat0.animating) { 274 | return; 275 | } 276 | this.map[hat0.i][hat0.j] = null; 277 | for (i=hat0.i, j=hat0.j-1; j>=0; j--) { 278 | var hat = this.map[i][j]; 279 | if (hat) { 280 | this.map[i][j] = null; 281 | this.map[i][j+1] = hat; 282 | this.moves.push(hat.moveDown()); 283 | } 284 | } 285 | var nhat = this.map[hat0.i][0] = this.newHat(hat0.i, -1); 286 | this.moves.push(nhat.moveDown()); 287 | this.moves.push(hat0.splat()); 288 | }, 289 | heartbeat: function() { 290 | if (this.moves.length) { 291 | var live = [], i, m; 292 | for (i = 0; (m = this.moves[i]); i++) { 293 | if (m.move()) { 294 | live.push(m); 295 | } 296 | } 297 | this.moves = live; 298 | if (this.moves.length === 0) { 299 | this.splatMatches(this.checkMatches()); 300 | // stabilze board 301 | var hc = this.$.hatbox.getClientControls(); 302 | for (i = 0, h; (h = hc[i]); i++) { 303 | h.iChanged(); 304 | h.jChanged(); 305 | } 306 | } 307 | this.$.hatbox.update(); 308 | } 309 | }, 310 | newHat: function(i,j) { 311 | var type = this.getType(); 312 | var image = this.getImage(type); 313 | return this.$.hatbox.createComponent({kind: "Hat", i: i, j: j, owner: this, image:image, type:type, bounds:{}, size:this.hatsize, margin:this.hatmargin}); 314 | }, 315 | resizeHandler: function() { 316 | enyo.job(this.id + "resize", enyo.bind(this, "rescale"), 100); 317 | }, 318 | rescale: function() { 319 | this.stop(); 320 | this.scale(); 321 | this.start(); 322 | }, 323 | touchstartHandler: function() { 324 | if (this.ios && !this.iosfirstsound) { 325 | this.iosfirstsound = true; 326 | this.playSound(-1, true); 327 | } 328 | } 329 | }); 330 | -------------------------------------------------------------------------------- /apps/piratepig/source/Audio.js: -------------------------------------------------------------------------------- 1 | enyo.kind({ 2 | name:"GameAudio", 3 | kind:"Control", 4 | published: { 5 | src: "" 6 | }, 7 | events: { 8 | onEnded: "" 9 | }, 10 | //* @protected 11 | create: function() { 12 | this.inherited(arguments); 13 | this.srcChanged(); 14 | }, 15 | srcChanged: function() { 16 | this.destroyClientControls(); 17 | var a = this.createComponent({tag:"audio", name:"client", style:"display:none;", owner:this}); 18 | var srcs = this.src; 19 | if (srcs && !enyo.isArray(srcs)) { 20 | srcs = [srcs]; 21 | } 22 | for (var i = 0, s; (s = srcs[i]); i++) { 23 | var type = s.replace(/[^.]*./,""); 24 | var mime = {mp3:"audio/mpeg", ogg:"audio/ogg", wav:"audio/wav"}[type]; 25 | a.createComponent({tag:"source", src:s, attributes:{type:mime}, owner:this}); 26 | } 27 | a.attributes.ended = enyo.bind(this, "doEnded"); 28 | }, 29 | //* @public 30 | play: function() { 31 | var a = this.$.client.hasNode(); 32 | if (a && a.play) { 33 | a.play(); 34 | } 35 | } 36 | }); 37 | -------------------------------------------------------------------------------- /apps/piratepig/source/Hat.js: -------------------------------------------------------------------------------- 1 | enyo.kind({ 2 | name:"Hat", 3 | kind:enyo.canvas.Control, 4 | published: { 5 | image: null, 6 | type: 0, 7 | i: 0, 8 | j: 0, 9 | size: 48, 10 | margin: 8, 11 | steps: 10 12 | }, 13 | create: function() { 14 | this.inherited(arguments); 15 | this.sizeChanged(); 16 | this.iChanged(); 17 | this.jChanged(); 18 | }, 19 | renderSelf: function(ctx) { 20 | if (this.image) { 21 | ctx.drawImage(this.image, this.bounds.l, this.bounds.t, this.bounds.w, this.bounds.h); 22 | } 23 | }, 24 | iChanged: function() { 25 | this.bounds.l = this.box * this.i; 26 | }, 27 | jChanged: function() { 28 | this.bounds.t = this.box * this.j; 29 | }, 30 | sizeChanged: function() { 31 | this.bounds.w = this.size; 32 | this.bounds.h = this.size; 33 | this.box = (this.size + this.margin); 34 | }, 35 | marginChanged: function() { 36 | this.box = (this.size + this.margin); 37 | }, 38 | 39 | distance: function(inOtherHat) { 40 | return {di: inOtherHat.i - this.i, dj: inOtherHat.j - this.j}; 41 | }, 42 | moveX: function(inDx) { 43 | this.animating = true; 44 | var i = this.i; 45 | this.i += inDx; 46 | return { 47 | hat: this, 48 | box: this.box, 49 | l: i * this.box + this.box * inDx, 50 | dx: this.steps, 51 | steps: this.steps, 52 | move: function() { 53 | this.dx--; 54 | var lerp = enyo.easing.cubicIn(this.dx/this.steps) * this.steps; 55 | this.hat.bounds.l = this.l - lerp*inDx*4; 56 | this.hat.animating = Boolean(this.dx >= 0); 57 | return this.hat.animating; 58 | } 59 | }; 60 | }, 61 | moveY: function(inDy) { 62 | this.animating = true; 63 | var j = this.j; 64 | this.j += inDy; 65 | return { 66 | hat: this, 67 | box: this.box, 68 | t: j * this.box + inDy * this.box, 69 | dy: this.steps, 70 | steps: this.steps, 71 | move: function() { 72 | this.dy--; 73 | var lerp = enyo.easing.cubicIn(this.dy/this.steps) * this.steps; 74 | this.hat.bounds.t = this.t - lerp*inDy*4; 75 | this.hat.animating = Boolean(this.dy >= 0); 76 | return this.hat.animating; 77 | } 78 | }; 79 | }, 80 | moveUp: function() { 81 | return this.moveY(-1); 82 | }, 83 | moveDown: function() { 84 | return this.moveY(1); 85 | }, 86 | splat: function() { 87 | this.animating = true; 88 | return { 89 | hat: this, 90 | size: this.size, 91 | box: this.box, 92 | dy: this.steps, 93 | steps: this.steps, 94 | l: this.bounds.l, 95 | t: this.bounds.t, 96 | move: function() { 97 | this.dy--; 98 | var lerp = enyo.easing.cubicOut(this.dy/this.steps); 99 | var off = this.size * (1-lerp); 100 | this.hat.bounds.w = Math.floor(this.size - off); 101 | this.hat.bounds.h = Math.floor(this.size - off); 102 | this.hat.bounds.l = Math.floor(this.l + off / 2); 103 | this.hat.bounds.t = Math.floor(this.t + off / 2); 104 | if (this.dy >= 0) { 105 | return true; 106 | } 107 | this.hat.destroy(); 108 | } 109 | }; 110 | } 111 | }); 112 | -------------------------------------------------------------------------------- /apps/piratepig/source/Selector.js: -------------------------------------------------------------------------------- 1 | enyo.kind({ 2 | name:"Selector", 3 | kind:enyo.canvas.Circle, 4 | published: { 5 | size: 48, 6 | margin: 8 7 | }, 8 | create: function() { 9 | this.inherited(arguments); 10 | this.sizeChanged(); 11 | }, 12 | sizeChanged: function() { 13 | this.hide(); 14 | this.bounds.w = this.size / 2; 15 | }, 16 | show: function (inHatRow, inHatCol) { 17 | this.bounds.l = this.bounds.w + (this.size + this.margin) * inHatRow; 18 | this.bounds.t = this.bounds.w + (this.size + this.margin) * inHatCol; 19 | }, 20 | hide: function() { 21 | this.bounds.t = this.bounds.w * -2; 22 | this.bounds.l = this.bounds.w * -2; 23 | } 24 | }); 25 | -------------------------------------------------------------------------------- /apps/piratepig/source/app.css: -------------------------------------------------------------------------------- 1 | .bg { 2 | background-image: url(../images/background_tile.png); 3 | background-repeat: repeat-x; 4 | background-color: #d8f9ff; 5 | } 6 | 7 | .bottom { 8 | background-image: url(../images/center_bottom.png); 9 | background-position: bottom; 10 | background-repeat: no-repeat; 11 | background-size: auto; 12 | } 13 | 14 | .overlap { 15 | position: absolute; 16 | margin: auto; 17 | } 18 | 19 | .center, .hcenter { 20 | left: 0; 21 | right: 0; 22 | } 23 | 24 | .center, .vcenter { 25 | top: 0; 26 | bottom: 0; 27 | } 28 | 29 | .right { 30 | right: 0; 31 | } 32 | 33 | .border { 34 | border: 1px solid black; 35 | } 36 | 37 | .score { 38 | text-align: right; 39 | vertical-align: center; 40 | font-family: "Sans"; 41 | font-size: 25px; 42 | } 43 | 44 | .banner { 45 | margin: auto; 46 | background-image: url(../images/logo.png); 47 | background-position: top left; 48 | background-repeat: no-repeat; 49 | background-size: contain; 50 | } 51 | -------------------------------------------------------------------------------- /apps/piratepig/source/package.js: -------------------------------------------------------------------------------- 1 | enyo.depends( 2 | "$lib/canvas", 3 | "app.css", 4 | "Audio.js", 5 | "Hat.js", 6 | "Selector.js", 7 | "App.js" 8 | ); 9 | -------------------------------------------------------------------------------- /examples/0 - Hello World/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enyo 5 | 6 | 7 | 8 | 12 | 13 | 14 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/1 - Making a Kind/HelloWorld.js: -------------------------------------------------------------------------------- 1 | enyo.kind({ 2 | name: "HelloWorld", 3 | kind: enyo.Control, 4 | content: "Hello World from Enyo" 5 | }); -------------------------------------------------------------------------------- /examples/1 - Making a Kind/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enyo 5 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /examples/2 - Making a Package/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enyo 5 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /examples/2 - Making a Package/source/HelloWorld.css: -------------------------------------------------------------------------------- 1 | .bigfont { 2 | font-size: x-large; 3 | }; -------------------------------------------------------------------------------- /examples/2 - Making a Package/source/HelloWorld.js: -------------------------------------------------------------------------------- 1 | enyo.kind({ 2 | name: "HelloWorld", 3 | kind: enyo.Control, 4 | content: "Hello World from Enyo" 5 | }); -------------------------------------------------------------------------------- /examples/2 - Making a Package/source/package.js: -------------------------------------------------------------------------------- 1 | enyo.depends( 2 | "HelloWorld.css", 3 | "HelloWorld.js" 4 | ); 5 | -------------------------------------------------------------------------------- /examples/3 - Making Builds/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enyo 5 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /examples/3 - Making Builds/source/HelloWorld.css: -------------------------------------------------------------------------------- 1 | .bigfont { 2 | font-size: x-large; 3 | }; -------------------------------------------------------------------------------- /examples/3 - Making Builds/source/HelloWorld.js: -------------------------------------------------------------------------------- 1 | enyo.kind({ 2 | name: "HelloWorld", 3 | kind: enyo.Control, 4 | content: "Hello World from Enyo" 5 | }); -------------------------------------------------------------------------------- /examples/3 - Making Builds/source/minify-include-enyo/minify.bat: -------------------------------------------------------------------------------- 1 | REM @ECHO OFF 2 | 3 | SET ENYO=..\..\..\..\ 4 | 5 | SET TOOLS=%ENYO%\tools 6 | SET NODE=%TOOLS%\node.exe 7 | SET MINIFY=%TOOLS%\minifier\minify.js 8 | 9 | MKDIR build 10 | %NODE% %MINIFY% package.js -enyo %ENYO% -output build/build 11 | 12 | PAUSE -------------------------------------------------------------------------------- /examples/3 - Making Builds/source/minify-include-enyo/package.js: -------------------------------------------------------------------------------- 1 | enyo.depends( 2 | "$enyo/source", 3 | ".." 4 | ); 5 | -------------------------------------------------------------------------------- /examples/3 - Making Builds/source/minify/minify.bat: -------------------------------------------------------------------------------- 1 | REM @ECHO OFF 2 | 3 | SET ENYO=..\..\..\..\ 4 | 5 | SET TOOLS=%ENYO%\tools 6 | SET NODE=%TOOLS%\node.exe 7 | SET MINIFY=%TOOLS%\minifier\minify.js 8 | 9 | MKDIR build 10 | %NODE% %MINIFY% package.js -enyo %ENYO% -output build/build 11 | 12 | PAUSE -------------------------------------------------------------------------------- /examples/3 - Making Builds/source/minify/package.js: -------------------------------------------------------------------------------- 1 | enyo.depends( 2 | "../package.js" 3 | ); 4 | -------------------------------------------------------------------------------- /examples/3 - Making Builds/source/package.js: -------------------------------------------------------------------------------- 1 | enyo.depends( 2 | "HelloWorld.css", 3 | "HelloWorld.js" 4 | ); 5 | -------------------------------------------------------------------------------- /examples/ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enyo Ui Examples 5 | 6 | 34 | 35 | 36 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /examples/ui/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyojs/support/0ddaf011d7b66a43f8d4e4a8f68e29f713aa8c46/examples/ui/search.png --------------------------------------------------------------------------------