├── .gitignore ├── Default-568h@2x.png ├── README.md ├── WebGLBrowser.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── WebGLBrowser ├── GLBAppDelegate.h ├── GLBAppDelegate.m ├── GLBDetailViewController.h ├── GLBDetailViewController.m ├── GLBMasterViewController.h ├── GLBMasterViewController.m ├── WebGLBrowser-Info.plist ├── WebGLBrowser-Prefix.pch ├── WebGLBrowser.xcdatamodeld │ ├── .xccurrentversion │ └── WebGLBrowser.xcdatamodel │ │ └── contents ├── en.lproj │ ├── GLBDetailViewController_iPad.xib │ ├── GLBDetailViewController_iPhone.xib │ ├── GLBMasterViewController_iPad.xib │ ├── GLBMasterViewController_iPhone.xib │ └── InfoPlist.strings └── main.m ├── icon-114.png ├── icon-144.png ├── icon-57.png └── icon-72.png /.gitignore: -------------------------------------------------------------------------------- 1 | xcuserdata 2 | 3 | -------------------------------------------------------------------------------- /Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benvanik/WebGLBrowser/df16037b3890bbee2aa65063bd47087943116b75/Default-568h@2x.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WebGLBrowser - an iOS browser shim with WebGL 2 | ======== 3 | 4 | This is a simple wrapper around UIWebView, the built-in WebKit-based browser that ships as part of iOS. It adds 5 | some bookmarking and other features handy for testing, and enables the WebGL functionality that's in the iOS 6 | WebKit available to iAds. You can't ship anything with this, but you can test your code and it runs well enough 7 | to start developing touch-enabled 3D web apps. Enjoy! 8 | 9 | WTF? 10 | --------- 11 | 12 | Early last year [James Darpinian](https://twitter.com/modeless) discovered the hidden property required to get WebGL 13 | toggled on in UIWebView on iOS. We hacked together some shims that made it possible to run WebGL pages and although 14 | the underlying WebKit implementation of WebGL had some issues it was good enough for testing. 15 | 16 | Flash forward to today: I lost the shim I had written and wanted to test some WebGL content on a touch device. I 17 | found a great article by [Nathan de Vries](http://atnan.com/blog/2011/11/03/enabling-and-using-webgl-on-ios/) that 18 | goes in depth through the process James did so long ago and it inspired me to whip up something a bit more useful 19 | than what I had before. 20 | 21 | So, here it is. It's super hacky, unreleasable on the App Store (due to the use of private APIs), and since WebGL 22 | is not officially supported on iOS often fails in interesting ways. Unfortunately the performance characteristics 23 | of this shim are not indicative of what Mobile Safari would behave as with WebGL content (when/if it's enabled), as 24 | Apple has disabled their JIT for apps using UIWebView. GPU-heavy apps should be fairly close to real perf, though, 25 | despite some likely inefficient compositing capping the framerate at just under $ass fps. 26 | 27 | In theory, one can grab the latest Xcode with iOS SDK, build, and run on their device with no changes. Should work 28 | on both iPhone and iPad with iOS5+. 29 | 30 | Features 31 | --------- 32 | 33 | * Add bookmarks for quickly returning to a URL 34 | * Fullscreen mode (hit the home button and reopen to return to normal mode) 35 | * WebGL! 36 | * AirPlay mirroring works (there's a bit of a lag, but the framerate is good) 37 | * Remote inspecting when running in the iOS simulator 38 | 39 | Inspecting Pages 40 | --------- 41 | 42 | Using [Nathan de Vries](http://atnan.com/blog/2011/11/17/enabling-remote-debugging-via-private-apis-in-mobile-safari/)' 43 | awesome find, you can now run the WebKit Inspector against pages loaded in the app. Unfortunately you can't inspect 44 | pages running on device yet, but it's a full featured inspector (debugging/networking/sampling/etc) and it should match 45 | device features. 46 | 47 | To use, launch the app in either the iPhone or iPad simulator, navigate to a page, and then open http://localhost:9999 48 | in Safari (no other browser works). 49 | 50 | Notes 51 | --------- 52 | 53 | * Q3BSP runs at 60FPS on my iPhone 4S and iPad 2 - nice! 54 | * WebGL Aquarium is slow (20-25FPS) - likely due to some scaling tricks used 55 | * Avoid scaling your canvas with CSS or doing canvas->canvas draws 56 | * Some demos don't work because people are using platform detection 57 | * Use feature detection! 58 | * JSON parsing in mobile WebKit is slow - demos that parse large JSON blobs for models/etc will take awhile to load 59 | 60 | WebGL Conformance Tests 61 | --------- 62 | I ran a few of the conformance test suites. Unfortunately the test runner hides the output at the end, so I can't 63 | see all of the results. Some things the tests highlight that one should watch out for when testing their own code: 64 | 65 | * 1.0.0: 5626 of 5649 passed, 1 timed out 66 | * The video test timed out 67 | * 1.0.1: 68 | * drawingBufferWidth/Height are undefined 69 | * Many programs fail to link, causing many test errors 70 | * Looks like there are some deletion errors (not properly clearing state/cleaning bindings) 71 | * Long variable name handling in shaders ir broken 72 | * gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS) == null 73 | * gl.getProgramParameter(gl.LINK_STATUS) == true | null (not false!) - likely the same for shaders 74 | * CORS handling is broken (looks like it doesn't check at all) 75 | * pixelStorei has issues (likely being ignored) 76 | * NPOT cubemaps broken 77 | * Uploading custom mip levels is brokenish 78 | * TypedArrays has a few errors with wrapping ArrayBuffer in ArrayBufferView (and no Float64Array) 79 | * conformance/typedarrays/array-unit-tests.html 80 | * copyTexImage2D has issues 81 | * 1.0.2: Crash! -------------------------------------------------------------------------------- /WebGLBrowser.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 90087429160E3EA9002719DA /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 90087428160E3EA9002719DA /* Default-568h@2x.png */; }; 11 | 902FAA891512A68E0002FD00 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 902FAA881512A68E0002FD00 /* UIKit.framework */; }; 12 | 902FAA8B1512A68E0002FD00 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 902FAA8A1512A68E0002FD00 /* Foundation.framework */; }; 13 | 902FAA8D1512A68E0002FD00 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 902FAA8C1512A68E0002FD00 /* CoreGraphics.framework */; }; 14 | 902FAA8F1512A68E0002FD00 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 902FAA8E1512A68E0002FD00 /* CoreData.framework */; }; 15 | 902FAA951512A68E0002FD00 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 902FAA931512A68E0002FD00 /* InfoPlist.strings */; }; 16 | 902FAA971512A68E0002FD00 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 902FAA961512A68E0002FD00 /* main.m */; }; 17 | 902FAA9B1512A68E0002FD00 /* GLBAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 902FAA9A1512A68E0002FD00 /* GLBAppDelegate.m */; }; 18 | 902FAA9E1512A68E0002FD00 /* WebGLBrowser.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 902FAA9C1512A68E0002FD00 /* WebGLBrowser.xcdatamodeld */; }; 19 | 902FAAA11512A68E0002FD00 /* GLBMasterViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 902FAAA01512A68E0002FD00 /* GLBMasterViewController.m */; }; 20 | 902FAAA41512A68E0002FD00 /* GLBDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 902FAAA31512A68E0002FD00 /* GLBDetailViewController.m */; }; 21 | 902FAAA71512A68E0002FD00 /* GLBMasterViewController_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 902FAAA51512A68E0002FD00 /* GLBMasterViewController_iPhone.xib */; }; 22 | 902FAAAA1512A68E0002FD00 /* GLBMasterViewController_iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 902FAAA81512A68E0002FD00 /* GLBMasterViewController_iPad.xib */; }; 23 | 902FAAAD1512A68E0002FD00 /* GLBDetailViewController_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 902FAAAB1512A68E0002FD00 /* GLBDetailViewController_iPhone.xib */; }; 24 | 902FAAB01512A68E0002FD00 /* GLBDetailViewController_iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 902FAAAE1512A68E0002FD00 /* GLBDetailViewController_iPad.xib */; }; 25 | 90ED1A131513BC0300E613C5 /* icon-57.png in Resources */ = {isa = PBXBuildFile; fileRef = 90ED1A0F1513BC0300E613C5 /* icon-57.png */; }; 26 | 90ED1A141513BC0300E613C5 /* icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = 90ED1A101513BC0300E613C5 /* icon-72.png */; }; 27 | 90ED1A151513BC0300E613C5 /* icon-114.png in Resources */ = {isa = PBXBuildFile; fileRef = 90ED1A111513BC0300E613C5 /* icon-114.png */; }; 28 | 90ED1A161513BC0300E613C5 /* icon-144.png in Resources */ = {isa = PBXBuildFile; fileRef = 90ED1A121513BC0300E613C5 /* icon-144.png */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 90087428160E3EA9002719DA /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 33 | 902FAA841512A68E0002FD00 /* WebGLBrowser.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WebGLBrowser.app; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 902FAA881512A68E0002FD00 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 35 | 902FAA8A1512A68E0002FD00 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 36 | 902FAA8C1512A68E0002FD00 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 37 | 902FAA8E1512A68E0002FD00 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 38 | 902FAA921512A68E0002FD00 /* WebGLBrowser-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "WebGLBrowser-Info.plist"; sourceTree = ""; }; 39 | 902FAA941512A68E0002FD00 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 40 | 902FAA961512A68E0002FD00 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 41 | 902FAA981512A68E0002FD00 /* WebGLBrowser-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "WebGLBrowser-Prefix.pch"; sourceTree = ""; }; 42 | 902FAA991512A68E0002FD00 /* GLBAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GLBAppDelegate.h; sourceTree = ""; }; 43 | 902FAA9A1512A68E0002FD00 /* GLBAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GLBAppDelegate.m; sourceTree = ""; }; 44 | 902FAA9D1512A68E0002FD00 /* WebGLBrowser.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = WebGLBrowser.xcdatamodel; sourceTree = ""; }; 45 | 902FAA9F1512A68E0002FD00 /* GLBMasterViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GLBMasterViewController.h; sourceTree = ""; }; 46 | 902FAAA01512A68E0002FD00 /* GLBMasterViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GLBMasterViewController.m; sourceTree = ""; }; 47 | 902FAAA21512A68E0002FD00 /* GLBDetailViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GLBDetailViewController.h; sourceTree = ""; }; 48 | 902FAAA31512A68E0002FD00 /* GLBDetailViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GLBDetailViewController.m; sourceTree = ""; }; 49 | 902FAAA61512A68E0002FD00 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/GLBMasterViewController_iPhone.xib; sourceTree = ""; }; 50 | 902FAAA91512A68E0002FD00 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/GLBMasterViewController_iPad.xib; sourceTree = ""; }; 51 | 902FAAAC1512A68E0002FD00 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/GLBDetailViewController_iPhone.xib; sourceTree = ""; }; 52 | 902FAAAF1512A68E0002FD00 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/GLBDetailViewController_iPad.xib; sourceTree = ""; }; 53 | 90ED1A0F1513BC0300E613C5 /* icon-57.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-57.png"; sourceTree = ""; }; 54 | 90ED1A101513BC0300E613C5 /* icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-72.png"; sourceTree = ""; }; 55 | 90ED1A111513BC0300E613C5 /* icon-114.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-114.png"; sourceTree = ""; }; 56 | 90ED1A121513BC0300E613C5 /* icon-144.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-144.png"; sourceTree = ""; }; 57 | /* End PBXFileReference section */ 58 | 59 | /* Begin PBXFrameworksBuildPhase section */ 60 | 902FAA811512A68E0002FD00 /* Frameworks */ = { 61 | isa = PBXFrameworksBuildPhase; 62 | buildActionMask = 2147483647; 63 | files = ( 64 | 902FAA891512A68E0002FD00 /* UIKit.framework in Frameworks */, 65 | 902FAA8B1512A68E0002FD00 /* Foundation.framework in Frameworks */, 66 | 902FAA8D1512A68E0002FD00 /* CoreGraphics.framework in Frameworks */, 67 | 902FAA8F1512A68E0002FD00 /* CoreData.framework in Frameworks */, 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | /* End PBXFrameworksBuildPhase section */ 72 | 73 | /* Begin PBXGroup section */ 74 | 902FAA791512A68E0002FD00 = { 75 | isa = PBXGroup; 76 | children = ( 77 | 90087428160E3EA9002719DA /* Default-568h@2x.png */, 78 | 902FAA901512A68E0002FD00 /* WebGLBrowser */, 79 | 90ED1A0D1513BBE300E613C5 /* Resources */, 80 | 90ED1A0E1513BBF300E613C5 /* Icons */, 81 | 902FAA871512A68E0002FD00 /* Frameworks */, 82 | 902FAA851512A68E0002FD00 /* Products */, 83 | ); 84 | sourceTree = ""; 85 | }; 86 | 902FAA851512A68E0002FD00 /* Products */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 902FAA841512A68E0002FD00 /* WebGLBrowser.app */, 90 | ); 91 | name = Products; 92 | sourceTree = ""; 93 | }; 94 | 902FAA871512A68E0002FD00 /* Frameworks */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 902FAA881512A68E0002FD00 /* UIKit.framework */, 98 | 902FAA8A1512A68E0002FD00 /* Foundation.framework */, 99 | 902FAA8C1512A68E0002FD00 /* CoreGraphics.framework */, 100 | 902FAA8E1512A68E0002FD00 /* CoreData.framework */, 101 | ); 102 | name = Frameworks; 103 | sourceTree = ""; 104 | }; 105 | 902FAA901512A68E0002FD00 /* WebGLBrowser */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 902FAA991512A68E0002FD00 /* GLBAppDelegate.h */, 109 | 902FAA9A1512A68E0002FD00 /* GLBAppDelegate.m */, 110 | 902FAA9F1512A68E0002FD00 /* GLBMasterViewController.h */, 111 | 902FAAA01512A68E0002FD00 /* GLBMasterViewController.m */, 112 | 902FAAA21512A68E0002FD00 /* GLBDetailViewController.h */, 113 | 902FAAA31512A68E0002FD00 /* GLBDetailViewController.m */, 114 | 902FAAA51512A68E0002FD00 /* GLBMasterViewController_iPhone.xib */, 115 | 902FAAA81512A68E0002FD00 /* GLBMasterViewController_iPad.xib */, 116 | 902FAAAB1512A68E0002FD00 /* GLBDetailViewController_iPhone.xib */, 117 | 902FAAAE1512A68E0002FD00 /* GLBDetailViewController_iPad.xib */, 118 | 902FAA9C1512A68E0002FD00 /* WebGLBrowser.xcdatamodeld */, 119 | 902FAA911512A68E0002FD00 /* Supporting Files */, 120 | ); 121 | path = WebGLBrowser; 122 | sourceTree = ""; 123 | }; 124 | 902FAA911512A68E0002FD00 /* Supporting Files */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 902FAA921512A68E0002FD00 /* WebGLBrowser-Info.plist */, 128 | 902FAA931512A68E0002FD00 /* InfoPlist.strings */, 129 | 902FAA961512A68E0002FD00 /* main.m */, 130 | 902FAA981512A68E0002FD00 /* WebGLBrowser-Prefix.pch */, 131 | ); 132 | name = "Supporting Files"; 133 | sourceTree = ""; 134 | }; 135 | 90ED1A0D1513BBE300E613C5 /* Resources */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | ); 139 | name = Resources; 140 | sourceTree = ""; 141 | }; 142 | 90ED1A0E1513BBF300E613C5 /* Icons */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 90ED1A0F1513BC0300E613C5 /* icon-57.png */, 146 | 90ED1A101513BC0300E613C5 /* icon-72.png */, 147 | 90ED1A111513BC0300E613C5 /* icon-114.png */, 148 | 90ED1A121513BC0300E613C5 /* icon-144.png */, 149 | ); 150 | name = Icons; 151 | sourceTree = ""; 152 | }; 153 | /* End PBXGroup section */ 154 | 155 | /* Begin PBXNativeTarget section */ 156 | 902FAA831512A68E0002FD00 /* WebGLBrowser */ = { 157 | isa = PBXNativeTarget; 158 | buildConfigurationList = 902FAAB31512A68F0002FD00 /* Build configuration list for PBXNativeTarget "WebGLBrowser" */; 159 | buildPhases = ( 160 | 902FAA801512A68E0002FD00 /* Sources */, 161 | 902FAA811512A68E0002FD00 /* Frameworks */, 162 | 902FAA821512A68E0002FD00 /* Resources */, 163 | ); 164 | buildRules = ( 165 | ); 166 | dependencies = ( 167 | ); 168 | name = WebGLBrowser; 169 | productName = WebGLBrowser; 170 | productReference = 902FAA841512A68E0002FD00 /* WebGLBrowser.app */; 171 | productType = "com.apple.product-type.application"; 172 | }; 173 | /* End PBXNativeTarget section */ 174 | 175 | /* Begin PBXProject section */ 176 | 902FAA7B1512A68E0002FD00 /* Project object */ = { 177 | isa = PBXProject; 178 | attributes = { 179 | LastUpgradeCheck = 0430; 180 | ORGANIZATIONNAME = "Ben Vanik"; 181 | }; 182 | buildConfigurationList = 902FAA7E1512A68E0002FD00 /* Build configuration list for PBXProject "WebGLBrowser" */; 183 | compatibilityVersion = "Xcode 3.2"; 184 | developmentRegion = English; 185 | hasScannedForEncodings = 0; 186 | knownRegions = ( 187 | en, 188 | ); 189 | mainGroup = 902FAA791512A68E0002FD00; 190 | productRefGroup = 902FAA851512A68E0002FD00 /* Products */; 191 | projectDirPath = ""; 192 | projectRoot = ""; 193 | targets = ( 194 | 902FAA831512A68E0002FD00 /* WebGLBrowser */, 195 | ); 196 | }; 197 | /* End PBXProject section */ 198 | 199 | /* Begin PBXResourcesBuildPhase section */ 200 | 902FAA821512A68E0002FD00 /* Resources */ = { 201 | isa = PBXResourcesBuildPhase; 202 | buildActionMask = 2147483647; 203 | files = ( 204 | 902FAA951512A68E0002FD00 /* InfoPlist.strings in Resources */, 205 | 902FAAA71512A68E0002FD00 /* GLBMasterViewController_iPhone.xib in Resources */, 206 | 902FAAAA1512A68E0002FD00 /* GLBMasterViewController_iPad.xib in Resources */, 207 | 902FAAAD1512A68E0002FD00 /* GLBDetailViewController_iPhone.xib in Resources */, 208 | 902FAAB01512A68E0002FD00 /* GLBDetailViewController_iPad.xib in Resources */, 209 | 90ED1A131513BC0300E613C5 /* icon-57.png in Resources */, 210 | 90ED1A141513BC0300E613C5 /* icon-72.png in Resources */, 211 | 90ED1A151513BC0300E613C5 /* icon-114.png in Resources */, 212 | 90ED1A161513BC0300E613C5 /* icon-144.png in Resources */, 213 | 90087429160E3EA9002719DA /* Default-568h@2x.png in Resources */, 214 | ); 215 | runOnlyForDeploymentPostprocessing = 0; 216 | }; 217 | /* End PBXResourcesBuildPhase section */ 218 | 219 | /* Begin PBXSourcesBuildPhase section */ 220 | 902FAA801512A68E0002FD00 /* Sources */ = { 221 | isa = PBXSourcesBuildPhase; 222 | buildActionMask = 2147483647; 223 | files = ( 224 | 902FAA971512A68E0002FD00 /* main.m in Sources */, 225 | 902FAA9B1512A68E0002FD00 /* GLBAppDelegate.m in Sources */, 226 | 902FAA9E1512A68E0002FD00 /* WebGLBrowser.xcdatamodeld in Sources */, 227 | 902FAAA11512A68E0002FD00 /* GLBMasterViewController.m in Sources */, 228 | 902FAAA41512A68E0002FD00 /* GLBDetailViewController.m in Sources */, 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | }; 232 | /* End PBXSourcesBuildPhase section */ 233 | 234 | /* Begin PBXVariantGroup section */ 235 | 902FAA931512A68E0002FD00 /* InfoPlist.strings */ = { 236 | isa = PBXVariantGroup; 237 | children = ( 238 | 902FAA941512A68E0002FD00 /* en */, 239 | ); 240 | name = InfoPlist.strings; 241 | sourceTree = ""; 242 | }; 243 | 902FAAA51512A68E0002FD00 /* GLBMasterViewController_iPhone.xib */ = { 244 | isa = PBXVariantGroup; 245 | children = ( 246 | 902FAAA61512A68E0002FD00 /* en */, 247 | ); 248 | name = GLBMasterViewController_iPhone.xib; 249 | sourceTree = ""; 250 | }; 251 | 902FAAA81512A68E0002FD00 /* GLBMasterViewController_iPad.xib */ = { 252 | isa = PBXVariantGroup; 253 | children = ( 254 | 902FAAA91512A68E0002FD00 /* en */, 255 | ); 256 | name = GLBMasterViewController_iPad.xib; 257 | sourceTree = ""; 258 | }; 259 | 902FAAAB1512A68E0002FD00 /* GLBDetailViewController_iPhone.xib */ = { 260 | isa = PBXVariantGroup; 261 | children = ( 262 | 902FAAAC1512A68E0002FD00 /* en */, 263 | ); 264 | name = GLBDetailViewController_iPhone.xib; 265 | sourceTree = ""; 266 | }; 267 | 902FAAAE1512A68E0002FD00 /* GLBDetailViewController_iPad.xib */ = { 268 | isa = PBXVariantGroup; 269 | children = ( 270 | 902FAAAF1512A68E0002FD00 /* en */, 271 | ); 272 | name = GLBDetailViewController_iPad.xib; 273 | sourceTree = ""; 274 | }; 275 | /* End PBXVariantGroup section */ 276 | 277 | /* Begin XCBuildConfiguration section */ 278 | 902FAAB11512A68E0002FD00 /* Debug */ = { 279 | isa = XCBuildConfiguration; 280 | buildSettings = { 281 | ALWAYS_SEARCH_USER_PATHS = NO; 282 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 283 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 284 | COPY_PHASE_STRIP = NO; 285 | GCC_C_LANGUAGE_STANDARD = gnu99; 286 | GCC_DYNAMIC_NO_PIC = NO; 287 | GCC_OPTIMIZATION_LEVEL = 0; 288 | GCC_PREPROCESSOR_DEFINITIONS = ( 289 | "DEBUG=1", 290 | "$(inherited)", 291 | ); 292 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 293 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 294 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 295 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 296 | GCC_WARN_UNUSED_VARIABLE = YES; 297 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 298 | SDKROOT = iphoneos; 299 | TARGETED_DEVICE_FAMILY = "1,2"; 300 | }; 301 | name = Debug; 302 | }; 303 | 902FAAB21512A68E0002FD00 /* Release */ = { 304 | isa = XCBuildConfiguration; 305 | buildSettings = { 306 | ALWAYS_SEARCH_USER_PATHS = NO; 307 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 308 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 309 | COPY_PHASE_STRIP = YES; 310 | GCC_C_LANGUAGE_STANDARD = gnu99; 311 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 312 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 313 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 314 | GCC_WARN_UNUSED_VARIABLE = YES; 315 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 316 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 317 | SDKROOT = iphoneos; 318 | TARGETED_DEVICE_FAMILY = "1,2"; 319 | VALIDATE_PRODUCT = YES; 320 | }; 321 | name = Release; 322 | }; 323 | 902FAAB41512A68F0002FD00 /* Debug */ = { 324 | isa = XCBuildConfiguration; 325 | buildSettings = { 326 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 327 | GCC_PREFIX_HEADER = "WebGLBrowser/WebGLBrowser-Prefix.pch"; 328 | INFOPLIST_FILE = "WebGLBrowser/WebGLBrowser-Info.plist"; 329 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 330 | PRODUCT_NAME = "$(TARGET_NAME)"; 331 | WRAPPER_EXTENSION = app; 332 | }; 333 | name = Debug; 334 | }; 335 | 902FAAB51512A68F0002FD00 /* Release */ = { 336 | isa = XCBuildConfiguration; 337 | buildSettings = { 338 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 339 | GCC_PREFIX_HEADER = "WebGLBrowser/WebGLBrowser-Prefix.pch"; 340 | INFOPLIST_FILE = "WebGLBrowser/WebGLBrowser-Info.plist"; 341 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 342 | PRODUCT_NAME = "$(TARGET_NAME)"; 343 | WRAPPER_EXTENSION = app; 344 | }; 345 | name = Release; 346 | }; 347 | /* End XCBuildConfiguration section */ 348 | 349 | /* Begin XCConfigurationList section */ 350 | 902FAA7E1512A68E0002FD00 /* Build configuration list for PBXProject "WebGLBrowser" */ = { 351 | isa = XCConfigurationList; 352 | buildConfigurations = ( 353 | 902FAAB11512A68E0002FD00 /* Debug */, 354 | 902FAAB21512A68E0002FD00 /* Release */, 355 | ); 356 | defaultConfigurationIsVisible = 0; 357 | defaultConfigurationName = Release; 358 | }; 359 | 902FAAB31512A68F0002FD00 /* Build configuration list for PBXNativeTarget "WebGLBrowser" */ = { 360 | isa = XCConfigurationList; 361 | buildConfigurations = ( 362 | 902FAAB41512A68F0002FD00 /* Debug */, 363 | 902FAAB51512A68F0002FD00 /* Release */, 364 | ); 365 | defaultConfigurationIsVisible = 0; 366 | defaultConfigurationName = Release; 367 | }; 368 | /* End XCConfigurationList section */ 369 | 370 | /* Begin XCVersionGroup section */ 371 | 902FAA9C1512A68E0002FD00 /* WebGLBrowser.xcdatamodeld */ = { 372 | isa = XCVersionGroup; 373 | children = ( 374 | 902FAA9D1512A68E0002FD00 /* WebGLBrowser.xcdatamodel */, 375 | ); 376 | currentVersion = 902FAA9D1512A68E0002FD00 /* WebGLBrowser.xcdatamodel */; 377 | path = WebGLBrowser.xcdatamodeld; 378 | sourceTree = ""; 379 | versionGroupType = wrapper.xcdatamodel; 380 | }; 381 | /* End XCVersionGroup section */ 382 | }; 383 | rootObject = 902FAA7B1512A68E0002FD00 /* Project object */; 384 | } 385 | -------------------------------------------------------------------------------- /WebGLBrowser.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WebGLBrowser/GLBAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // GLBAppDelegate.h 3 | // WebGLBrowser 4 | // 5 | // Created by Ben Vanik on 3/15/12. 6 | // Copyright (c) 2012 Ben Vanik. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GLBAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext; 16 | @property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel; 17 | @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator; 18 | 19 | - (void)saveContext; 20 | - (NSURL *)applicationDocumentsDirectory; 21 | 22 | @property (strong, nonatomic) UINavigationController *navigationController; 23 | 24 | @property (strong, nonatomic) UISplitViewController *splitViewController; 25 | 26 | - (void)addBookmark:(NSString*)url; 27 | - (void)addBookmark:(NSString*)url withTitle:(NSString*)title; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /WebGLBrowser/GLBAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // GLBAppDelegate.m 3 | // WebGLBrowser 4 | // 5 | // Created by Ben Vanik on 3/15/12. 6 | // Copyright (c) 2012 Ben Vanik. All rights reserved. 7 | // 8 | 9 | #import "GLBAppDelegate.h" 10 | 11 | #import "GLBMasterViewController.h" 12 | 13 | #import "GLBDetailViewController.h" 14 | 15 | @interface GLBAppDelegate () 16 | - (void)addDefaultBookmarks; 17 | @end 18 | 19 | @implementation GLBAppDelegate 20 | 21 | @synthesize window = _window; 22 | @synthesize managedObjectContext = __managedObjectContext; 23 | @synthesize managedObjectModel = __managedObjectModel; 24 | @synthesize persistentStoreCoordinator = __persistentStoreCoordinator; 25 | @synthesize navigationController = _navigationController; 26 | @synthesize splitViewController = _splitViewController; 27 | 28 | - (void)dealloc 29 | { 30 | [_window release]; 31 | [__managedObjectContext release]; 32 | [__managedObjectModel release]; 33 | [__persistentStoreCoordinator release]; 34 | [_navigationController release]; 35 | [_splitViewController release]; 36 | [super dealloc]; 37 | } 38 | 39 | // Unused - prevents warning 40 | - (void)_enableRemoteInspector { 41 | } 42 | 43 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 44 | { 45 | // From Nathan de Vries: 46 | // http://atnan.com/blog/2011/11/17/enabling-remote-debugging-via-private-apis-in-mobile-safari/ 47 | // This will enable remote debugging when running in the iOS simulator 48 | // Launch and navigate to http://localhost:9999 49 | // It does not yet work with devices 50 | // Only enable in the simulator so we don't hurt perf on devices 51 | #if TARGET_IPHONE_SIMULATOR 52 | [NSClassFromString(@"WebView") _enableRemoteInspector]; 53 | #endif 54 | 55 | [self addDefaultBookmarks]; 56 | 57 | self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; 58 | // Override point for customization after application launch. 59 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { 60 | GLBMasterViewController *masterViewController = [[[GLBMasterViewController alloc] initWithNibName:@"GLBMasterViewController_iPhone" bundle:nil] autorelease]; 61 | self.navigationController = [[[UINavigationController alloc] initWithRootViewController:masterViewController] autorelease]; 62 | self.window.rootViewController = self.navigationController; 63 | masterViewController.managedObjectContext = self.managedObjectContext; 64 | } else { 65 | GLBMasterViewController *masterViewController = [[[GLBMasterViewController alloc] initWithNibName:@"GLBMasterViewController_iPad" bundle:nil] autorelease]; 66 | UINavigationController *masterNavigationController = [[[UINavigationController alloc] initWithRootViewController:masterViewController] autorelease]; 67 | 68 | GLBDetailViewController *detailViewController = [[[GLBDetailViewController alloc] initWithNibName:@"GLBDetailViewController_iPad" bundle:nil] autorelease]; 69 | UINavigationController *detailNavigationController = [[[UINavigationController alloc] initWithRootViewController:detailViewController] autorelease]; 70 | 71 | self.splitViewController = [[[UISplitViewController alloc] init] autorelease]; 72 | self.splitViewController.delegate = detailViewController; 73 | self.splitViewController.viewControllers = [NSArray arrayWithObjects:masterNavigationController, detailNavigationController, nil]; 74 | 75 | self.window.rootViewController = self.splitViewController; 76 | masterViewController.detailViewController = detailViewController; 77 | masterViewController.managedObjectContext = self.managedObjectContext; 78 | detailViewController.managedObjectContext = self.managedObjectContext; 79 | } 80 | 81 | [application setStatusBarHidden:YES]; 82 | 83 | [self.window makeKeyAndVisible]; 84 | return YES; 85 | } 86 | 87 | - (void)applicationWillResignActive:(UIApplication *)application 88 | { 89 | /* 90 | Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 91 | Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 92 | */ 93 | } 94 | 95 | - (void)applicationDidEnterBackground:(UIApplication *)application 96 | { 97 | /* 98 | Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 99 | If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 100 | */ 101 | } 102 | 103 | - (void)applicationWillEnterForeground:(UIApplication *)application 104 | { 105 | /* 106 | Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 107 | */ 108 | } 109 | 110 | - (void)applicationDidBecomeActive:(UIApplication *)application 111 | { 112 | /* 113 | Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 114 | */ 115 | } 116 | 117 | - (void)applicationWillTerminate:(UIApplication *)application 118 | { 119 | // Saves changes in the application's managed object context before the application terminates. 120 | [self saveContext]; 121 | } 122 | 123 | - (void)saveContext 124 | { 125 | NSError *error = nil; 126 | NSManagedObjectContext *managedObjectContext = self.managedObjectContext; 127 | if (managedObjectContext != nil) 128 | { 129 | if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) 130 | { 131 | /* 132 | Replace this implementation with code to handle the error appropriately. 133 | 134 | abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 135 | */ 136 | NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 137 | abort(); 138 | } 139 | } 140 | } 141 | 142 | #pragma mark - Core Data stack 143 | 144 | /** 145 | Returns the managed object context for the application. 146 | If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application. 147 | */ 148 | - (NSManagedObjectContext *)managedObjectContext 149 | { 150 | if (__managedObjectContext != nil) 151 | { 152 | return __managedObjectContext; 153 | } 154 | 155 | NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; 156 | if (coordinator != nil) 157 | { 158 | __managedObjectContext = [[NSManagedObjectContext alloc] init]; 159 | [__managedObjectContext setPersistentStoreCoordinator:coordinator]; 160 | } 161 | return __managedObjectContext; 162 | } 163 | 164 | /** 165 | Returns the managed object model for the application. 166 | If the model doesn't already exist, it is created from the application's model. 167 | */ 168 | - (NSManagedObjectModel *)managedObjectModel 169 | { 170 | if (__managedObjectModel != nil) 171 | { 172 | return __managedObjectModel; 173 | } 174 | NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"WebGLBrowser" withExtension:@"momd"]; 175 | __managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; 176 | return __managedObjectModel; 177 | } 178 | 179 | /** 180 | Returns the persistent store coordinator for the application. 181 | If the coordinator doesn't already exist, it is created and the application's store added to it. 182 | */ 183 | - (NSPersistentStoreCoordinator *)persistentStoreCoordinator 184 | { 185 | if (__persistentStoreCoordinator != nil) 186 | { 187 | return __persistentStoreCoordinator; 188 | } 189 | 190 | NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"WebGLBrowser.sqlite"]; 191 | 192 | NSError *error = nil; 193 | __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; 194 | if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) 195 | { 196 | /* 197 | Replace this implementation with code to handle the error appropriately. 198 | 199 | abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 200 | 201 | Typical reasons for an error here include: 202 | * The persistent store is not accessible; 203 | * The schema for the persistent store is incompatible with current managed object model. 204 | Check the error message to determine what the actual problem was. 205 | 206 | 207 | If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory. 208 | 209 | If you encounter schema incompatibility errors during development, you can reduce their frequency by: 210 | * Simply deleting the existing store: 211 | [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil] 212 | 213 | * Performing automatic lightweight migration by passing the following dictionary as the options parameter: 214 | [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil]; 215 | 216 | Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details. 217 | 218 | */ 219 | NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 220 | abort(); 221 | } 222 | 223 | return __persistentStoreCoordinator; 224 | } 225 | 226 | #pragma mark - Application's Documents directory 227 | 228 | /** 229 | Returns the URL to the application's Documents directory. 230 | */ 231 | - (NSURL *)applicationDocumentsDirectory 232 | { 233 | return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; 234 | } 235 | 236 | #pragma mark - Data 237 | 238 | - (void)addDefaultBookmarks { 239 | [self addBookmark:@"http://www.google.com/" 240 | withTitle:@"Google"]; 241 | [self addBookmark:@"http://webglsamples.googlecode.com/hg/aquarium/aquarium.html" 242 | withTitle:@"WebGL Aquarium"]; 243 | [self addBookmark:@"http://learningwebgl.com/lessons/lesson05/index.html" 244 | withTitle:@"Learning WebGL Lesson 05"]; 245 | [self addBookmark:@"http://learningwebgl.com/lessons/lesson16/index.html" 246 | withTitle:@"Learning WebGL Lesson 16"]; 247 | [self addBookmark:@"http://media.tojicode.com/q3bsp/" 248 | withTitle:@"Quake 3 WebGL Demo"]; 249 | [self addBookmark:@"https://cvs.khronos.org/svn/repos/registry/trunk/public/webgl/conformance-suites/1.0.0/webgl-conformance-tests.html" 250 | withTitle:@"WebGL Conformance Test 1.0.0"]; 251 | [self addBookmark:@"https://cvs.khronos.org/svn/repos/registry/trunk/public/webgl/conformance-suites/1.0.1/webgl-conformance-tests.html" 252 | withTitle:@"WebGL Conformance Test 1.0.1"]; 253 | [self addBookmark:@"https://cvs.khronos.org/svn/repos/registry/trunk/public/webgl/sdk/tests/webgl-conformance-tests.html" 254 | withTitle:@"WebGL Conformance Test (Latest)"]; 255 | [self addBookmark:@"http://glsl.heroku.com/" 256 | withTitle:@"GLSL Sandbox"]; 257 | } 258 | 259 | - (void)addBookmark:(NSString*)url 260 | { 261 | [self addBookmark:url withTitle:url]; 262 | } 263 | 264 | - (void)addBookmark:(NSString*)url withTitle:(NSString*)title 265 | { 266 | NSManagedObjectContext *context = [self managedObjectContext]; 267 | NSEntityDescription *entity = [NSEntityDescription entityForName:@"Bookmark" 268 | inManagedObjectContext:context]; 269 | 270 | // Don't allow duplicates - just update title 271 | NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease]; 272 | [request setEntity:entity]; 273 | [request setPredicate:[NSPredicate predicateWithFormat:@"url like %@", url]]; 274 | 275 | NSError *error = nil; 276 | NSArray *fetchedObjects = [context executeFetchRequest:request error:&error]; 277 | 278 | NSManagedObject *obj = nil; 279 | if ([fetchedObjects count]) { 280 | obj = [fetchedObjects objectAtIndex:0]; 281 | } else { 282 | obj = [NSEntityDescription insertNewObjectForEntityForName:[entity name] 283 | inManagedObjectContext:context]; 284 | } 285 | 286 | [obj setValue:title forKey:@"title"]; 287 | [obj setValue:url forKey:@"url"]; 288 | [obj setValue:nil forKey:@"icon"]; 289 | 290 | if (![context save:&error]) { 291 | NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 292 | abort(); 293 | } 294 | } 295 | 296 | @end 297 | -------------------------------------------------------------------------------- /WebGLBrowser/GLBDetailViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GLBDetailViewController.h 3 | // WebGLBrowser 4 | // 5 | // Created by Ben Vanik on 3/15/12. 6 | // Copyright (c) 2012 Ben Vanik. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GLBDetailViewController : UIViewController 12 | 13 | @property (strong, nonatomic) NSManagedObjectContext *managedObjectContext; 14 | 15 | @property (strong, nonatomic) id detailItem; 16 | 17 | @property (strong, nonatomic) IBOutlet UIBarButtonItem* fullScreenButtonItem; 18 | @property (strong, nonatomic) IBOutlet UIBarButtonItem* reloadButtonItem; 19 | @property (strong, nonatomic) IBOutlet UIBarButtonItem* backButtonItem; 20 | @property (strong, nonatomic) IBOutlet UIBarButtonItem* addButtonItem; 21 | @property (strong, nonatomic) IBOutlet UIWebView *webView; 22 | 23 | - (IBAction)fullScreenAction:(id)sender; 24 | - (IBAction)reloadAction:(id)sender; 25 | - (IBAction)backAction:(id)sender; 26 | - (IBAction)addAction:(id)sender; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /WebGLBrowser/GLBDetailViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GLBDetailViewController.m 3 | // WebGLBrowser 4 | // 5 | // Created by Ben Vanik on 3/15/12. 6 | // Copyright (c) 2012 Ben Vanik. All rights reserved. 7 | // 8 | 9 | #import "GLBDetailViewController.h" 10 | 11 | #import "GLBAppDelegate.h" 12 | 13 | @interface UIBackingWebView 14 | - (void)_setWebGLEnabled:(BOOL)value; 15 | @end 16 | 17 | @interface GLBDetailViewController () 18 | @property (strong, nonatomic) UIPopoverController *masterPopoverController; 19 | - (void)configureView; 20 | @end 21 | 22 | @implementation GLBDetailViewController 23 | 24 | @synthesize masterPopoverController = _masterPopoverController; 25 | @synthesize managedObjectContext = __managedObjectContext; 26 | @synthesize detailItem = _detailItem; 27 | @synthesize fullScreenButtonItem = _fullScreenButtonItem; 28 | @synthesize reloadButtonItem = _reloadButtonItem; 29 | @synthesize backButtonItem = _backButtonItem; 30 | @synthesize addButtonItem = _addButtonItem; 31 | @synthesize webView = _webView; 32 | 33 | - (void)dealloc 34 | { 35 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 36 | 37 | [_masterPopoverController release]; 38 | [__managedObjectContext release]; 39 | [_detailItem release]; 40 | [_fullScreenButtonItem release]; 41 | [_reloadButtonItem release]; 42 | [_backButtonItem release]; 43 | [_addButtonItem release]; 44 | [_webView release]; 45 | [super dealloc]; 46 | } 47 | 48 | #pragma mark - Managing the detail item 49 | 50 | - (void)setDetailItem:(id)newDetailItem 51 | { 52 | if (_detailItem != newDetailItem) { 53 | [_detailItem release]; 54 | _detailItem = [newDetailItem retain]; 55 | } 56 | 57 | // Always reload the page, even if the item is the same 58 | [self configureView]; 59 | 60 | if (self.masterPopoverController != nil) { 61 | [self.masterPopoverController dismissPopoverAnimated:YES]; 62 | } 63 | } 64 | 65 | - (void)navigateToURL:(NSString*)target 66 | { 67 | NSURL* url = [NSURL URLWithString:target]; 68 | NSURLRequest* request = [NSURLRequest requestWithURL:url]; 69 | [[self webView] loadRequest:request]; 70 | } 71 | 72 | - (void)configureView 73 | { 74 | // HACK: clear back/forward history 75 | id internalWebView = [[[self webView] _documentView] webView]; 76 | [internalWebView setMaintainsBackForwardList:NO]; 77 | [internalWebView setMaintainsBackForwardList:YES]; 78 | 79 | if ([self detailItem]) { 80 | [self setTitle:@""]; 81 | [self navigateToURL:[self.detailItem valueForKey:@"url"]]; 82 | } else { 83 | [self setTitle:NSLocalizedString(@"Page", @"Page")]; 84 | [self navigateToURL:@"http://www.google.com/"]; 85 | } 86 | } 87 | 88 | - (void)didReceiveMemoryWarning 89 | { 90 | [super didReceiveMemoryWarning]; 91 | // Release any cached data, images, etc that aren't in use. 92 | } 93 | 94 | #pragma mark - Behavior 95 | 96 | - (IBAction)fullScreenAction:(id)sender { 97 | GLBAppDelegate *app = (GLBAppDelegate*)[[UIApplication sharedApplication] delegate]; 98 | 99 | [[self navigationController] setNavigationBarHidden:YES animated:YES]; 100 | 101 | [[app splitViewController] setWantsFullScreenLayout:NO]; 102 | } 103 | 104 | - (IBAction)reloadAction:(id)sender { 105 | [self.webView reload]; 106 | } 107 | 108 | - (IBAction)backAction:(id)sender { 109 | [self.webView goBack]; 110 | } 111 | 112 | - (IBAction)addAction:(id)sender { 113 | NSString* title = [self.webView stringByEvaluatingJavaScriptFromString:@"document.title"]; 114 | NSString* url = [self.webView stringByEvaluatingJavaScriptFromString:@"window.location.toString()"]; 115 | 116 | GLBAppDelegate *app = (GLBAppDelegate*)[[UIApplication sharedApplication] delegate]; 117 | [app addBookmark:url withTitle:title]; 118 | } 119 | 120 | #pragma mark - View lifecycle 121 | 122 | - (void)viewDidLoad 123 | { 124 | [super viewDidLoad]; 125 | // Do any additional setup after loading the view, typically from a nib. 126 | [self configureView]; 127 | 128 | self.navigationItem.rightBarButtonItems = 129 | [NSArray arrayWithObjects:self.fullScreenButtonItem, 130 | self.addButtonItem, 131 | self.reloadButtonItem, 132 | self.backButtonItem, nil]; 133 | 134 | // Enable WebGL 135 | id webDocumentView = [self.webView performSelector:@selector(_browserView)]; 136 | id backingWebView = [webDocumentView performSelector:@selector(webView)]; 137 | [(UIBackingWebView*)backingWebView _setWebGLEnabled:YES]; 138 | 139 | [[NSNotificationCenter defaultCenter] addObserver:self 140 | selector:@selector(resumed:) 141 | name:UIApplicationDidBecomeActiveNotification object:nil]; 142 | } 143 | 144 | - (void)resumed:(id)sender { 145 | [[self navigationController] setNavigationBarHidden:NO]; 146 | } 147 | 148 | - (void)viewDidUnload 149 | { 150 | [super viewDidUnload]; 151 | // Release any retained subviews of the main view. 152 | // e.g. self.myOutlet = nil; 153 | [self navigateToURL:@"about:blank"]; 154 | } 155 | 156 | - (void)viewWillAppear:(BOOL)animated 157 | { 158 | [super viewWillAppear:animated]; 159 | 160 | [[self navigationController] setNavigationBarHidden:NO]; 161 | } 162 | 163 | - (void)viewDidAppear:(BOOL)animated 164 | { 165 | [super viewDidAppear:animated]; 166 | } 167 | 168 | - (void)viewWillDisappear:(BOOL)animated 169 | { 170 | [self navigateToURL:@"about:blank"]; 171 | [super viewWillDisappear:animated]; 172 | } 173 | 174 | - (void)viewDidDisappear:(BOOL)animated 175 | { 176 | [super viewDidDisappear:animated]; 177 | } 178 | 179 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 180 | { 181 | // Return YES for supported orientations 182 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { 183 | return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown); 184 | } else { 185 | return YES; 186 | } 187 | } 188 | 189 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 190 | { 191 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 192 | if (self) { 193 | self.title = NSLocalizedString(@"Page", @"Page"); 194 | } 195 | return self; 196 | } 197 | 198 | #pragma mark - Split view 199 | 200 | - (BOOL)splitViewController:(UISplitViewController *)svc shouldHideViewController:(UIViewController *)vc inOrientation:(UIInterfaceOrientation)orientation 201 | { 202 | return YES; 203 | } 204 | 205 | - (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController 206 | { 207 | barButtonItem.title = NSLocalizedString(@"Bookmarks", @"Bookmarks"); 208 | [self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES]; 209 | self.masterPopoverController = popoverController; 210 | } 211 | 212 | - (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem 213 | { 214 | // Called when the view is shown again in the split view, invalidating the button and popover controller. 215 | [self.navigationItem setLeftBarButtonItem:nil animated:YES]; 216 | self.masterPopoverController = nil; 217 | } 218 | 219 | @end 220 | -------------------------------------------------------------------------------- /WebGLBrowser/GLBMasterViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GLBMasterViewController.h 3 | // WebGLBrowser 4 | // 5 | // Created by Ben Vanik on 3/15/12. 6 | // Copyright (c) 2012 Ben Vanik. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class GLBDetailViewController; 12 | 13 | #import 14 | 15 | @interface GLBMasterViewController : UITableViewController 16 | 17 | @property (strong, nonatomic) GLBDetailViewController *detailViewController; 18 | 19 | @property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController; 20 | @property (strong, nonatomic) NSManagedObjectContext *managedObjectContext; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /WebGLBrowser/GLBMasterViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GLBMasterViewController.m 3 | // WebGLBrowser 4 | // 5 | // Created by Ben Vanik on 3/15/12. 6 | // Copyright (c) 2012 Ben Vanik. All rights reserved. 7 | // 8 | 9 | #import "GLBMasterViewController.h" 10 | 11 | #import "GLBAppDelegate.h" 12 | #import "GLBDetailViewController.h" 13 | 14 | @interface GLBMasterViewController () 15 | - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath; 16 | @end 17 | 18 | @implementation GLBMasterViewController 19 | 20 | @synthesize detailViewController = _detailViewController; 21 | @synthesize fetchedResultsController = __fetchedResultsController; 22 | @synthesize managedObjectContext = __managedObjectContext; 23 | 24 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 25 | { 26 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 27 | if (self) { 28 | self.title = NSLocalizedString(@"Bookmarks", @"Bookmarks"); 29 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { 30 | self.clearsSelectionOnViewWillAppear = NO; 31 | self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0); 32 | } 33 | } 34 | return self; 35 | } 36 | 37 | - (void)dealloc 38 | { 39 | [_detailViewController release]; 40 | [__fetchedResultsController release]; 41 | [__managedObjectContext release]; 42 | [super dealloc]; 43 | } 44 | 45 | - (void)didReceiveMemoryWarning 46 | { 47 | [super didReceiveMemoryWarning]; 48 | // Release any cached data, images, etc that aren't in use. 49 | } 50 | 51 | #pragma mark - Behavior 52 | 53 | - (void)insertNewObject { 54 | UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Add Bookmark" 55 | message:nil 56 | delegate:self 57 | cancelButtonTitle:@"Cancel" 58 | otherButtonTitles:@"Add", nil]; 59 | [alertView setAlertViewStyle:UIAlertViewStylePlainTextInput]; 60 | UITextField *textField = [alertView textFieldAtIndex:0]; 61 | [textField setKeyboardType:UIKeyboardTypeURL]; 62 | [textField setText:@"http://"]; 63 | [alertView show]; 64 | } 65 | 66 | - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex 67 | { 68 | if (buttonIndex == 1) { 69 | NSString* url = [[alertView textFieldAtIndex:0] text]; 70 | 71 | GLBAppDelegate *app = (GLBAppDelegate*)[[UIApplication sharedApplication] delegate]; 72 | [app addBookmark:url]; 73 | } 74 | [alertView autorelease]; 75 | } 76 | 77 | #pragma mark - View lifecycle 78 | 79 | - (void)viewDidLoad 80 | { 81 | [super viewDidLoad]; 82 | // Do any additional setup after loading the view, typically from a nib. 83 | // Set up the edit and add buttons. 84 | self.navigationItem.leftBarButtonItem = self.editButtonItem; 85 | 86 | UIBarButtonItem *addButton = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject)] autorelease]; 87 | self.navigationItem.rightBarButtonItem = addButton; 88 | } 89 | 90 | - (void)viewDidUnload 91 | { 92 | [super viewDidUnload]; 93 | // Release any retained subviews of the main view. 94 | // e.g. self.myOutlet = nil; 95 | } 96 | 97 | - (void)viewWillAppear:(BOOL)animated 98 | { 99 | [super viewWillAppear:animated]; 100 | } 101 | 102 | - (void)viewDidAppear:(BOOL)animated 103 | { 104 | [super viewDidAppear:animated]; 105 | } 106 | 107 | - (void)viewWillDisappear:(BOOL)animated 108 | { 109 | [super viewWillDisappear:animated]; 110 | } 111 | 112 | - (void)viewDidDisappear:(BOOL)animated 113 | { 114 | [super viewDidDisappear:animated]; 115 | } 116 | 117 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 118 | { 119 | // Return YES for supported orientations 120 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { 121 | return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown); 122 | } else { 123 | return YES; 124 | } 125 | } 126 | 127 | // Customize the number of sections in the table view. 128 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 129 | { 130 | return [[self.fetchedResultsController sections] count]; 131 | } 132 | 133 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 134 | { 135 | id sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section]; 136 | return [sectionInfo numberOfObjects]; 137 | } 138 | 139 | // Customize the appearance of table view cells. 140 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 141 | { 142 | static NSString *CellIdentifier = @"Cell"; 143 | 144 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 145 | if (cell == nil) { 146 | cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease]; 147 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { 148 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 149 | } 150 | } 151 | 152 | [self configureCell:cell atIndexPath:indexPath]; 153 | return cell; 154 | } 155 | 156 | /* 157 | // Override to support conditional editing of the table view. 158 | - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath 159 | { 160 | // Return NO if you do not want the specified item to be editable. 161 | return YES; 162 | } 163 | */ 164 | 165 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 166 | { 167 | if (editingStyle == UITableViewCellEditingStyleDelete) { 168 | // Delete the managed object for the given index path 169 | NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext]; 170 | [context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]]; 171 | 172 | // Save the context. 173 | NSError *error = nil; 174 | if (![context save:&error]) { 175 | /* 176 | Replace this implementation with code to handle the error appropriately. 177 | 178 | abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 179 | */ 180 | NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 181 | abort(); 182 | } 183 | } 184 | } 185 | 186 | - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath 187 | { 188 | // The table view should not be re-orderable. 189 | return NO; 190 | } 191 | 192 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 193 | { 194 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { 195 | if (!self.detailViewController) { 196 | self.detailViewController = [[[GLBDetailViewController alloc] initWithNibName:@"GLBDetailViewController_iPhone" bundle:nil] autorelease]; 197 | self.detailViewController.managedObjectContext = self.managedObjectContext; 198 | } 199 | NSManagedObject *selectedObject = [[self fetchedResultsController] objectAtIndexPath:indexPath]; 200 | self.detailViewController.detailItem = selectedObject; 201 | [self.navigationController pushViewController:self.detailViewController animated:YES]; 202 | } else { 203 | NSManagedObject *selectedObject = [[self fetchedResultsController] objectAtIndexPath:indexPath]; 204 | self.detailViewController.detailItem = selectedObject; 205 | } 206 | } 207 | 208 | #pragma mark - Fetched results controller 209 | 210 | - (NSFetchedResultsController *)fetchedResultsController 211 | { 212 | if (__fetchedResultsController != nil) { 213 | return __fetchedResultsController; 214 | } 215 | 216 | // Set up the fetched results controller. 217 | // Create the fetch request for the entity. 218 | NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease]; 219 | // Edit the entity name as appropriate. 220 | NSEntityDescription *entity = [NSEntityDescription entityForName:@"Bookmark" inManagedObjectContext:self.managedObjectContext]; 221 | [fetchRequest setEntity:entity]; 222 | 223 | // Set the batch size to a suitable number. 224 | [fetchRequest setFetchBatchSize:20]; 225 | 226 | // Edit the sort key as appropriate. 227 | NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"title" ascending:YES] autorelease]; 228 | NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil]; 229 | 230 | [fetchRequest setSortDescriptors:sortDescriptors]; 231 | 232 | // Edit the section name key path and cache name if appropriate. 233 | // nil for section name key path means "no sections". 234 | NSFetchedResultsController *aFetchedResultsController = [[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Master"] autorelease]; 235 | aFetchedResultsController.delegate = self; 236 | self.fetchedResultsController = aFetchedResultsController; 237 | 238 | NSError *error = nil; 239 | if (![self.fetchedResultsController performFetch:&error]) { 240 | /* 241 | Replace this implementation with code to handle the error appropriately. 242 | 243 | abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 244 | */ 245 | NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 246 | abort(); 247 | } 248 | 249 | return __fetchedResultsController; 250 | } 251 | 252 | - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller 253 | { 254 | [self.tableView beginUpdates]; 255 | } 256 | 257 | - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo 258 | atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type 259 | { 260 | switch(type) { 261 | case NSFetchedResultsChangeInsert: 262 | [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; 263 | break; 264 | 265 | case NSFetchedResultsChangeDelete: 266 | [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; 267 | break; 268 | } 269 | } 270 | 271 | - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject 272 | atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type 273 | newIndexPath:(NSIndexPath *)newIndexPath 274 | { 275 | UITableView *tableView = self.tableView; 276 | 277 | switch(type) { 278 | case NSFetchedResultsChangeInsert: 279 | [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 280 | break; 281 | 282 | case NSFetchedResultsChangeDelete: 283 | [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 284 | break; 285 | 286 | case NSFetchedResultsChangeUpdate: 287 | [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; 288 | break; 289 | 290 | case NSFetchedResultsChangeMove: 291 | [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 292 | [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade]; 293 | break; 294 | } 295 | } 296 | 297 | - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller 298 | { 299 | [self.tableView endUpdates]; 300 | } 301 | 302 | /* 303 | // Implementing the above methods to update the table view in response to individual changes may have performance implications if a large number of changes are made simultaneously. If this proves to be an issue, you can instead just implement controllerDidChangeContent: which notifies the delegate that all section and object changes have been processed. 304 | 305 | - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller 306 | { 307 | // In the simplest, most efficient, case, reload the table view. 308 | [self.tableView reloadData]; 309 | } 310 | */ 311 | 312 | - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath 313 | { 314 | NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath]; 315 | cell.textLabel.text = [[managedObject valueForKey:@"title"] description]; 316 | cell.detailTextLabel.text = [[managedObject valueForKey:@"url"] description]; 317 | } 318 | 319 | @end 320 | -------------------------------------------------------------------------------- /WebGLBrowser/WebGLBrowser-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Browser 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFiles 12 | 13 | icon-72.png 14 | icon-144.png 15 | icon-57.png 16 | icon-114.png 17 | 18 | CFBundleIcons 19 | 20 | CFBundlePrimaryIcon 21 | 22 | CFBundleIconFiles 23 | 24 | icon-72.png 25 | icon-144.png 26 | icon-57.png 27 | icon-114.png 28 | 29 | 30 | 31 | CFBundleIdentifier 32 | org.noxa.experiments.${PRODUCT_NAME:rfc1034identifier} 33 | CFBundleInfoDictionaryVersion 34 | 6.0 35 | CFBundleName 36 | ${PRODUCT_NAME} 37 | CFBundlePackageType 38 | APPL 39 | CFBundleShortVersionString 40 | 1.0 41 | CFBundleSignature 42 | ???? 43 | CFBundleVersion 44 | 1.0 45 | LSRequiresIPhoneOS 46 | 47 | UIRequiredDeviceCapabilities 48 | 49 | armv7 50 | 51 | UISupportedInterfaceOrientations 52 | 53 | UIInterfaceOrientationPortrait 54 | UIInterfaceOrientationLandscapeLeft 55 | UIInterfaceOrientationLandscapeRight 56 | 57 | UISupportedInterfaceOrientations~ipad 58 | 59 | UIInterfaceOrientationPortrait 60 | UIInterfaceOrientationPortraitUpsideDown 61 | UIInterfaceOrientationLandscapeLeft 62 | UIInterfaceOrientationLandscapeRight 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /WebGLBrowser/WebGLBrowser-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'WebGLBrowser' target in the 'WebGLBrowser' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_4_0 8 | #warning "This project uses features only available in iOS SDK 4.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #import 15 | #endif 16 | -------------------------------------------------------------------------------- /WebGLBrowser/WebGLBrowser.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | WebGLBrowser.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /WebGLBrowser/WebGLBrowser.xcdatamodeld/WebGLBrowser.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /WebGLBrowser/en.lproj/GLBDetailViewController_iPad.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1280 5 | 11D50b 6 | 1938 7 | 1138.32 8 | 568.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 933 12 | 13 | 14 | IBUIWebView 15 | IBUIBarButtonItem 16 | IBUIView 17 | IBProxyObject 18 | 19 | 20 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 21 | 22 | 23 | PluginDependencyRecalculationVersion 24 | 25 | 26 | 27 | 28 | IBFilesOwner 29 | IBIPadFramework 30 | 31 | 32 | IBFirstResponder 33 | IBIPadFramework 34 | 35 | 36 | 37 | 274 38 | 39 | 40 | 41 | 274 42 | {768, 1004} 43 | 44 | 45 | _NS:693 46 | 47 | 1 48 | MSAxIDEAA 49 | 50 | YES 51 | IBIPadFramework 52 | 53 | 54 | {{0, 20}, {768, 1004}} 55 | 56 | 57 | 58 | 59 | 3 60 | MQA 61 | 62 | NO 63 | 64 | 2 65 | 66 | IBIPadFramework 67 | 68 | 69 | IBIPadFramework 70 | 1 71 | 17 72 | 73 | 74 | IBIPadFramework 75 | 1 76 | 13 77 | 78 | 79 | IBIPadFramework 80 | 1 81 | 19 82 | 83 | 84 | IBIPadFramework 85 | 1 86 | 4 87 | 88 | 89 | 90 | 91 | 92 | 93 | view 94 | 95 | 96 | 97 | 12 98 | 99 | 100 | 101 | webView 102 | 103 | 104 | 105 | 68 106 | 107 | 108 | 109 | reloadButtonItem 110 | 111 | 112 | 113 | 71 114 | 115 | 116 | 117 | fullScreenButtonItem 118 | 119 | 120 | 121 | 73 122 | 123 | 124 | 125 | backButtonItem 126 | 127 | 128 | 129 | 77 130 | 131 | 132 | 133 | addButtonItem 134 | 135 | 136 | 137 | 81 138 | 139 | 140 | 141 | delegate 142 | 143 | 144 | 145 | 69 146 | 147 | 148 | 149 | reloadAction: 150 | 151 | 152 | 153 | 74 154 | 155 | 156 | 157 | fullScreenAction: 158 | 159 | 160 | 161 | 75 162 | 163 | 164 | 165 | backAction: 166 | 167 | 168 | 169 | 78 170 | 171 | 172 | 173 | addAction: 174 | 175 | 176 | 177 | 80 178 | 179 | 180 | 181 | 182 | 183 | 0 184 | 185 | 186 | 187 | 188 | 189 | -1 190 | 191 | 192 | File's Owner 193 | 194 | 195 | -2 196 | 197 | 198 | 199 | 200 | 8 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 67 209 | 210 | 211 | 212 | 213 | 70 214 | 215 | 216 | 217 | 218 | 72 219 | 220 | 221 | 222 | 223 | 76 224 | 225 | 226 | 227 | 228 | 79 229 | 230 | 231 | 232 | 233 | 234 | 235 | GLBDetailViewController 236 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 237 | UIResponder 238 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 239 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 240 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 241 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 242 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 243 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 244 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 245 | 246 | 247 | 248 | 249 | 250 | 81 251 | 252 | 253 | 254 | 255 | GLBDetailViewController 256 | UIViewController 257 | 258 | id 259 | id 260 | id 261 | id 262 | 263 | 264 | 265 | addAction: 266 | id 267 | 268 | 269 | backAction: 270 | id 271 | 272 | 273 | fullScreenAction: 274 | id 275 | 276 | 277 | reloadAction: 278 | id 279 | 280 | 281 | 282 | UIBarButtonItem 283 | UIBarButtonItem 284 | UIBarButtonItem 285 | UIBarButtonItem 286 | UIWebView 287 | 288 | 289 | 290 | addButtonItem 291 | UIBarButtonItem 292 | 293 | 294 | backButtonItem 295 | UIBarButtonItem 296 | 297 | 298 | fullScreenButtonItem 299 | UIBarButtonItem 300 | 301 | 302 | reloadButtonItem 303 | UIBarButtonItem 304 | 305 | 306 | webView 307 | UIWebView 308 | 309 | 310 | 311 | IBProjectSource 312 | ./Classes/GLBDetailViewController.h 313 | 314 | 315 | 316 | 317 | 0 318 | IBIPadFramework 319 | YES 320 | 3 321 | 933 322 | 323 | 324 | -------------------------------------------------------------------------------- /WebGLBrowser/en.lproj/GLBDetailViewController_iPhone.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1536 5 | 12C54 6 | 2840 7 | 1187.34 8 | 625.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 1926 12 | 13 | 14 | IBNSLayoutConstraint 15 | IBProxyObject 16 | IBUIBarButtonItem 17 | IBUIView 18 | IBUIWebView 19 | 20 | 21 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 22 | 23 | 24 | PluginDependencyRecalculationVersion 25 | 26 | 27 | 28 | 29 | IBFilesOwner 30 | IBCocoaTouchFramework 31 | 32 | 33 | IBFirstResponder 34 | IBCocoaTouchFramework 35 | 36 | 37 | 38 | 274 39 | 40 | 41 | 42 | 274 43 | {320, 460} 44 | 45 | 46 | _NS:693 47 | 48 | 1 49 | MSAxIDEAA 50 | 51 | YES 52 | IBCocoaTouchFramework 53 | 54 | 55 | {{0, 20}, {320, 460}} 56 | 57 | 58 | 59 | 60 | 3 61 | MQA 62 | 63 | 2 64 | 65 | 66 | 67 | IBCocoaTouchFramework 68 | 69 | 70 | IBCocoaTouchFramework 71 | 1 72 | 17 73 | 74 | 75 | IBCocoaTouchFramework 76 | 1 77 | 13 78 | 79 | 80 | IBCocoaTouchFramework 81 | 1 82 | 19 83 | 84 | 85 | IBCocoaTouchFramework 86 | 1 87 | 4 88 | 89 | 90 | 91 | 92 | 93 | 94 | view 95 | 96 | 97 | 98 | 3 99 | 100 | 101 | 102 | webView 103 | 104 | 105 | 106 | 9 107 | 108 | 109 | 110 | reloadButtonItem 111 | 112 | 113 | 114 | 11 115 | 116 | 117 | 118 | fullScreenButtonItem 119 | 120 | 121 | 122 | 13 123 | 124 | 125 | 126 | backButtonItem 127 | 128 | 129 | 130 | 18 131 | 132 | 133 | 134 | addButtonItem 135 | 136 | 137 | 138 | 22 139 | 140 | 141 | 142 | delegate 143 | 144 | 145 | 146 | 8 147 | 148 | 149 | 150 | reloadAction: 151 | 152 | 153 | 154 | 14 155 | 156 | 157 | 158 | fullScreenAction: 159 | 160 | 161 | 162 | 15 163 | 164 | 165 | 166 | backAction: 167 | 168 | 169 | 170 | 19 171 | 172 | 173 | 174 | addAction: 175 | 176 | 177 | 178 | 23 179 | 180 | 181 | 182 | 183 | 184 | 0 185 | 186 | 187 | 188 | 189 | 190 | 1 191 | 192 | 193 | 194 | 195 | 196 | 3 197 | 0 198 | 199 | 3 200 | 1 201 | 202 | 0.0 203 | 204 | 1000 205 | 206 | 8 207 | 29 208 | 3 209 | 210 | 211 | 212 | 5 213 | 0 214 | 215 | 5 216 | 1 217 | 218 | 0.0 219 | 220 | 1000 221 | 222 | 8 223 | 29 224 | 3 225 | 226 | 227 | 228 | 4 229 | 0 230 | 231 | 4 232 | 1 233 | 234 | 0.0 235 | 236 | 1000 237 | 238 | 8 239 | 29 240 | 3 241 | 242 | 243 | 244 | 6 245 | 0 246 | 247 | 6 248 | 1 249 | 250 | 0.0 251 | 252 | 1000 253 | 254 | 8 255 | 29 256 | 3 257 | 258 | 259 | 260 | 261 | 262 | -1 263 | 264 | 265 | File's Owner 266 | 267 | 268 | -2 269 | 270 | 271 | 272 | 273 | 7 274 | 275 | 276 | 277 | 278 | 10 279 | 280 | 281 | 282 | 283 | 12 284 | 285 | 286 | 287 | 288 | 16 289 | 290 | 291 | 292 | 293 | 20 294 | 295 | 296 | 297 | 298 | 24 299 | 300 | 301 | 302 | 303 | 25 304 | 305 | 306 | 307 | 308 | 26 309 | 310 | 311 | 312 | 313 | 27 314 | 315 | 316 | 317 | 318 | 319 | 320 | GLBDetailViewController 321 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 322 | UIResponder 323 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 324 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 325 | 326 | 327 | 328 | 329 | 330 | 331 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 332 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 333 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 334 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 335 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 336 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 337 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 338 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 339 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 340 | 341 | 342 | 343 | 344 | 345 | 346 | 27 347 | 348 | 349 | 350 | 351 | GLBDetailViewController 352 | UIViewController 353 | 354 | id 355 | id 356 | id 357 | id 358 | 359 | 360 | 361 | addAction: 362 | id 363 | 364 | 365 | backAction: 366 | id 367 | 368 | 369 | fullScreenAction: 370 | id 371 | 372 | 373 | reloadAction: 374 | id 375 | 376 | 377 | 378 | UIBarButtonItem 379 | UIBarButtonItem 380 | UIBarButtonItem 381 | UIBarButtonItem 382 | UIWebView 383 | 384 | 385 | 386 | addButtonItem 387 | UIBarButtonItem 388 | 389 | 390 | backButtonItem 391 | UIBarButtonItem 392 | 393 | 394 | fullScreenButtonItem 395 | UIBarButtonItem 396 | 397 | 398 | reloadButtonItem 399 | UIBarButtonItem 400 | 401 | 402 | webView 403 | UIWebView 404 | 405 | 406 | 407 | IBProjectSource 408 | ./Classes/GLBDetailViewController.h 409 | 410 | 411 | 412 | NSLayoutConstraint 413 | NSObject 414 | 415 | IBProjectSource 416 | ./Classes/NSLayoutConstraint.h 417 | 418 | 419 | 420 | 421 | 0 422 | IBCocoaTouchFramework 423 | YES 424 | 3 425 | YES 426 | 1926 427 | 428 | 429 | -------------------------------------------------------------------------------- /WebGLBrowser/en.lproj/GLBMasterViewController_iPad.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1280 5 | 11D50b 6 | 1938 7 | 1138.32 8 | 568.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 933 12 | 13 | 14 | IBProxyObject 15 | IBUITableView 16 | 17 | 18 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 19 | 20 | 21 | PluginDependencyRecalculationVersion 22 | 23 | 24 | 25 | 26 | IBFilesOwner 27 | IBIPadFramework 28 | 29 | 30 | IBFirstResponder 31 | IBIPadFramework 32 | 33 | 34 | 35 | 274 36 | {{0, 20}, {320, 832}} 37 | 38 | 39 | 40 | 3 41 | MQA 42 | 43 | YES 44 | 45 | 2 46 | 47 | 48 | IBUISplitViewMasterSimulatedSizeMetrics 49 | 50 | YES 51 | 52 | 53 | 54 | 55 | 56 | {320, 852} 57 | {320, 768} 58 | 59 | 60 | IBIPadFramework 61 | Master 62 | IBUISplitViewController 63 | 64 | IBUISplitViewControllerContentSizeLocation 65 | IBUISplitViewControllerContentSizeLocationMaster 66 | 67 | 68 | IBIPadFramework 69 | YES 70 | 1 71 | 0 72 | YES 73 | 44 74 | 22 75 | 22 76 | 77 | 78 | 79 | 80 | 81 | 82 | view 83 | 84 | 85 | 86 | 3 87 | 88 | 89 | 90 | dataSource 91 | 92 | 93 | 94 | 4 95 | 96 | 97 | 98 | delegate 99 | 100 | 101 | 102 | 5 103 | 104 | 105 | 106 | 107 | 108 | 0 109 | 110 | 111 | 112 | 113 | 114 | -1 115 | 116 | 117 | File's Owner 118 | 119 | 120 | -2 121 | 122 | 123 | 124 | 125 | 2 126 | 127 | 128 | 129 | 130 | 131 | 132 | GLBMasterViewController 133 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 134 | UIResponder 135 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 136 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 137 | 138 | 139 | 140 | 141 | 142 | 7 143 | 144 | 145 | 146 | 147 | GLBMasterViewController 148 | UITableViewController 149 | 150 | IBProjectSource 151 | ./Classes/GLBMasterViewController.h 152 | 153 | 154 | 155 | 156 | 0 157 | IBIPadFramework 158 | YES 159 | 3 160 | 933 161 | 162 | 163 | -------------------------------------------------------------------------------- /WebGLBrowser/en.lproj/GLBMasterViewController_iPhone.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1536 5 | 12C54 6 | 2840 7 | 1187.34 8 | 625.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 1926 12 | 13 | 14 | IBProxyObject 15 | IBUITableView 16 | 17 | 18 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 19 | 20 | 21 | PluginDependencyRecalculationVersion 22 | 23 | 24 | 25 | 26 | IBFilesOwner 27 | IBCocoaTouchFramework 28 | 29 | 30 | IBFirstResponder 31 | IBCocoaTouchFramework 32 | 33 | 34 | 35 | 274 36 | {{0, 20}, {320, 460}} 37 | 38 | 39 | 40 | 3 41 | MQA 42 | 43 | YES 44 | 45 | IBCocoaTouchFramework 46 | YES 47 | 1 48 | 0 49 | YES 50 | 44 51 | 22 52 | 22 53 | 54 | 55 | 56 | 57 | 58 | 59 | view 60 | 61 | 62 | 63 | 3 64 | 65 | 66 | 67 | dataSource 68 | 69 | 70 | 71 | 4 72 | 73 | 74 | 75 | delegate 76 | 77 | 78 | 79 | 5 80 | 81 | 82 | 83 | 84 | 85 | 0 86 | 87 | 88 | 89 | 90 | 91 | -1 92 | 93 | 94 | File's Owner 95 | 96 | 97 | -2 98 | 99 | 100 | 101 | 102 | 2 103 | 104 | 105 | 106 | 107 | 108 | 109 | GLBMasterViewController 110 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 111 | UIResponder 112 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 113 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 114 | 115 | 116 | 117 | 118 | 119 | 7 120 | 121 | 122 | 123 | 124 | GLBMasterViewController 125 | UITableViewController 126 | 127 | IBProjectSource 128 | ./Classes/GLBMasterViewController.h 129 | 130 | 131 | 132 | 133 | 0 134 | IBCocoaTouchFramework 135 | YES 136 | 3 137 | YES 138 | 1926 139 | 140 | 141 | -------------------------------------------------------------------------------- /WebGLBrowser/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /WebGLBrowser/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // WebGLBrowser 4 | // 5 | // Created by Ben Vanik on 3/15/12. 6 | // Copyright (c) 2012 Ben Vanik. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "GLBAppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([GLBAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /icon-114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benvanik/WebGLBrowser/df16037b3890bbee2aa65063bd47087943116b75/icon-114.png -------------------------------------------------------------------------------- /icon-144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benvanik/WebGLBrowser/df16037b3890bbee2aa65063bd47087943116b75/icon-144.png -------------------------------------------------------------------------------- /icon-57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benvanik/WebGLBrowser/df16037b3890bbee2aa65063bd47087943116b75/icon-57.png -------------------------------------------------------------------------------- /icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benvanik/WebGLBrowser/df16037b3890bbee2aa65063bd47087943116b75/icon-72.png --------------------------------------------------------------------------------