├── .gitignore
├── Ejecta.xcodeproj
└── project.pbxproj
├── README.md
├── Resources
├── Assets-TV.xcassets
│ ├── App Icon & Top Shelf Image.brandassets
│ │ ├── App Icon - Large.imagestack
│ │ │ ├── Back.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ └── Icon-Large-Back.png
│ │ │ │ └── Contents.json
│ │ │ ├── Contents.json
│ │ │ └── Front.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── Icon-Large-Front.png
│ │ │ │ └── Contents.json
│ │ ├── App Icon - Small.imagestack
│ │ │ ├── Back.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ └── Icon-Back.png
│ │ │ │ └── Contents.json
│ │ │ ├── Contents.json
│ │ │ ├── Front.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ └── Icon-Front.png
│ │ │ │ └── Contents.json
│ │ │ └── Middle.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── Icon-Middle.png
│ │ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ └── Top Shelf Image.imageset
│ │ │ ├── Contents.json
│ │ │ └── Top-Shelf.png
│ ├── Contents.json
│ └── LaunchImage.launchimage
│ │ ├── Contents.json
│ │ └── LaunchScreen-TV.png
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── Icon-60@2x.png
│ │ ├── Icon-60@3x.png
│ │ ├── Icon-76.png
│ │ ├── Icon-76@2x.png
│ │ └── Icon-83.5@2x.png
│ └── Contents.json
├── Info-TV.plist
├── Info.plist
├── LaunchScreen-Logo.png
└── LaunchScreen.xib
├── Source
├── AppDelegate.h
├── AppDelegate.m
├── Ejecta
│ ├── EJAppViewController.h
│ ├── EJAppViewController.m
│ ├── EJAudio
│ │ ├── EJAudioSource.h
│ │ ├── EJAudioSourceAVAudio.h
│ │ ├── EJAudioSourceAVAudio.m
│ │ ├── EJAudioSourceOpenAL.h
│ │ ├── EJAudioSourceOpenAL.m
│ │ ├── EJBindingAudio.h
│ │ ├── EJBindingAudio.m
│ │ ├── EJOpenALBuffer.h
│ │ ├── EJOpenALBuffer.m
│ │ ├── EJSharedOpenALManager.h
│ │ └── EJSharedOpenALManager.m
│ ├── EJBindingBase.h
│ ├── EJBindingBase.m
│ ├── EJBindingEventedBase.h
│ ├── EJBindingEventedBase.m
│ ├── EJBindingGlobalUtils.h
│ ├── EJBindingGlobalUtils.m
│ ├── EJBindingVideo.h
│ ├── EJBindingVideo.m
│ ├── EJCanvas
│ │ ├── 2D
│ │ │ ├── EJBindingCanvasContext2D.h
│ │ │ ├── EJBindingCanvasContext2D.m
│ │ │ ├── EJBindingCanvasGradient.h
│ │ │ ├── EJBindingCanvasGradient.m
│ │ │ ├── EJBindingCanvasPattern.h
│ │ │ ├── EJBindingCanvasPattern.m
│ │ │ ├── EJBindingImageData.h
│ │ │ ├── EJBindingImageData.m
│ │ │ ├── EJBindingTextMetrics.h
│ │ │ ├── EJBindingTextMetrics.m
│ │ │ ├── EJCanvas2DTypes.h
│ │ │ ├── EJCanvasContext2D.h
│ │ │ ├── EJCanvasContext2D.m
│ │ │ ├── EJCanvasContext2DScreen.h
│ │ │ ├── EJCanvasContext2DScreen.m
│ │ │ ├── EJCanvasContext2DTexture.h
│ │ │ ├── EJCanvasContext2DTexture.m
│ │ │ ├── EJCanvasGradient.h
│ │ │ ├── EJCanvasGradient.m
│ │ │ ├── EJCanvasPattern.h
│ │ │ ├── EJCanvasPattern.m
│ │ │ ├── EJCanvasShaders.h
│ │ │ ├── EJConvertColorRGBA.h
│ │ │ ├── EJConvertColorRGBA.m
│ │ │ ├── EJFont.h
│ │ │ ├── EJFont.mm
│ │ │ ├── EJFontCache.h
│ │ │ ├── EJFontCache.m
│ │ │ ├── EJGLProgram2D.h
│ │ │ ├── EJGLProgram2D.m
│ │ │ ├── EJGLProgram2DRadialGradient.h
│ │ │ ├── EJGLProgram2DRadialGradient.m
│ │ │ ├── EJImageData.h
│ │ │ ├── EJImageData.m
│ │ │ ├── EJPath.h
│ │ │ └── EJPath.mm
│ │ ├── EAGLView.h
│ │ ├── EAGLView.m
│ │ ├── EJBindingCanvas.h
│ │ ├── EJBindingCanvas.m
│ │ ├── EJBindingCanvasStyle.h
│ │ ├── EJBindingCanvasStyle.m
│ │ ├── EJBindingImage.h
│ │ ├── EJBindingImage.m
│ │ ├── EJCanvasContext.h
│ │ ├── EJCanvasContext.m
│ │ ├── EJDrawable.h
│ │ ├── EJPresentable.h
│ │ ├── EJSharedTextureCache.h
│ │ ├── EJSharedTextureCache.m
│ │ ├── EJTexture.h
│ │ ├── EJTexture.m
│ │ ├── EJTextureStorage.h
│ │ ├── EJTextureStorage.m
│ │ └── WebGL
│ │ │ ├── EJBindingCanvasContextWebGL.h
│ │ │ ├── EJBindingCanvasContextWebGL.m
│ │ │ ├── EJBindingWebGLExtensions.h
│ │ │ ├── EJBindingWebGLExtensions.m
│ │ │ ├── EJBindingWebGLObjects.h
│ │ │ ├── EJBindingWebGLObjects.m
│ │ │ ├── EJCanvasContextWebGL.h
│ │ │ ├── EJCanvasContextWebGL.m
│ │ │ ├── EJCanvasContextWebGLScreen.h
│ │ │ ├── EJCanvasContextWebGLScreen.m
│ │ │ ├── EJCanvasContextWebGLTexture.h
│ │ │ ├── EJCanvasContextWebGLTexture.m
│ │ │ ├── EJConvertWebGL.h
│ │ │ └── EJConvertWebGL.m
│ ├── EJClassLoader.h
│ ├── EJClassLoader.m
│ ├── EJConvert.h
│ ├── EJConvert.m
│ ├── EJJavaScriptView.h
│ ├── EJJavaScriptView.m
│ ├── EJNonRetainingProxy.h
│ ├── EJNonRetainingProxy.m
│ ├── EJSharedOpenGLContext.h
│ ├── EJSharedOpenGLContext.m
│ ├── EJTimer.h
│ ├── EJTimer.m
│ ├── EJUtils
│ │ ├── EJBindingDeviceMotion.h
│ │ ├── EJBindingDeviceMotion.m
│ │ ├── EJBindingGameCenter.h
│ │ ├── EJBindingGameCenter.m
│ │ ├── EJBindingGeolocation.h
│ │ ├── EJBindingGeolocation.m
│ │ ├── EJBindingHttpRequest.h
│ │ ├── EJBindingHttpRequest.m
│ │ ├── EJBindingImagePicker.h
│ │ ├── EJBindingImagePicker.m
│ │ ├── EJBindingKeyInput.h
│ │ ├── EJBindingKeyInput.m
│ │ ├── EJBindingLocalStorage.h
│ │ ├── EJBindingLocalStorage.m
│ │ ├── EJBindingTouchInput.h
│ │ ├── EJBindingTouchInput.m
│ │ ├── EJBindingWebSocket.h
│ │ ├── EJBindingWebSocket.m
│ │ ├── EJBindingWindowEvents.h
│ │ ├── EJBindingWindowEvents.m
│ │ ├── Gamepad
│ │ │ ├── EJBindingGamepad.h
│ │ │ ├── EJBindingGamepad.m
│ │ │ ├── EJBindingGamepadButton.h
│ │ │ ├── EJBindingGamepadButton.m
│ │ │ ├── EJBindingGamepadProvider.h
│ │ │ └── EJBindingGamepadProvider.m
│ │ └── IAP
│ │ │ ├── EJBindingIAPManager.h
│ │ │ ├── EJBindingIAPManager.m
│ │ │ ├── EJBindingIAPProduct.h
│ │ │ ├── EJBindingIAPProduct.m
│ │ │ ├── EJBindingIAPTransaction.h
│ │ │ └── EJBindingIAPTransaction.m
│ └── Ejecta.js
├── Prefix.pch
├── lib
│ └── SocketRocket
│ │ ├── SRWebSocket.h
│ │ ├── SRWebSocket.m
│ │ └── SocketRocket-Prefix.pch
└── main.m
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | .DS_Store
3 | build/
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | *.xcworkspace
13 | !default.xcworkspace
14 | xcuserdata
15 | profile
16 | *.moved-aside
17 | DerivedData
18 | .idea/
19 |
20 | # Ignoring App folder for
21 | # ease of implementation
22 | App
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ejecta
2 |
3 | Ejecta is a fast, open source JavaScript, Canvas & Audio implementation for iOS (iPhone, iPod Touch, iPad) and tvOS (Apple TV). Think of it as a Browser that can only display a Canvas element.
4 |
5 | More info & Documentation: http://impactjs.com/ejecta
6 |
7 | Ejecta is published under the [MIT Open Source License](http://opensource.org/licenses/mit-license.php).
8 |
9 |
10 | ## Quick Start
11 |
12 | 1. Create a folder called `App` within this Xcode project
13 | 2. Copy your canvas application into the `App` folder
14 | 3. Ensure you have at least 1 file named `index.js`
15 | 4. Build the Xcode project
16 |
17 | For an example application, copy `./index.js` into the `App` folder.
18 |
19 | ## Recent Breaking Changes
20 |
21 | ### 2016-06-23 - Typed Arrays are fast again!
22 |
23 | The JSC version that comes with iOS 10 provides a new API to read and write Typed Arrays in native code. The workaround from previous versions is not needed anymore
24 |
25 | ### 2016-06-23 - Removed iAds
26 |
27 | Apple will discontinue its own iAd Network on June 30. The iAd-Banner API has been removed from Ejecta.
28 |
29 | ### 2015-12-12 - Use OS provided JavaScriptCore library
30 |
31 | Since [08741b4](https://github.com/phoboslab/Ejecta/commit/08741b4489ff945b117ebee0333c7eb7a6177c2e) Ejecta uses the JSC lib provided by iOS and tvOS instead of bundling a custom fork of it. This mainly means two things: The resulting binary will be much smaller and reading/writing of Typed Arrays is much slower.
32 |
33 | This only affects WebGL and the `get/setImageData()` functions for Canvas2D. Some tests indicate that the performance is still good enough for most WebGL games. On 64bit systems it's highly optimized to take about 7ms/Megabyte for reading and about 20ms/Megabyte for writing. It's much slower on 32bit systems, though.
34 |
35 | More info about this change can be found [in my blog](http://phoboslab.org/log/2015/11/the-absolute-worst-way-to-read-typed-array-data-with-javascriptcore). Please comment on [this Webkit bug](https://bugs.webkit.org/show_bug.cgi?id=120112) if you want to have fast Typed Array support again.
36 |
37 | ### 2015-11-27 – Allowed orientation change
38 |
39 | Allowed interface orientations should now be set using the "Device Orientation" checkboxes in the Project's General settings. Ejecta now rotates to all allowed orientations automatically. If the window size changes due to a rotation (i.e. when rotating from landscape to portrait or vice versa), the window's `resize` event is fired.
40 |
41 | ```javascript
42 | window.addEventListener('resize', function() {
43 | // Resize your screen canvas here if needed.
44 | console.log('new window size:', window.innerWidth, window.innerHeight);
45 | });
46 | ```
47 |
48 | ### 2015-11-09 - Moved Antialias (MSAA) settings to getContext options
49 |
50 | The `canvas.MSAAEnabled` and `canvas.MSAASamples` properties have been removed. Instead, you can now specify antialias settings in a separate options object when calling `getContext()`, similar to how it works in a browser. Antialias now works on 2D and WebGL contexts.
51 |
52 | Note that for 2D contexts antialias will have no effect when drawing images, other than slowing down performance. It only makes sense to enable antialias if you draw shapes and paths.
53 |
54 | ```javascript
55 | var gl = canvas.getContext('webgl', {antialias: true, antialiasSamples: 4});
56 |
57 | // Or for 2d contexts
58 |
59 | var ctx = canvas.getContext('2d', {antialias: true, antialiasSamples: 4});
60 | ```
61 |
62 | ### 2015-11-08 - Removed automatic pixel doubling for retina devices
63 |
64 | The Canvas' backing store is now exactly the same size as the `canvas.width` and `canvas.height`. Ejecta *does not* automatically double the internal resolution on retina devices anymore. The `ctx.backingStorePixelRatio` and `canvas.retinaResolutionEnabled` properties as well as the `HD` variants for the `ctx.getImageData`, `ctx.putImageData` and `ctx.createImageData` functions have been removed.
65 |
66 | You can of course still render in retina resolution, by setting the `width` and `height` to the retina resolution while forcing the `style` to scale the canvas to the logical display resolution. This is in line with current browsers.
67 |
68 | ```javascript
69 | canvas.width = window.innerWidth * window.devicePixelRatio;
70 | canvas.height = window.innerHeight * window.devicePixelRatio;
71 | canvas.style.width = window.innerWidth + 'px';
72 | canvas.style.height = window.innerHeight + 'px';
73 |
74 | // For 2D contexts you may want to zoom in afterwards
75 | ctx.scale( window.devicePixelRatio, window.devicePixelRatio );
76 | ```
77 |
78 |
79 | ## WebGL Support
80 |
81 | Ejecta supports WebGL out of the box, alongside with Canvas2D. You can create WebGL textures from Canvas2D instances and also draw WebGL Canvases into 2D Contexts as images. Note that you can't change the "mode" (2D or WebGL) of a Context after it has been created.
82 |
83 |
84 | ## Three.js on iOS with Ejecta
85 |
86 | Ejecta always creates the screen Canvas element for you. You have to hand this Canvas element over to Three.js instead of letting it create its own.
87 |
88 | ```javascript
89 | renderer = new THREE.WebGLRenderer( {canvas: document.getElementById('canvas')} );
90 | ```
91 |
92 | An example App folder with the Three.js [Walt CubeMap demo](http://mrdoob.github.com/three.js/examples/webgl_materials_cubemap.html) can be found here:
93 |
94 | http://phoboslab.org/files/Ejecta-ThreeJS-CubeMap.zip
95 |
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "filename" : "Icon-Large-Back.png",
6 | "scale" : "1x"
7 | }
8 | ],
9 | "info" : {
10 | "version" : 1,
11 | "author" : "xcode"
12 | }
13 | }
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Icon-Large-Back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phoboslab/Ejecta/678a37c2f9425612e8606fb688b9096f9f62a9d6/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Icon-Large-Back.png
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "layers" : [
3 | {
4 | "filename" : "Front.imagestacklayer"
5 | },
6 | {
7 | "filename" : "Back.imagestacklayer"
8 | }
9 | ],
10 | "info" : {
11 | "version" : 1,
12 | "author" : "xcode"
13 | }
14 | }
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "filename" : "Icon-Large-Front.png",
6 | "scale" : "1x"
7 | }
8 | ],
9 | "info" : {
10 | "version" : 1,
11 | "author" : "xcode"
12 | }
13 | }
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Icon-Large-Front.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phoboslab/Ejecta/678a37c2f9425612e8606fb688b9096f9f62a9d6/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Icon-Large-Front.png
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "filename" : "Icon-Back.png",
6 | "scale" : "1x"
7 | }
8 | ],
9 | "info" : {
10 | "version" : 1,
11 | "author" : "xcode"
12 | }
13 | }
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Icon-Back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phoboslab/Ejecta/678a37c2f9425612e8606fb688b9096f9f62a9d6/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Icon-Back.png
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "layers" : [
3 | {
4 | "filename" : "Front.imagestacklayer"
5 | },
6 | {
7 | "filename" : "Middle.imagestacklayer"
8 | },
9 | {
10 | "filename" : "Back.imagestacklayer"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "filename" : "Icon-Front.png",
6 | "scale" : "1x"
7 | }
8 | ],
9 | "info" : {
10 | "version" : 1,
11 | "author" : "xcode"
12 | }
13 | }
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Icon-Front.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phoboslab/Ejecta/678a37c2f9425612e8606fb688b9096f9f62a9d6/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Icon-Front.png
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "filename" : "Icon-Middle.png",
6 | "scale" : "1x"
7 | }
8 | ],
9 | "info" : {
10 | "version" : 1,
11 | "author" : "xcode"
12 | }
13 | }
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Icon-Middle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phoboslab/Ejecta/678a37c2f9425612e8606fb688b9096f9f62a9d6/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Icon-Middle.png
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "assets" : [
3 | {
4 | "size" : "1280x768",
5 | "idiom" : "tv",
6 | "filename" : "App Icon - Large.imagestack",
7 | "role" : "primary-app-icon"
8 | },
9 | {
10 | "size" : "400x240",
11 | "idiom" : "tv",
12 | "filename" : "App Icon - Small.imagestack",
13 | "role" : "primary-app-icon"
14 | },
15 | {
16 | "size" : "1920x720",
17 | "idiom" : "tv",
18 | "filename" : "Top Shelf Image.imageset",
19 | "role" : "top-shelf-image"
20 | }
21 | ],
22 | "info" : {
23 | "version" : 1,
24 | "author" : "xcode"
25 | }
26 | }
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "filename" : "Top-Shelf.png",
6 | "scale" : "1x"
7 | }
8 | ],
9 | "info" : {
10 | "version" : 1,
11 | "author" : "xcode"
12 | }
13 | }
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Top-Shelf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phoboslab/Ejecta/678a37c2f9425612e8606fb688b9096f9f62a9d6/Resources/Assets-TV.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Top-Shelf.png
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "orientation" : "landscape",
5 | "idiom" : "tv",
6 | "filename" : "LaunchScreen-TV.png",
7 | "extent" : "full-screen",
8 | "minimum-system-version" : "9.0",
9 | "scale" : "1x"
10 | }
11 | ],
12 | "info" : {
13 | "version" : 1,
14 | "author" : "xcode"
15 | }
16 | }
--------------------------------------------------------------------------------
/Resources/Assets-TV.xcassets/LaunchImage.launchimage/LaunchScreen-TV.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phoboslab/Ejecta/678a37c2f9425612e8606fb688b9096f9f62a9d6/Resources/Assets-TV.xcassets/LaunchImage.launchimage/LaunchScreen-TV.png
--------------------------------------------------------------------------------
/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "size" : "60x60",
25 | "idiom" : "iphone",
26 | "filename" : "Icon-60@2x.png",
27 | "scale" : "2x"
28 | },
29 | {
30 | "size" : "60x60",
31 | "idiom" : "iphone",
32 | "filename" : "Icon-60@3x.png",
33 | "scale" : "3x"
34 | },
35 | {
36 | "idiom" : "ipad",
37 | "size" : "29x29",
38 | "scale" : "1x"
39 | },
40 | {
41 | "idiom" : "ipad",
42 | "size" : "29x29",
43 | "scale" : "2x"
44 | },
45 | {
46 | "idiom" : "ipad",
47 | "size" : "40x40",
48 | "scale" : "1x"
49 | },
50 | {
51 | "idiom" : "ipad",
52 | "size" : "40x40",
53 | "scale" : "2x"
54 | },
55 | {
56 | "size" : "76x76",
57 | "idiom" : "ipad",
58 | "filename" : "Icon-76.png",
59 | "scale" : "1x"
60 | },
61 | {
62 | "size" : "76x76",
63 | "idiom" : "ipad",
64 | "filename" : "Icon-76@2x.png",
65 | "scale" : "2x"
66 | },
67 | {
68 | "size" : "83.5x83.5",
69 | "idiom" : "ipad",
70 | "filename" : "Icon-83.5@2x.png",
71 | "scale" : "2x"
72 | }
73 | ],
74 | "info" : {
75 | "version" : 1,
76 | "author" : "xcode"
77 | }
78 | }
--------------------------------------------------------------------------------
/Resources/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phoboslab/Ejecta/678a37c2f9425612e8606fb688b9096f9f62a9d6/Resources/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
--------------------------------------------------------------------------------
/Resources/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phoboslab/Ejecta/678a37c2f9425612e8606fb688b9096f9f62a9d6/Resources/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png
--------------------------------------------------------------------------------
/Resources/Assets.xcassets/AppIcon.appiconset/Icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phoboslab/Ejecta/678a37c2f9425612e8606fb688b9096f9f62a9d6/Resources/Assets.xcassets/AppIcon.appiconset/Icon-76.png
--------------------------------------------------------------------------------
/Resources/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phoboslab/Ejecta/678a37c2f9425612e8606fb688b9096f9f62a9d6/Resources/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
--------------------------------------------------------------------------------
/Resources/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phoboslab/Ejecta/678a37c2f9425612e8606fb688b9096f9f62a9d6/Resources/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png
--------------------------------------------------------------------------------
/Resources/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Resources/Info-TV.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleDisplayName
8 | ${PRODUCT_NAME}
9 | CFBundleExecutable
10 | ${EXECUTABLE_NAME}
11 | CFBundleIconFile
12 |
13 | CFBundleIcons
14 |
15 | CFBundleIdentifier
16 | $(PRODUCT_BUNDLE_IDENTIFIER)
17 | CFBundleInfoDictionaryVersion
18 | 6.0
19 | CFBundleName
20 | ${PRODUCT_NAME}
21 | CFBundlePackageType
22 | APPL
23 | CFBundleShortVersionString
24 | 1.0
25 | CFBundleSignature
26 | ????
27 | CFBundleVersion
28 | 1.0
29 | LSRequiresIPhoneOS
30 |
31 | UIInterfaceOrientation
32 | UIInterfaceOrientationPortrait
33 | UIRequiredDeviceCapabilities
34 |
35 | arm64
36 |
37 | UIStatusBarHidden
38 |
39 | UISupportedInterfaceOrientations
40 |
41 | UISupportedInterfaceOrientations~ipad
42 |
43 | UIViewControllerBasedStatusBarAppearance
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleDisplayName
8 | ${PRODUCT_NAME}
9 | CFBundleExecutable
10 | ${EXECUTABLE_NAME}
11 | CFBundleIconFile
12 |
13 | CFBundleIcons
14 |
15 | CFBundleIdentifier
16 | $(PRODUCT_BUNDLE_IDENTIFIER)
17 | CFBundleInfoDictionaryVersion
18 | 6.0
19 | CFBundleName
20 | ${PRODUCT_NAME}
21 | CFBundlePackageType
22 | APPL
23 | CFBundleShortVersionString
24 | 1.0
25 | CFBundleSignature
26 | ????
27 | CFBundleVersion
28 | 1.0
29 | LSRequiresIPhoneOS
30 |
31 | UILaunchStoryboardName
32 | LaunchScreen
33 | UIRequiresFullScreen
34 |
35 | UIStatusBarHidden
36 |
37 | UISupportedInterfaceOrientations
38 |
39 | UIInterfaceOrientationPortrait
40 |
41 | UISupportedInterfaceOrientations~ipad
42 |
43 | UIInterfaceOrientationPortrait
44 | UIInterfaceOrientationPortraitUpsideDown
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Resources/LaunchScreen-Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phoboslab/Ejecta/678a37c2f9425612e8606fb688b9096f9f62a9d6/Resources/LaunchScreen-Logo.png
--------------------------------------------------------------------------------
/Resources/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/Source/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "EJAppViewController.h"
3 |
4 | @interface AppDelegate : NSObject
5 |
6 | @property (nonatomic, retain) UIWindow *window;
7 | @end
8 |
9 |
--------------------------------------------------------------------------------
/Source/AppDelegate.m:
--------------------------------------------------------------------------------
1 |
2 | #import "AppDelegate.h"
3 | #import "EJJavaScriptView.h"
4 | @implementation AppDelegate
5 | @synthesize window;
6 |
7 | #pragma mark -
8 | #pragma mark Application lifecycle
9 |
10 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
11 |
12 | // Optionally set the idle timer disabled, this prevents the device from sleep when
13 | // not being interacted with by touch. ie. games with motion control.
14 | application.idleTimerDisabled = YES;
15 |
16 | window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
17 | [self loadViewControllerWithScriptAtPath:@"index.js"];
18 |
19 | [window makeKeyAndVisible];
20 | return YES;
21 | }
22 |
23 | - (void)loadViewControllerWithScriptAtPath:(NSString *)path {
24 | // Release any previous ViewController
25 | window.rootViewController = nil;
26 |
27 | EJAppViewController *vc = [[EJAppViewController alloc] initWithScriptAtPath:path];
28 | window.rootViewController = vc;
29 | [vc release];
30 | }
31 |
32 |
33 | #pragma mark -
34 | #pragma mark Memory management
35 |
36 | - (void)dealloc {
37 | window.rootViewController = nil;
38 | [window release];
39 | [super dealloc];
40 | }
41 |
42 |
43 | @end
44 |
--------------------------------------------------------------------------------
/Source/Ejecta/EJAppViewController.h:
--------------------------------------------------------------------------------
1 | // Well, this is actually the place where most of the stuff from the
2 | // EJJavaScriptView should happen, if we'd follow the Model-View-Controller
3 | // (MVC) methodology.
4 |
5 | // Since the EJJavaScriptView is so tightly coupled to the JS execution and run
6 | // loop, the View Controller here is mostly an empty shell that just
7 | // instantiates a EJJavaScriptView, but does little else.
8 |
9 | #import
10 |
11 | @interface EJAppViewController : UIViewController {
12 | NSString *path;
13 | }
14 |
15 | - (id)initWithScriptAtPath:(NSString *)pathp;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/Source/Ejecta/EJAppViewController.m:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "EJAppViewController.h"
4 | #import "EJJavaScriptView.h"
5 |
6 | @implementation EJAppViewController
7 |
8 | - (id)initWithScriptAtPath:(NSString *)pathp {
9 | if( self = [super init] ) {
10 | path = [pathp retain];
11 | }
12 | return self;
13 | }
14 |
15 | - (void)dealloc {
16 | self.view = nil;
17 | [path release];
18 | [super dealloc];
19 | }
20 |
21 | - (void)didReceiveMemoryWarning {
22 | [(EJJavaScriptView *)self.view clearCaches];
23 | [super didReceiveMemoryWarning];
24 | }
25 |
26 | - (void)loadView {
27 | CGRect frame = UIScreen.mainScreen.bounds;
28 |
29 | EJJavaScriptView *view = [[EJJavaScriptView alloc] initWithFrame:frame];
30 | self.view = view;
31 |
32 | [view loadScriptAtPath:path];
33 | [view release];
34 | }
35 |
36 | @end
37 |
--------------------------------------------------------------------------------
/Source/Ejecta/EJAudio/EJAudioSource.h:
--------------------------------------------------------------------------------
1 | // The protocol that all Audio Source types have to conform with in order to
2 | // be used as the source for an EJBindingAudio element.
3 |
4 | #import
5 |
6 | @protocol EJAudioSourceDelegate;
7 | @protocol EJAudioSource
8 |
9 | - (id)initWithPath:(NSString *)path;
10 | - (void)play;
11 | - (void)pause;
12 | - (void)setLooping:(BOOL)loop;
13 | - (void)setVolume:(float)volume;
14 | - (void)setPlaybackRate:(float)playbackRate;
15 |
16 | @property (nonatomic) float duration;
17 | @property (nonatomic) float currentTime;
18 | @property (nonatomic, assign) NSObject *delegate;
19 |
20 | @end
21 |
22 | @protocol EJAudioSourceDelegate
23 | - (void)sourceDidFinishPlaying:(NSObject *)source;
24 | @end
25 |
--------------------------------------------------------------------------------
/Source/Ejecta/EJAudio/EJAudioSourceAVAudio.h:
--------------------------------------------------------------------------------
1 | // An Audio Source backed by the iOS native AVAudioPlayer. An instance of this
2 | // class can be used as the source of a EJBindingAudio element.
3 |
4 | // AVAudioPlayer supports streaming from a file, so that this type of source
5 | // does not need much memory. However, starting playback can have a bit of lag.
6 |
7 | // Typically, you want this type of Audio Source for music or background ambient
8 | // sounds, but not for sound effects that need tight timing.
9 |
10 | #import
11 | #import
12 |
13 | #import "EJAudioSource.h"
14 |
15 | @interface EJAudioSourceAVAudio : NSObject {
16 | NSString *path;
17 | AVAudioPlayer *player;
18 | NSObject *delegate;
19 | }
20 |
21 | @property (nonatomic) float currentTime;
22 | @property (nonatomic) float duration;
23 | @property (nonatomic, assign) NSObject *delegate;
24 |
25 | @end
26 |
--------------------------------------------------------------------------------
/Source/Ejecta/EJAudio/EJAudioSourceAVAudio.m:
--------------------------------------------------------------------------------
1 | #import "EJAudioSourceAVAudio.h"
2 |
3 |
4 | @implementation EJAudioSourceAVAudio
5 |
6 | @synthesize delegate;
7 |
8 | - (id)initWithPath:(NSString *)pathp {
9 | if( self = [super init] ) {
10 | path = [pathp retain];
11 | player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:nil];
12 | player.delegate = self;
13 | }
14 | return self;
15 | }
16 |
17 | - (void)dealloc {
18 | [path release];
19 | [player release];
20 |
21 | [super dealloc];
22 | }
23 |
24 | - (void)play {
25 | [player play];
26 | }
27 |
28 | - (void)pause {
29 | [player pause];
30 | }
31 |
32 | - (void)setLooping:(BOOL)loop {
33 | player.numberOfLoops = loop ? -1 : 0;
34 | }
35 |
36 | - (void)setVolume:(float)volume {
37 | player.volume = volume;
38 | }
39 |
40 | - (void)setPlaybackRate:(float)playbackRate {
41 | player.enableRate = YES;
42 | player.rate = playbackRate;
43 | }
44 |
45 | - (float)currentTime {
46 | return player.currentTime;
47 | }
48 |
49 | - (void)setCurrentTime:(float)time {
50 | player.currentTime = time;
51 | }
52 |
53 | - (float)duration {
54 | return player.duration;
55 | }
56 |
57 | - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
58 | [delegate sourceDidFinishPlaying:self];
59 | }
60 |
61 | @end
62 |
--------------------------------------------------------------------------------
/Source/Ejecta/EJAudio/EJAudioSourceOpenAL.h:
--------------------------------------------------------------------------------
1 | // An Audio Source backed by OpenAL. Several instances of AudioSourceOpenAL
2 | // may share the same underlying OpenALBuffer instance.
3 |
4 | // OpenAL sources are loaded fully into memory but provide lower latency
5 | // and "rapid fire" playback in contrast to AVAudio sources.
6 |
7 | #import
8 |
9 | #import
10 | #import
11 |
12 | #import "EJAudioSource.h"
13 | #import "EJSharedOpenALManager.h"
14 | #import "EJOpenALBuffer.h"
15 |
16 | @interface EJAudioSourceOpenAL : NSObject {
17 | NSString *path;
18 | unsigned int sourceId;
19 | EJOpenALBuffer *buffer;
20 | BOOL looping;
21 |
22 | NSTimer *endTimer;
23 | NSObject *delegate;
24 | }
25 |
26 | @property (nonatomic) float currentTime;
27 | @property (nonatomic) float duration;
28 | @property (nonatomic, assign) NSObject *delegate;
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/Source/Ejecta/EJAudio/EJAudioSourceOpenAL.m:
--------------------------------------------------------------------------------
1 | #import "EJAudioSourceOpenAL.h"
2 | #import "EJAppViewController.h"
3 |
4 | @implementation EJAudioSourceOpenAL
5 |
6 | @synthesize delegate;
7 |
8 | - (id)initWithPath:(NSString *)pathp {
9 | if( self = [super init] ) {
10 | path = [pathp retain];
11 |
12 | buffer = [[EJOpenALBuffer cachedBufferWithPath:pathp] retain];
13 |
14 | alGenSources(1, &sourceId);
15 | alSourcei(sourceId, AL_BUFFER, buffer.bufferId);
16 | alSourcef(sourceId, AL_PITCH, 1.0f);
17 | alSourcef(sourceId, AL_GAIN, 1.0f);
18 | }
19 | return self;
20 | }
21 |
22 | - (void)dealloc {
23 | if( sourceId ) {
24 | alDeleteSources(1, &sourceId);
25 | }
26 |
27 | [buffer release];
28 | [path release];
29 | [endTimer invalidate];
30 |
31 | [super dealloc];
32 | }
33 |
34 | - (void)play {
35 | alSourcePlay( sourceId );
36 |
37 | [endTimer invalidate];
38 |
39 | float targetTime = buffer.duration - self.currentTime;
40 | endTimer = [NSTimer scheduledTimerWithTimeInterval:targetTime
41 | target:self selector:@selector(ended:) userInfo:nil repeats:NO];
42 | }
43 |
44 | - (void)pause {
45 | alSourceStop( sourceId );
46 | [endTimer invalidate];
47 | endTimer = nil;
48 | }
49 |
50 | - (void)setLooping:(BOOL)loop {
51 | looping = loop;
52 | alSourcei( sourceId, AL_LOOPING, loop ? AL_TRUE : AL_FALSE );
53 | }
54 |
55 | - (void)setVolume:(float)volume {
56 | alSourcef( sourceId, AL_GAIN, volume );
57 | }
58 |
59 | - (void)setPlaybackRate:(float)playbackRate {
60 | alSourcef( sourceId, AL_PITCH, playbackRate);
61 | }
62 |
63 | - (float)currentTime {
64 | float time;
65 | alGetSourcef( sourceId, AL_SEC_OFFSET, &time );
66 | return time;
67 | }
68 |
69 | - (void)setCurrentTime:(float)time {
70 | alSourcef( sourceId, AL_SEC_OFFSET, time );
71 | }
72 |
73 | - (float)duration {
74 | return buffer.duration;
75 | }
76 |
77 | - (void)ended:(NSTimer *)timer {
78 | endTimer = nil;
79 | if( !looping ) {
80 | [delegate sourceDidFinishPlaying:self];
81 | }
82 | }
83 |
84 | @end
85 |
--------------------------------------------------------------------------------
/Source/Ejecta/EJAudio/EJBindingAudio.h:
--------------------------------------------------------------------------------
1 | // This provides an