├── 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 | 
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
--------------------------------------------------------------------------------