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