From 554c3c751509b252cf780b17e043f490dba8cfa3 Mon Sep 17 00:00:00 2001 From: saturngod Date: Mon, 21 Oct 2013 14:02:39 +0800 Subject: [PATCH] update with latest library --- ASIHTTP/ASIAuthenticationDialog.h | 35 - ASIHTTP/ASIAuthenticationDialog.m | 487 - ASIHTTP/ASICacheDelegate.h | 103 - ASIHTTP/ASIDataCompressor.h | 42 - ASIHTTP/ASIDataCompressor.m | 219 - ASIHTTP/ASIDataDecompressor.h | 41 - ASIHTTP/ASIDataDecompressor.m | 218 - ASIHTTP/ASIDownloadCache.h | 46 - ASIHTTP/ASIDownloadCache.m | 514 - ASIHTTP/ASIFormDataRequest.h | 76 - ASIHTTP/ASIFormDataRequest.m | 361 - ASIHTTP/ASIHTTPRequest.h | 1004 - ASIHTTP/ASIHTTPRequest.m | 5107 - ASIHTTP/ASIHTTPRequestConfig.h | 43 - ASIHTTP/ASIHTTPRequestDelegate.h | 35 - ASIHTTP/ASIInputStream.h | 26 - ASIHTTP/ASIInputStream.m | 138 - ASIHTTP/ASINetworkQueue.h | 108 - ASIHTTP/ASINetworkQueue.m | 343 - ASIHTTP/ASIProgressDelegate.h | 38 - ASIHTTP/ASIWebPageRequest.h | 80 - ASIHTTP/ASIWebPageRequest.m | 722 - AsyncSocket/AsyncSocket Documentation.html | 1848 - AsyncSocket/AsyncSocket.h | 511 - AsyncSocket/AsyncSocket.m | 3184 - AsyncSocket/AsyncUdpSocket.h | 365 - AsyncSocket/AsyncUdpSocket.m | 2343 - AsyncSocket/changes.txt | 98 - JSON/NSObject+SBJson.h | 67 - JSON/SBJson.h | 84 - JSON/SBJsonTokeniser.h | 67 - JSON/SBJsonTokeniser.m | 463 - JSON/SBJsonUTF8Stream.h | 58 - JSON/SBJsonUTF8Stream.m | 143 - README.md | 29 +- Reachability.h | 194 - Reachability.m | 814 - RegexKitLite.h | 295 - RegexKitLite.m | 2636 - SocketIO.h | 107 - SocketIO.m | 642 - WebSocket.h | 54 - WebSocket.m | 178 - .../socketIOexample.xcodeproj/project.pbxproj | 637 + .../contents.xcworkspacedata | 2 +- .../UserInterfaceState.xcuserstate | Bin 0 -> 16915 bytes .../xcdebugger/Breakpoints_v2.xcbkptlist | 5 + .../xcschemes/socketIOexample.xcscheme | 43 +- .../xcschemes/xcschememanagement.plist | 9 +- socketIOexample/socketIOexample/AppDelegate.h | 15 + socketIOexample/socketIOexample/AppDelegate.m | 46 + .../Base.lproj/Main.storyboard | 56 + .../AppIcon.appiconset/Contents.json | 23 + .../LaunchImage.launchimage/Contents.json | 23 + .../socketIOexample/ViewController.h | 18 + .../socketIOexample/ViewController.m | 80 + .../en.lproj/InfoPlist.strings | 0 socketIOexample/socketIOexample/main.m | 18 + .../socketIOexample-Info.plist | 12 +- .../socketIOexample-Prefix.pch | 16 + .../en.lproj/InfoPlist.strings | 2 + .../socketIOexampleTests-Info.plist | 22 + .../socketIOexampleTests.m | 34 + .../socketio.objc/JSON-Framework/SBJson.h | 44 +- .../JSON-Framework}/SBJsonParser.h | 33 +- .../JSON-Framework}/SBJsonParser.m | 31 +- .../JSON-Framework}/SBJsonStreamParser.h | 93 +- .../JSON-Framework}/SBJsonStreamParser.m | 172 +- .../SBJsonStreamParserAccumulator.h | 0 .../SBJsonStreamParserAccumulator.m | 12 +- .../SBJsonStreamParserAdapter.h | 90 +- .../SBJsonStreamParserAdapter.m | 19 +- .../JSON-Framework}/SBJsonStreamParserState.h | 2 +- .../JSON-Framework}/SBJsonStreamParserState.m | 65 +- .../JSON-Framework/SBJsonStreamTokeniser.h | 40 + .../JSON-Framework/SBJsonStreamTokeniser.m | 397 + .../JSON-Framework}/SBJsonStreamWriter.h | 92 +- .../JSON-Framework}/SBJsonStreamWriter.m | 43 +- .../SBJsonStreamWriterAccumulator.h | 0 .../SBJsonStreamWriterAccumulator.m | 8 +- .../JSON-Framework}/SBJsonStreamWriterState.h | 0 .../JSON-Framework}/SBJsonStreamWriterState.m | 12 +- .../JSON-Framework}/SBJsonWriter.h | 67 +- .../JSON-Framework}/SBJsonWriter.m | 30 +- socketIOexample/socketio.objc/SocketIO.h | 125 + socketIOexample/socketio.objc/SocketIO.m | 839 + .../socketio.objc/SocketIOJSONSerialization.h | 31 + .../socketio.objc/SocketIOJSONSerialization.m | 115 + .../socketio.objc/SocketIOPacket.h | 52 + .../socketio.objc/SocketIOPacket.m | 104 + .../socketio.objc/SocketIOTransport.h | 50 + .../SocketIOTransportWebsocket.h | 36 + .../SocketIOTransportWebsocket.m | 128 + .../socketio.objc/SocketIOTransportXHR.h | 37 + .../socketio.objc/SocketIOTransportXHR.m | 255 + .../SocketRocket/NSData+SRB64Additions.h | 24 + .../SocketRocket/NSData+SRB64Additions.m | 39 + .../socketio.objc/SocketRocket/SRWebSocket.h | 114 + .../socketio.objc/SocketRocket/SRWebSocket.m | 1757 + .../SocketRocket/SocketRocket-Prefix.pch | 27 + .../socketio.objc/SocketRocket/base64.c | 314 + .../socketio.objc/SocketRocket/base64.h | 34 + socketio.xcodeproj/project.pbxproj | 546 - .../UserInterfaceState.xcuserstate | Bin 10416 -> 0 bytes .../UserInterfaceState.xcuserstate | Bin 23362 -> 0 bytes .../xcschemes/socketio.xcscheme | 72 - .../xcschemes/xcschememanagement.plist | 22 - socketio/Classes-iOS/GHUNIT/GHAsyncTestCase.h | 163 - socketio/Classes-iOS/GHUNIT/GHAsyncTestCase.m | 186 - .../GHUNIT/GHTest/GHTest+JUnitXML.h | 44 - .../GHUNIT/GHTest/GHTest+JUnitXML.m | 48 - socketio/Classes-iOS/GHUNIT/GHTest/GHTest.h | 274 - socketio/Classes-iOS/GHUNIT/GHTest/GHTest.m | 277 - .../GHUNIT/GHTest/GHTestGroup+JUnitXML.h | 42 - .../GHUNIT/GHTest/GHTestGroup+JUnitXML.m | 64 - .../Classes-iOS/GHUNIT/GHTest/GHTestGroup.h | 187 - .../Classes-iOS/GHUNIT/GHTest/GHTestGroup.m | 396 - .../GHUNIT/GHTest/GHTestOperation.h | 47 - .../GHUNIT/GHTest/GHTestOperation.m | 63 - .../Classes-iOS/GHUNIT/GHTest/GHTestRunner.h | 221 - .../Classes-iOS/GHUNIT/GHTest/GHTestRunner.m | 291 - .../Classes-iOS/GHUNIT/GHTest/GHTestSuite.h | 118 - .../Classes-iOS/GHUNIT/GHTest/GHTestSuite.m | 167 - .../Classes-iOS/GHUNIT/GHTest/GHTesting.h | 158 - .../Classes-iOS/GHUNIT/GHTest/GHTesting.m | 340 - .../NSException+GHTestFailureExceptions.h | 97 - .../NSException+GHTestFailureExceptions.m | 263 - .../GHUNIT/GHTest/NSValue+GHValueFormatter.h | 71 - .../GHUNIT/GHTest/NSValue+GHValueFormatter.m | 161 - socketio/Classes-iOS/GHUNIT/GHTestCase.h | 152 - socketio/Classes-iOS/GHUNIT/GHTestCase.m | 64 - socketio/Classes-iOS/GHUNIT/GHTestMacros.h | 1045 - socketio/Classes-iOS/GHUNIT/GHUnit.h | 55 - .../GHUNIT/Mock/GHMockNSHTTPURLResponse.h | 46 - .../GHUNIT/Mock/GHMockNSHTTPURLResponse.m | 69 - .../GHUNIT/Mock/GHMockNSURLConnection.h | 167 - .../GHUNIT/Mock/GHMockNSURLConnection.m | 129 - .../Classes-iOS/GHUNIT/Mock/GHNSLocale+Mock.h | 68 - .../Classes-iOS/GHUNIT/Mock/GHNSLocale+Mock.m | 85 - .../GHUNIT/Mock/GHUNSObject+Swizzle.h | 51 - .../GHUNIT/Mock/GHUNSObject+Swizzle.m | 131 - .../GHUNIT/SharedUI/GHTestViewModel.h | 218 - .../GHUNIT/SharedUI/GHTestViewModel.m | 424 - socketio/External/GHUnit/README-GHUnit | 3 - socketio/External/Reachability/Reachability.h | 194 - socketio/External/Reachability/Reachability.m | 814 - socketio/Tests/ASICloudFilesRequestTests.h | 18 - socketio/Tests/ASICloudFilesRequestTests.m | 338 - socketio/Tests/ASIDataCompressorTests.h | 16 - socketio/Tests/ASIDataCompressorTests.m | 179 - socketio/Tests/ASIDownloadCacheTests.h | 17 - socketio/Tests/ASIDownloadCacheTests.m | 562 - socketio/Tests/ASIFormDataRequestTests.h | 22 - socketio/Tests/ASIFormDataRequestTests.m | 291 - socketio/Tests/ASIHTTPRequestTests.h | 64 - socketio/Tests/ASIHTTPRequestTests.m | 2001 - socketio/Tests/ASINetworkQueueTests.h | 83 - socketio/Tests/ASINetworkQueueTests.m | 1274 - socketio/Tests/ASIS3RequestTests.h | 28 - socketio/Tests/ASIS3RequestTests.m | 860 - socketio/Tests/ASITestCase.h | 20 - socketio/Tests/ASITestCase.m | 23 - socketio/Tests/ASIWebPageRequestTests.h | 16 - socketio/Tests/ASIWebPageRequestTests.m | 41 - socketio/Tests/BlocksTests.h | 16 - socketio/Tests/BlocksTests.m | 105 - socketio/Tests/ClientCertificateTests.h | 21 - socketio/Tests/ClientCertificateTests.m | 76 - socketio/Tests/GHUnitTestMain.m | 89 - socketio/Tests/PerformanceTests.h | 28 - socketio/Tests/PerformanceTests.m | 234 - socketio/Tests/ProxyTests.h | 29 - socketio/Tests/ProxyTests.m | 203 - socketio/Tests/StressTests.h | 46 - socketio/Tests/StressTests.m | 192 - socketio/en.lproj/MainWindow.xib | 444 - socketio/en.lproj/socketioViewController.xib | 271 - socketio/main.m | 17 - socketio/socketio-Prefix.pch | 14 - socketio/socketioAppDelegate.h | 19 - socketio/socketioAppDelegate.m | 73 - socketio/socketioViewController.h | 15 - socketio/socketioViewController.m | 70 - sockio-server/app.js | 39 + sockio-server/assets/css/bootstrap.min.css | 9 + sockio-server/assets/css/custom.css | 57 + sockio-server/assets/js/jquery-1.10.1.min.js | 6 + sockio-server/node_modules/.bin/express | 1 + sockio-server/node_modules/express/.npmignore | 9 + .../node_modules/express/.travis.yml | 4 + sockio-server/node_modules/express/History.md | 1183 + sockio-server/node_modules/express/LICENSE | 22 + sockio-server/node_modules/express/Makefile | 33 + sockio-server/node_modules/express/Readme.md | 179 + .../node_modules/express/bin/express | 423 + sockio-server/node_modules/express/index.js | 4 + .../node_modules/express/lib/application.js | 536 + .../node_modules/express/lib/express.js | 86 + .../node_modules/express/lib/middleware.js | 32 + .../node_modules/express/lib/request.js | 526 + .../node_modules/express/lib/response.js | 760 + .../node_modules/express/lib/router/index.js | 311 + .../node_modules/express/lib/router/route.js | 72 + .../node_modules/express/lib/utils.js | 313 + .../node_modules/express/lib/view.js | 77 + .../node_modules/buffer-crc32/.npmignore | 1 + .../node_modules/buffer-crc32/.travis.yml | 8 + .../node_modules/buffer-crc32/README.md | 47 + .../node_modules/buffer-crc32/index.js | 88 + .../node_modules/buffer-crc32/package.json | 43 + .../buffer-crc32/tests/crc.test.js | 89 + .../express/node_modules/commander/History.md | 158 + .../express/node_modules/commander/Readme.md | 276 + .../express/node_modules/commander/index.js | 1152 + .../commander/node_modules/keypress/README.md | 101 + .../commander/node_modules/keypress/index.js | 346 + .../node_modules/keypress/package.json | 35 + .../commander/node_modules/keypress/test.js | 28 + .../node_modules/commander/package.json | 44 + .../express/node_modules/connect/.npmignore | 12 + .../express/node_modules/connect/.travis.yml | 4 + .../express/node_modules/connect/LICENSE | 24 + .../express/node_modules/connect/Readme.md | 133 + .../express/node_modules/connect/index.js | 4 + .../express/node_modules/connect/lib/cache.js | 81 + .../node_modules/connect/lib/connect.js | 92 + .../express/node_modules/connect/lib/index.js | 50 + .../connect/lib/middleware/basicAuth.js | 103 + .../connect/lib/middleware/bodyParser.js | 61 + .../connect/lib/middleware/compress.js | 189 + .../connect/lib/middleware/cookieParser.js | 62 + .../connect/lib/middleware/cookieSession.js | 115 + .../connect/lib/middleware/csrf.js | 75 + .../connect/lib/middleware/directory.js | 229 + .../connect/lib/middleware/errorHandler.js | 86 + .../connect/lib/middleware/favicon.js | 80 + .../connect/lib/middleware/json.js | 89 + .../connect/lib/middleware/limit.js | 78 + .../connect/lib/middleware/logger.js | 339 + .../connect/lib/middleware/methodOverride.js | 59 + .../connect/lib/middleware/multipart.js | 133 + .../connect/lib/middleware/query.js | 46 + .../connect/lib/middleware/responseTime.js | 32 + .../connect/lib/middleware/session.js | 355 + .../connect/lib/middleware/session/cookie.js | 140 + .../connect/lib/middleware/session/memory.js | 129 + .../connect/lib/middleware/session/session.js | 116 + .../connect/lib/middleware/session/store.js | 84 + .../connect/lib/middleware/static.js | 95 + .../connect/lib/middleware/staticCache.js | 231 + .../connect/lib/middleware/timeout.js | 55 + .../connect/lib/middleware/urlencoded.js | 78 + .../connect/lib/middleware/vhost.js | 40 + .../express/node_modules/connect/lib/patch.js | 79 + .../express/node_modules/connect/lib/proto.js | 230 + .../connect/lib/public/directory.html | 81 + .../connect/lib/public/error.html | 14 + .../connect/lib/public/favicon.ico | Bin 0 -> 1406 bytes .../connect/lib/public/icons/page.png | Bin 0 -> 635 bytes .../connect/lib/public/icons/page_add.png | Bin 0 -> 739 bytes .../connect/lib/public/icons/page_attach.png | Bin 0 -> 794 bytes .../connect/lib/public/icons/page_code.png | Bin 0 -> 818 bytes .../connect/lib/public/icons/page_copy.png | Bin 0 -> 663 bytes .../connect/lib/public/icons/page_delete.png | Bin 0 -> 740 bytes .../connect/lib/public/icons/page_edit.png | Bin 0 -> 807 bytes .../connect/lib/public/icons/page_error.png | Bin 0 -> 793 bytes .../connect/lib/public/icons/page_excel.png | Bin 0 -> 817 bytes .../connect/lib/public/icons/page_find.png | Bin 0 -> 879 bytes .../connect/lib/public/icons/page_gear.png | Bin 0 -> 833 bytes .../connect/lib/public/icons/page_go.png | Bin 0 -> 779 bytes .../connect/lib/public/icons/page_green.png | Bin 0 -> 621 bytes .../connect/lib/public/icons/page_key.png | Bin 0 -> 801 bytes .../lib/public/icons/page_lightning.png | Bin 0 -> 839 bytes .../connect/lib/public/icons/page_link.png | Bin 0 -> 830 bytes .../lib/public/icons/page_paintbrush.png | Bin 0 -> 813 bytes .../connect/lib/public/icons/page_paste.png | Bin 0 -> 703 bytes .../connect/lib/public/icons/page_red.png | Bin 0 -> 641 bytes .../connect/lib/public/icons/page_refresh.png | Bin 0 -> 858 bytes .../connect/lib/public/icons/page_save.png | Bin 0 -> 774 bytes .../connect/lib/public/icons/page_white.png | Bin 0 -> 294 bytes .../lib/public/icons/page_white_acrobat.png | Bin 0 -> 591 bytes .../public/icons/page_white_actionscript.png | Bin 0 -> 664 bytes .../lib/public/icons/page_white_add.png | Bin 0 -> 512 bytes .../connect/lib/public/icons/page_white_c.png | Bin 0 -> 587 bytes .../lib/public/icons/page_white_camera.png | Bin 0 -> 656 bytes .../lib/public/icons/page_white_cd.png | Bin 0 -> 666 bytes .../lib/public/icons/page_white_code.png | Bin 0 -> 603 bytes .../lib/public/icons/page_white_code_red.png | Bin 0 -> 587 bytes .../public/icons/page_white_coldfusion.png | Bin 0 -> 592 bytes .../public/icons/page_white_compressed.png | Bin 0 -> 724 bytes .../lib/public/icons/page_white_copy.png | Bin 0 -> 309 bytes .../lib/public/icons/page_white_cplusplus.png | Bin 0 -> 621 bytes .../lib/public/icons/page_white_csharp.png | Bin 0 -> 700 bytes .../lib/public/icons/page_white_cup.png | Bin 0 -> 639 bytes .../lib/public/icons/page_white_database.png | Bin 0 -> 579 bytes .../lib/public/icons/page_white_delete.png | Bin 0 -> 536 bytes .../lib/public/icons/page_white_dvd.png | Bin 0 -> 638 bytes .../lib/public/icons/page_white_edit.png | Bin 0 -> 618 bytes .../lib/public/icons/page_white_error.png | Bin 0 -> 623 bytes .../lib/public/icons/page_white_excel.png | Bin 0 -> 663 bytes .../lib/public/icons/page_white_find.png | Bin 0 -> 676 bytes .../lib/public/icons/page_white_flash.png | Bin 0 -> 582 bytes .../lib/public/icons/page_white_freehand.png | Bin 0 -> 639 bytes .../lib/public/icons/page_white_gear.png | Bin 0 -> 402 bytes .../lib/public/icons/page_white_get.png | Bin 0 -> 516 bytes .../lib/public/icons/page_white_go.png | Bin 0 -> 612 bytes .../connect/lib/public/icons/page_white_h.png | Bin 0 -> 603 bytes .../public/icons/page_white_horizontal.png | Bin 0 -> 296 bytes .../lib/public/icons/page_white_key.png | Bin 0 -> 616 bytes .../lib/public/icons/page_white_lightning.png | Bin 0 -> 669 bytes .../lib/public/icons/page_white_link.png | Bin 0 -> 614 bytes .../lib/public/icons/page_white_magnify.png | Bin 0 -> 554 bytes .../lib/public/icons/page_white_medal.png | Bin 0 -> 706 bytes .../lib/public/icons/page_white_office.png | Bin 0 -> 779 bytes .../lib/public/icons/page_white_paint.png | Bin 0 -> 688 bytes .../public/icons/page_white_paintbrush.png | Bin 0 -> 618 bytes .../lib/public/icons/page_white_paste.png | Bin 0 -> 620 bytes .../lib/public/icons/page_white_php.png | Bin 0 -> 538 bytes .../lib/public/icons/page_white_picture.png | Bin 0 -> 650 bytes .../public/icons/page_white_powerpoint.png | Bin 0 -> 588 bytes .../lib/public/icons/page_white_put.png | Bin 0 -> 523 bytes .../lib/public/icons/page_white_ruby.png | Bin 0 -> 626 bytes .../lib/public/icons/page_white_stack.png | Bin 0 -> 317 bytes .../lib/public/icons/page_white_star.png | Bin 0 -> 565 bytes .../lib/public/icons/page_white_swoosh.png | Bin 0 -> 634 bytes .../lib/public/icons/page_white_text.png | Bin 0 -> 342 bytes .../public/icons/page_white_text_width.png | Bin 0 -> 315 bytes .../lib/public/icons/page_white_tux.png | Bin 0 -> 668 bytes .../lib/public/icons/page_white_vector.png | Bin 0 -> 644 bytes .../public/icons/page_white_visualstudio.png | Bin 0 -> 702 bytes .../lib/public/icons/page_white_width.png | Bin 0 -> 309 bytes .../lib/public/icons/page_white_word.png | Bin 0 -> 651 bytes .../lib/public/icons/page_white_world.png | Bin 0 -> 734 bytes .../lib/public/icons/page_white_wrench.png | Bin 0 -> 613 bytes .../lib/public/icons/page_white_zip.png | Bin 0 -> 386 bytes .../connect/lib/public/icons/page_word.png | Bin 0 -> 777 bytes .../connect/lib/public/icons/page_world.png | Bin 0 -> 903 bytes .../node_modules/connect/lib/public/style.css | 141 + .../express/node_modules/connect/lib/utils.js | 386 + .../connect/node_modules/bytes/.npmignore | 1 + .../connect/node_modules/bytes/History.md | 10 + .../connect/node_modules/bytes/Makefile | 7 + .../connect/node_modules/bytes/Readme.md | 51 + .../connect/node_modules/bytes/component.json | 7 + .../connect/node_modules/bytes/index.js | 39 + .../connect/node_modules/bytes/package.json | 24 + .../node_modules/formidable/.npmignore | 4 + .../node_modules/formidable/.travis.yml | 5 + .../connect/node_modules/formidable/LICENSE | 7 + .../connect/node_modules/formidable/Readme.md | 419 + .../benchmark/bench-multipart-parser.js | 71 + .../node_modules/formidable/example/json.js | 67 + .../node_modules/formidable/example/post.js | 43 + .../node_modules/formidable/example/upload.js | 48 + .../connect/node_modules/formidable/index.js | 1 + .../node_modules/formidable/lib/file.js | 72 + .../formidable/lib/incoming_form.js | 535 + .../node_modules/formidable/lib/index.js | 3 + .../formidable/lib/json_parser.js | 35 + .../formidable/lib/multipart_parser.js | 324 + .../formidable/lib/octet_parser.js | 20 + .../formidable/lib/querystring_parser.js | 27 + .../node_modules/formidable/package.json | 42 + .../node_modules/formidable/test/common.js | 18 + .../test/fixture/file/beta-sticker-1.png | Bin 0 -> 1660 bytes .../test/fixture/file/binaryfile.tar.gz | Bin 0 -> 301 bytes .../formidable/test/fixture/file/blank.gif | Bin 0 -> 49 bytes .../test/fixture/file/funkyfilename.txt | 1 + .../test/fixture/file/menu_separator.png | Bin 0 -> 931 bytes .../formidable/test/fixture/file/plain.txt | 1 + .../http/special-chars-in-filename/info.md | 3 + .../formidable/test/fixture/js/encoding.js | 24 + .../formidable/test/fixture/js/misc.js | 6 + .../formidable/test/fixture/js/no-filename.js | 9 + .../formidable/test/fixture/js/preamble.js | 9 + .../fixture/js/special-chars-in-filename.js | 21 + .../formidable/test/fixture/js/workarounds.js | 8 + .../formidable/test/fixture/multipart.js | 72 + .../test/integration/test-fixtures.js | 96 + .../formidable/test/integration/test-json.js | 38 + .../test/integration/test-octet-stream.js | 45 + .../formidable/test/legacy/common.js | 24 + .../integration/test-multipart-parser.js | 80 + .../test/legacy/simple/test-file.js | 104 + .../test/legacy/simple/test-incoming-form.js | 756 + .../legacy/simple/test-multipart-parser.js | 50 + .../legacy/simple/test-querystring-parser.js | 45 + .../legacy/system/test-multi-video-upload.js | 71 + .../node_modules/formidable/test/run.js | 1 + .../standalone/test-connection-aborted.js | 27 + .../test-content-transfer-encoding.js | 48 + .../test/standalone/test-issue-46.js | 49 + .../formidable/test/tools/base64.html | 67 + .../formidable/test/unit/test-file.js | 33 + .../test/unit/test-incoming-form.js | 63 + .../node_modules/formidable/tool/record.js | 47 + .../connect/node_modules/pause/.npmignore | 4 + .../connect/node_modules/pause/History.md | 5 + .../connect/node_modules/pause/Makefile | 7 + .../connect/node_modules/pause/Readme.md | 29 + .../connect/node_modules/pause/index.js | 29 + .../connect/node_modules/pause/package.json | 24 + .../connect/node_modules/qs/.gitmodules | 6 + .../connect/node_modules/qs/.npmignore | 7 + .../connect/node_modules/qs/Readme.md | 58 + .../connect/node_modules/qs/index.js | 387 + .../connect/node_modules/qs/package.json | 41 + .../connect/node_modules/uid2/index.js | 49 + .../connect/node_modules/uid2/package.json | 16 + .../express/node_modules/connect/package.json | 59 + .../node_modules/cookie-signature/.npmignore | 4 + .../node_modules/cookie-signature/History.md | 11 + .../node_modules/cookie-signature/Makefile | 7 + .../node_modules/cookie-signature/Readme.md | 42 + .../node_modules/cookie-signature/index.js | 42 + .../cookie-signature/package.json | 28 + .../express/node_modules/cookie/.npmignore | 1 + .../express/node_modules/cookie/.travis.yml | 5 + .../express/node_modules/cookie/LICENSE | 9 + .../express/node_modules/cookie/README.md | 44 + .../express/node_modules/cookie/index.js | 70 + .../express/node_modules/cookie/package.json | 40 + .../node_modules/cookie/test/mocha.opts | 1 + .../express/node_modules/cookie/test/parse.js | 44 + .../node_modules/cookie/test/serialize.js | 64 + .../express/node_modules/debug/.npmignore | 4 + .../express/node_modules/debug/History.md | 62 + .../express/node_modules/debug/Readme.md | 115 + .../express/node_modules/debug/component.json | 9 + .../express/node_modules/debug/debug.js | 124 + .../express/node_modules/debug/example/app.js | 19 + .../node_modules/debug/example/browser.html | 24 + .../node_modules/debug/example/wildcards.js | 10 + .../node_modules/debug/example/worker.js | 22 + .../express/node_modules/debug/index.js | 5 + .../express/node_modules/debug/lib/debug.js | 134 + .../express/node_modules/debug/package.json | 40 + .../express/node_modules/fresh/.npmignore | 1 + .../express/node_modules/fresh/History.md | 5 + .../express/node_modules/fresh/Makefile | 7 + .../express/node_modules/fresh/Readme.md | 57 + .../express/node_modules/fresh/index.js | 53 + .../express/node_modules/fresh/package.json | 31 + .../express/node_modules/methods/index.js | 26 + .../express/node_modules/methods/package.json | 24 + .../express/node_modules/mkdirp/.npmignore | 2 + .../express/node_modules/mkdirp/.travis.yml | 5 + .../express/node_modules/mkdirp/LICENSE | 21 + .../node_modules/mkdirp/examples/pow.js | 6 + .../express/node_modules/mkdirp/index.js | 82 + .../express/node_modules/mkdirp/package.json | 37 + .../node_modules/mkdirp/readme.markdown | 63 + .../express/node_modules/mkdirp/test/chmod.js | 38 + .../node_modules/mkdirp/test/clobber.js | 37 + .../node_modules/mkdirp/test/mkdirp.js | 28 + .../express/node_modules/mkdirp/test/perm.js | 32 + .../node_modules/mkdirp/test/perm_sync.js | 39 + .../express/node_modules/mkdirp/test/race.js | 41 + .../express/node_modules/mkdirp/test/rel.js | 32 + .../node_modules/mkdirp/test/return.js | 25 + .../node_modules/mkdirp/test/return_sync.js | 24 + .../express/node_modules/mkdirp/test/root.js | 18 + .../express/node_modules/mkdirp/test/sync.js | 32 + .../express/node_modules/mkdirp/test/umask.js | 28 + .../node_modules/mkdirp/test/umask_sync.js | 32 + .../node_modules/range-parser/.npmignore | 1 + .../node_modules/range-parser/History.md | 15 + .../node_modules/range-parser/Makefile | 7 + .../node_modules/range-parser/Readme.md | 28 + .../node_modules/range-parser/index.js | 49 + .../node_modules/range-parser/package.json | 24 + .../express/node_modules/send/.npmignore | 4 + .../express/node_modules/send/History.md | 40 + .../express/node_modules/send/Makefile | 8 + .../express/node_modules/send/Readme.md | 128 + .../express/node_modules/send/index.js | 2 + .../express/node_modules/send/lib/send.js | 474 + .../express/node_modules/send/lib/utils.js | 47 + .../send/node_modules/mime/LICENSE | 19 + .../send/node_modules/mime/README.md | 66 + .../send/node_modules/mime/mime.js | 114 + .../send/node_modules/mime/package.json | 39 + .../send/node_modules/mime/test.js | 84 + .../send/node_modules/mime/types/mime.types | 1588 + .../send/node_modules/mime/types/node.types | 77 + .../express/node_modules/send/package.json | 45 + .../node_modules/express/package.json | 88 + sockio-server/node_modules/express/test.js | 14 + sockio-server/node_modules/redis/.npmignore | 1 + sockio-server/node_modules/redis/README.md | 714 + .../redis/benches/buffer_bench.js | 89 + .../redis/benches/hiredis_parser.js | 38 + .../node_modules/redis/benches/re_sub_test.js | 14 + .../redis/benches/reconnect_test.js | 29 + .../redis/benches/stress/codec.js | 16 + .../redis/benches/stress/pubsub/pub.js | 38 + .../redis/benches/stress/pubsub/run | 10 + .../redis/benches/stress/pubsub/server.js | 23 + .../redis/benches/stress/rpushblpop/pub.js | 49 + .../redis/benches/stress/rpushblpop/run | 6 + .../redis/benches/stress/rpushblpop/server.js | 30 + .../redis/benches/stress/speed/00 | 13 + .../redis/benches/stress/speed/plot | 13 + .../redis/benches/stress/speed/size-rate.png | Bin 0 -> 6672 bytes .../redis/benches/stress/speed/speed.js | 84 + .../redis/benches/sub_quit_test.js | 18 + sockio-server/node_modules/redis/changelog.md | 275 + .../redis/diff_multi_bench_output.js | 90 + .../node_modules/redis/examples/auth.js | 5 + .../redis/examples/backpressure_drain.js | 33 + .../node_modules/redis/examples/eval.js | 14 + .../node_modules/redis/examples/extend.js | 24 + .../node_modules/redis/examples/file.js | 32 + .../node_modules/redis/examples/mget.js | 5 + .../node_modules/redis/examples/monitor.js | 10 + .../node_modules/redis/examples/multi.js | 46 + .../node_modules/redis/examples/multi2.js | 29 + .../node_modules/redis/examples/psubscribe.js | 33 + .../node_modules/redis/examples/pub_sub.js | 41 + .../node_modules/redis/examples/simple.js | 24 + .../node_modules/redis/examples/sort.js | 17 + .../node_modules/redis/examples/subqueries.js | 15 + .../node_modules/redis/examples/subquery.js | 19 + .../redis/examples/unix_socket.js | 29 + .../node_modules/redis/examples/web_server.js | 31 + .../node_modules/redis/generate_commands.js | 39 + sockio-server/node_modules/redis/index.js | 1164 + .../node_modules/redis/lib/commands.js | 149 + .../node_modules/redis/lib/parser/hiredis.js | 46 + .../redis/lib/parser/javascript.js | 301 + sockio-server/node_modules/redis/lib/queue.js | 59 + .../node_modules/redis/lib/to_array.js | 12 + sockio-server/node_modules/redis/lib/util.js | 11 + .../node_modules/redis/multi_bench.js | 222 + sockio-server/node_modules/redis/package.json | 37 + sockio-server/node_modules/redis/test.js | 2049 + .../node_modules/socket.io/.npmignore | 3 + .../node_modules/socket.io/.travis.yml | 6 + .../node_modules/socket.io/History.md | 325 + sockio-server/node_modules/socket.io/LICENSE | 22 + sockio-server/node_modules/socket.io/Makefile | 31 + .../node_modules/socket.io/Readme.md | 364 + .../socket.io/benchmarks/decode.bench.js | 64 + .../socket.io/benchmarks/encode.bench.js | 90 + .../socket.io/benchmarks/runner.js | 55 + sockio-server/node_modules/socket.io/index.js | 8 + .../node_modules/socket.io/lib/logger.js | 97 + .../node_modules/socket.io/lib/manager.js | 1027 + .../node_modules/socket.io/lib/namespace.js | 355 + .../node_modules/socket.io/lib/parser.js | 249 + .../node_modules/socket.io/lib/socket.io.js | 143 + .../node_modules/socket.io/lib/socket.js | 369 + .../node_modules/socket.io/lib/static.js | 395 + .../node_modules/socket.io/lib/store.js | 98 + .../socket.io/lib/stores/memory.js | 143 + .../socket.io/lib/stores/redis.js | 269 + .../node_modules/socket.io/lib/transport.js | 534 + .../socket.io/lib/transports/flashsocket.js | 129 + .../socket.io/lib/transports/htmlfile.js | 83 + .../socket.io/lib/transports/http-polling.js | 147 + .../socket.io/lib/transports/http.js | 121 + .../socket.io/lib/transports/index.js | 12 + .../socket.io/lib/transports/jsonp-polling.js | 97 + .../socket.io/lib/transports/websocket.js | 36 + .../lib/transports/websocket/default.js | 362 + .../lib/transports/websocket/hybi-07-12.js | 622 + .../lib/transports/websocket/hybi-16.js | 622 + .../lib/transports/websocket/index.js | 11 + .../socket.io/lib/transports/xhr-polling.js | 69 + .../node_modules/socket.io/lib/util.js | 50 + .../node_modules/base64id/.npmignore | 3 + .../socket.io/node_modules/base64id/README.md | 18 + .../node_modules/base64id/lib/base64id.js | 103 + .../node_modules/base64id/package.json | 28 + .../node_modules/policyfile/.npmignore | 1 + .../socket.io/node_modules/policyfile/LICENSE | 19 + .../node_modules/policyfile/Makefile | 7 + .../node_modules/policyfile/README.md | 98 + .../node_modules/policyfile/doc/index.html | 375 + .../policyfile/examples/basic.fallback.js | 8 + .../node_modules/policyfile/examples/basic.js | 5 + .../node_modules/policyfile/index.js | 1 + .../node_modules/policyfile/lib/server.js | 289 + .../node_modules/policyfile/package.json | 55 + .../node_modules/policyfile/tests/ssl/ssl.crt | 21 + .../policyfile/tests/ssl/ssl.private.key | 27 + .../policyfile/tests/unit.test.js | 231 + .../socket.io/node_modules/redis/.npmignore | 1 + .../socket.io/node_modules/redis/README.md | 691 + .../redis/benches/buffer_bench.js | 89 + .../redis/benches/hiredis_parser.js | 38 + .../node_modules/redis/benches/re_sub_test.js | 14 + .../redis/benches/reconnect_test.js | 29 + .../redis/benches/stress/codec.js | 16 + .../redis/benches/stress/pubsub/pub.js | 38 + .../redis/benches/stress/pubsub/run | 10 + .../redis/benches/stress/pubsub/server.js | 23 + .../redis/benches/stress/rpushblpop/pub.js | 49 + .../redis/benches/stress/rpushblpop/run | 6 + .../redis/benches/stress/rpushblpop/server.js | 30 + .../redis/benches/stress/speed/00 | 13 + .../redis/benches/stress/speed/plot | 13 + .../redis/benches/stress/speed/size-rate.png | Bin 0 -> 6672 bytes .../redis/benches/stress/speed/speed.js | 84 + .../redis/benches/sub_quit_test.js | 18 + .../socket.io/node_modules/redis/changelog.md | 219 + .../redis/diff_multi_bench_output.js | 87 + .../node_modules/redis/examples/auth.js | 5 + .../redis/examples/backpressure_drain.js | 33 + .../node_modules/redis/examples/eval.js | 9 + .../node_modules/redis/examples/extend.js | 24 + .../node_modules/redis/examples/file.js | 32 + .../node_modules/redis/examples/mget.js | 5 + .../node_modules/redis/examples/monitor.js | 10 + .../node_modules/redis/examples/multi.js | 46 + .../node_modules/redis/examples/multi2.js | 29 + .../node_modules/redis/examples/psubscribe.js | 33 + .../node_modules/redis/examples/pub_sub.js | 41 + .../node_modules/redis/examples/simple.js | 24 + .../node_modules/redis/examples/sort.js | 17 + .../node_modules/redis/examples/subqueries.js | 15 + .../node_modules/redis/examples/subquery.js | 19 + .../redis/examples/unix_socket.js | 29 + .../node_modules/redis/examples/web_server.js | 31 + .../node_modules/redis/generate_commands.js | 39 + .../socket.io/node_modules/redis/index.js | 1113 + .../node_modules/redis/lib/commands.js | 147 + .../node_modules/redis/lib/parser/hiredis.js | 46 + .../redis/lib/parser/javascript.js | 317 + .../socket.io/node_modules/redis/lib/queue.js | 61 + .../node_modules/redis/lib/to_array.js | 12 + .../socket.io/node_modules/redis/lib/util.js | 11 + .../socket.io/node_modules/redis/mem.js | 11 + .../node_modules/redis/multi_bench.js | 225 + .../socket.io/node_modules/redis/package.json | 38 + .../socket.io/node_modules/redis/test.js | 1618 + .../node_modules/socket.io-client/.npmignore | 2 + .../node_modules/socket.io-client/History.md | 237 + .../node_modules/socket.io-client/Makefile | 20 + .../node_modules/socket.io-client/README.md | 246 + .../socket.io-client/bin/builder.js | 303 + .../components/component-bind/component.json | 14 + .../components/component-bind/index.js | 24 + .../component-emitter/component.json | 13 + .../components/component-emitter/index.js | 147 + .../component-json-fallback/component.json | 15 + .../component-json-fallback/index.js | 486 + .../components/component-json/component.json | 17 + .../components/component-json/index.js | 4 + .../component.json | 24 + .../lib/emitter.js | 52 + .../learnboost-engine.io-client/lib/index.js | 2 + .../learnboost-engine.io-client/lib/parser.js | 163 + .../learnboost-engine.io-client/lib/socket.js | 492 + .../lib/transport.js | 141 + .../lib/transports/flashsocket.js | 254 + .../lib/transports/index.js | 62 + .../lib/transports/polling-jsonp.js | 221 + .../lib/transports/polling-xhr.js | 288 + .../lib/transports/polling.js | 210 + .../lib/transports/websocket.js | 158 + .../learnboost-engine.io-client/lib/util.js | 265 + .../component.json | 12 + .../learnboost-socket.io-protocol/index.js | 177 + .../timoxley-to-array/component.json | 16 + .../components/timoxley-to-array/index.js | 27 + .../visionmedia-debug/component.json | 16 + .../components/visionmedia-debug/debug.js | 122 + .../components/visionmedia-debug/index.js | 5 + .../socket.io-client/dist/WebSocketMain.swf | Bin 0 -> 175830 bytes .../dist/WebSocketMainInsecure.swf | Bin 0 -> 175953 bytes .../socket.io-client/dist/socket.io.js | 3873 + .../socket.io-client/dist/socket.io.min.js | 2 + .../socket.io-client/lib/events.js | 182 + .../node_modules/socket.io-client/lib/io.js | 206 + .../node_modules/socket.io-client/lib/json.js | 322 + .../socket.io-client/lib/namespace.js | 242 + .../socket.io-client/lib/parser.js | 262 + .../socket.io-client/lib/socket.js | 579 + .../socket.io-client/lib/transport.js | 256 + .../lib/transports/flashsocket.js | 191 + .../lib/transports/htmlfile.js | 173 + .../lib/transports/jsonp-polling.js | 256 + .../lib/transports/websocket.js | 197 + .../lib/transports/xhr-polling.js | 177 + .../socket.io-client/lib/transports/xhr.js | 217 + .../node_modules/socket.io-client/lib/util.js | 365 + .../lib/vendor/web-socket-js/.npmignore | 1 + .../lib/vendor/web-socket-js/README.md | 157 + .../vendor/web-socket-js/WebSocketMain.swf | Bin 0 -> 175830 bytes .../web-socket-js/WebSocketMainInsecure.zip | Bin 0 -> 166610 bytes .../flash-src/IWebSocketLogger.as | 8 + .../web-socket-js/flash-src/WebSocket.as | 464 + .../web-socket-js/flash-src/WebSocketEvent.as | 33 + .../web-socket-js/flash-src/WebSocketMain.as | 150 + .../flash-src/WebSocketMainInsecure.as | 19 + .../vendor/web-socket-js/flash-src/build.sh | 10 + .../com/adobe/net/proxies/RFC2817Socket.as | 204 + .../flash-src/com/gsolo/encryption/MD5.as | 375 + .../flash-src/com/hurlant/crypto/Crypto.as | 287 + .../crypto/cert/MozillaRootCertificates.as | 3235 + .../hurlant/crypto/cert/X509Certificate.as | 218 + .../crypto/cert/X509CertificateCollection.as | 57 + .../flash-src/com/hurlant/crypto/hash/HMAC.as | 82 + .../com/hurlant/crypto/hash/IHMAC.as | 27 + .../com/hurlant/crypto/hash/IHash.as | 21 + .../flash-src/com/hurlant/crypto/hash/MAC.as | 137 + .../flash-src/com/hurlant/crypto/hash/MD2.as | 124 + .../flash-src/com/hurlant/crypto/hash/MD5.as | 204 + .../flash-src/com/hurlant/crypto/hash/SHA1.as | 106 + .../com/hurlant/crypto/hash/SHA224.as | 28 + .../com/hurlant/crypto/hash/SHA256.as | 115 + .../com/hurlant/crypto/hash/SHABase.as | 71 + .../flash-src/com/hurlant/crypto/prng/ARC4.as | 90 + .../com/hurlant/crypto/prng/IPRNG.as | 20 + .../com/hurlant/crypto/prng/Random.as | 119 + .../com/hurlant/crypto/prng/TLSPRF.as | 142 + .../com/hurlant/crypto/rsa/RSAKey.as | 339 + .../com/hurlant/crypto/symmetric/AESKey.as | 2797 + .../hurlant/crypto/symmetric/BlowFishKey.as | 375 + .../com/hurlant/crypto/symmetric/CBCMode.as | 55 + .../com/hurlant/crypto/symmetric/CFB8Mode.as | 61 + .../com/hurlant/crypto/symmetric/CFBMode.as | 64 + .../com/hurlant/crypto/symmetric/CTRMode.as | 58 + .../com/hurlant/crypto/symmetric/DESKey.as | 365 + .../com/hurlant/crypto/symmetric/ECBMode.as | 86 + .../com/hurlant/crypto/symmetric/ICipher.as | 21 + .../com/hurlant/crypto/symmetric/IMode.as | 15 + .../com/hurlant/crypto/symmetric/IPad.as | 32 + .../hurlant/crypto/symmetric/IStreamCipher.as | 21 + .../hurlant/crypto/symmetric/ISymmetricKey.as | 35 + .../com/hurlant/crypto/symmetric/IVMode.as | 110 + .../com/hurlant/crypto/symmetric/NullPad.as | 34 + .../com/hurlant/crypto/symmetric/OFBMode.as | 52 + .../com/hurlant/crypto/symmetric/PKCS5.as | 44 + .../com/hurlant/crypto/symmetric/SSLPad.as | 44 + .../hurlant/crypto/symmetric/SimpleIVMode.as | 60 + .../com/hurlant/crypto/symmetric/TLSPad.as | 42 + .../hurlant/crypto/symmetric/TripleDESKey.as | 88 + .../com/hurlant/crypto/symmetric/XTeaKey.as | 94 + .../com/hurlant/crypto/symmetric/aeskey.pl | 29 + .../com/hurlant/crypto/symmetric/dump.txt | 2304 + .../com/hurlant/crypto/tests/AESKeyTest.as | 1220 + .../com/hurlant/crypto/tests/ARC4Test.as | 58 + .../hurlant/crypto/tests/BigIntegerTest.as | 39 + .../hurlant/crypto/tests/BlowFishKeyTest.as | 148 + .../com/hurlant/crypto/tests/CBCModeTest.as | 160 + .../com/hurlant/crypto/tests/CFB8ModeTest.as | 71 + .../com/hurlant/crypto/tests/CFBModeTest.as | 98 + .../com/hurlant/crypto/tests/CTRModeTest.as | 109 + .../com/hurlant/crypto/tests/DESKeyTest.as | 112 + .../com/hurlant/crypto/tests/ECBModeTest.as | 151 + .../com/hurlant/crypto/tests/HMACTest.as | 184 + .../com/hurlant/crypto/tests/ITestHarness.as | 20 + .../com/hurlant/crypto/tests/MD2Test.as | 56 + .../com/hurlant/crypto/tests/MD5Test.as | 58 + .../com/hurlant/crypto/tests/OFBModeTest.as | 101 + .../com/hurlant/crypto/tests/RSAKeyTest.as | 92 + .../com/hurlant/crypto/tests/SHA1Test.as | 198 + .../com/hurlant/crypto/tests/SHA224Test.as | 58 + .../com/hurlant/crypto/tests/SHA256Test.as | 60 + .../com/hurlant/crypto/tests/TLSPRFTest.as | 51 + .../com/hurlant/crypto/tests/TestCase.as | 42 + .../hurlant/crypto/tests/TripleDESKeyTest.as | 59 + .../com/hurlant/crypto/tests/XTeaKeyTest.as | 66 + .../com/hurlant/crypto/tls/BulkCiphers.as | 102 + .../com/hurlant/crypto/tls/CipherSuites.as | 117 + .../hurlant/crypto/tls/IConnectionState.as | 14 + .../hurlant/crypto/tls/ISecurityParameters.as | 29 + .../com/hurlant/crypto/tls/KeyExchanges.as | 24 + .../flash-src/com/hurlant/crypto/tls/MACs.as | 38 + .../hurlant/crypto/tls/SSLConnectionState.as | 171 + .../com/hurlant/crypto/tls/SSLEvent.as | 26 + .../crypto/tls/SSLSecurityParameters.as | 340 + .../com/hurlant/crypto/tls/TLSConfig.as | 70 + .../hurlant/crypto/tls/TLSConnectionState.as | 151 + .../com/hurlant/crypto/tls/TLSEngine.as | 895 + .../com/hurlant/crypto/tls/TLSError.as | 39 + .../com/hurlant/crypto/tls/TLSEvent.as | 27 + .../crypto/tls/TLSSecurityParameters.as | 197 + .../com/hurlant/crypto/tls/TLSSocket.as | 370 + .../com/hurlant/crypto/tls/TLSSocketEvent.as | 26 + .../com/hurlant/crypto/tls/TLSTest.as | 180 + .../com/hurlant/math/BarrettReduction.as | 90 + .../flash-src/com/hurlant/math/BigInteger.as | 1543 + .../com/hurlant/math/ClassicReduction.as | 35 + .../flash-src/com/hurlant/math/IReduction.as | 11 + .../com/hurlant/math/MontgomeryReduction.as | 85 + .../com/hurlant/math/NullReduction.as | 34 + .../flash-src/com/hurlant/math/bi_internal.as | 11 + .../flash-src/com/hurlant/util/ArrayUtil.as | 25 + .../flash-src/com/hurlant/util/Base64.as | 189 + .../flash-src/com/hurlant/util/Hex.as | 66 + .../flash-src/com/hurlant/util/Memory.as | 28 + .../com/hurlant/util/der/ByteString.as | 43 + .../flash-src/com/hurlant/util/der/DER.as | 210 + .../com/hurlant/util/der/IAsn1Type.as | 21 + .../flash-src/com/hurlant/util/der/Integer.as | 44 + .../flash-src/com/hurlant/util/der/OID.as | 35 + .../com/hurlant/util/der/ObjectIdentifier.as | 112 + .../flash-src/com/hurlant/util/der/PEM.as | 118 + .../com/hurlant/util/der/PrintableString.as | 49 + .../com/hurlant/util/der/Sequence.as | 90 + .../flash-src/com/hurlant/util/der/Set.as | 27 + .../flash-src/com/hurlant/util/der/Type.as | 94 + .../flash-src/com/hurlant/util/der/UTCTime.as | 60 + .../lib/vendor/web-socket-js/sample.html | 75 + .../lib/vendor/web-socket-js/swfobject.js | 6 + .../lib/vendor/web-socket-js/web_socket.js | 349 + .../node_modules/.bin/uglifyjs | 1 + .../socket.io-client/node_modules/.bin/wscat | 1 + .../active-x-obfuscator/.npmignore | 2 + .../active-x-obfuscator/Readme.md | 33 + .../node_modules/active-x-obfuscator/index.js | 83 + .../node_modules/zeparser/.npmignore | 1 + .../node_modules/zeparser/LICENSE | 19 + .../node_modules/zeparser/README | 37 + .../node_modules/zeparser/Tokenizer.js | 646 + .../node_modules/zeparser/ZeParser.js | 2180 + .../node_modules/zeparser/benchmark.html | 111608 +++++++++++++++ .../node_modules/zeparser/index.js | 1 + .../node_modules/zeparser/package.json | 31 + .../node_modules/zeparser/test-parser.html | 26 + .../node_modules/zeparser/test-tokenizer.html | 23 + .../node_modules/zeparser/tests.js | 478 + .../zeparser/unicodecategories.js | 49 + .../active-x-obfuscator/package.json | 38 + .../node_modules/active-x-obfuscator/test.js | 53 + .../node_modules/uglify-js/.npmignore | 4 + .../node_modules/uglify-js/README.html | 981 + .../node_modules/uglify-js/README.org | 574 + .../node_modules/uglify-js/bin/uglifyjs | 323 + .../node_modules/uglify-js/docstyle.css | 75 + .../node_modules/uglify-js/lib/object-ast.js | 75 + .../node_modules/uglify-js/lib/parse-js.js | 1342 + .../node_modules/uglify-js/lib/process.js | 2011 + .../uglify-js/lib/squeeze-more.js | 69 + .../node_modules/uglify-js/package.json | 29 + .../node_modules/uglify-js/test/beautify.js | 28 + .../node_modules/uglify-js/test/testparser.js | 403 + .../test/unit/compress/expected/array1.js | 1 + .../test/unit/compress/expected/array2.js | 1 + .../test/unit/compress/expected/array3.js | 1 + .../test/unit/compress/expected/array4.js | 1 + .../test/unit/compress/expected/assignment.js | 1 + .../unit/compress/expected/concatstring.js | 1 + .../test/unit/compress/expected/const.js | 1 + .../unit/compress/expected/empty-blocks.js | 1 + .../unit/compress/expected/forstatement.js | 1 + .../test/unit/compress/expected/if.js | 1 + .../test/unit/compress/expected/ifreturn.js | 1 + .../test/unit/compress/expected/ifreturn2.js | 1 + .../test/unit/compress/expected/issue10.js | 1 + .../test/unit/compress/expected/issue11.js | 1 + .../test/unit/compress/expected/issue13.js | 1 + .../test/unit/compress/expected/issue14.js | 1 + .../test/unit/compress/expected/issue16.js | 1 + .../test/unit/compress/expected/issue17.js | 1 + .../test/unit/compress/expected/issue20.js | 1 + .../test/unit/compress/expected/issue21.js | 1 + .../test/unit/compress/expected/issue25.js | 1 + .../test/unit/compress/expected/issue27.js | 1 + .../test/unit/compress/expected/issue278.js | 1 + .../test/unit/compress/expected/issue28.js | 1 + .../test/unit/compress/expected/issue29.js | 1 + .../test/unit/compress/expected/issue30.js | 1 + .../test/unit/compress/expected/issue34.js | 1 + .../test/unit/compress/expected/issue4.js | 1 + .../test/unit/compress/expected/issue48.js | 1 + .../test/unit/compress/expected/issue50.js | 1 + .../test/unit/compress/expected/issue53.js | 1 + .../test/unit/compress/expected/issue54.1.js | 1 + .../test/unit/compress/expected/issue68.js | 1 + .../test/unit/compress/expected/issue69.js | 1 + .../test/unit/compress/expected/issue9.js | 1 + .../test/unit/compress/expected/mangle.js | 1 + .../unit/compress/expected/null_string.js | 1 + .../unit/compress/expected/strict-equals.js | 1 + .../test/unit/compress/expected/var.js | 1 + .../test/unit/compress/expected/whitespace.js | 1 + .../test/unit/compress/expected/with.js | 1 + .../test/unit/compress/test/array1.js | 3 + .../test/unit/compress/test/array2.js | 4 + .../test/unit/compress/test/array3.js | 4 + .../test/unit/compress/test/array4.js | 6 + .../test/unit/compress/test/assignment.js | 20 + .../test/unit/compress/test/concatstring.js | 3 + .../test/unit/compress/test/const.js | 5 + .../test/unit/compress/test/empty-blocks.js | 4 + .../test/unit/compress/test/forstatement.js | 10 + .../uglify-js/test/unit/compress/test/if.js | 6 + .../test/unit/compress/test/ifreturn.js | 9 + .../test/unit/compress/test/ifreturn2.js | 16 + .../test/unit/compress/test/issue10.js | 1 + .../test/unit/compress/test/issue11.js | 3 + .../test/unit/compress/test/issue13.js | 1 + .../test/unit/compress/test/issue14.js | 1 + .../test/unit/compress/test/issue16.js | 1 + .../test/unit/compress/test/issue17.js | 4 + .../test/unit/compress/test/issue20.js | 1 + .../test/unit/compress/test/issue21.js | 6 + .../test/unit/compress/test/issue25.js | 7 + .../test/unit/compress/test/issue27.js | 1 + .../test/unit/compress/test/issue278.js | 1 + .../test/unit/compress/test/issue28.js | 3 + .../test/unit/compress/test/issue29.js | 1 + .../test/unit/compress/test/issue30.js | 3 + .../test/unit/compress/test/issue34.js | 3 + .../test/unit/compress/test/issue4.js | 3 + .../test/unit/compress/test/issue48.js | 1 + .../test/unit/compress/test/issue50.js | 9 + .../test/unit/compress/test/issue53.js | 1 + .../test/unit/compress/test/issue54.1.js | 3 + .../test/unit/compress/test/issue68.js | 5 + .../test/unit/compress/test/issue69.js | 1 + .../test/unit/compress/test/issue9.js | 4 + .../test/unit/compress/test/mangle.js | 5 + .../test/unit/compress/test/null_string.js | 1 + .../test/unit/compress/test/strict-equals.js | 3 + .../uglify-js/test/unit/compress/test/var.js | 3 + .../test/unit/compress/test/whitespace.js | 21 + .../uglify-js/test/unit/compress/test/with.js | 2 + .../uglify-js/test/unit/scripts.js | 55 + .../node_modules/uglify-js/tmp/269.js | 13 + .../node_modules/uglify-js/tmp/app.js | 22315 +++ .../uglify-js/tmp/embed-tokens.js | 15 + .../node_modules/uglify-js/tmp/goto.js | 26 + .../node_modules/uglify-js/tmp/goto2.js | 8 + .../node_modules/uglify-js/tmp/hoist.js | 33 + .../node_modules/uglify-js/tmp/instrument.js | 97 + .../node_modules/uglify-js/tmp/instrument2.js | 138 + .../node_modules/uglify-js/tmp/liftvars.js | 8 + .../node_modules/uglify-js/tmp/test.js | 30 + .../uglify-js/tmp/uglify-hangs.js | 3930 + .../uglify-js/tmp/uglify-hangs2.js | 166 + .../node_modules/uglify-js/uglify-js.js | 17 + .../node_modules/ws/.npmignore | 6 + .../node_modules/ws/.travis.yml | 6 + .../node_modules/ws/History.md | 299 + .../socket.io-client/node_modules/ws/Makefile | 40 + .../node_modules/ws/README.md | 159 + .../node_modules/ws/bench/parser.benchmark.js | 115 + .../node_modules/ws/bench/sender.benchmark.js | 66 + .../node_modules/ws/bench/speed.js | 105 + .../node_modules/ws/bench/util.js | 105 + .../node_modules/ws/bin/wscat | 190 + .../node_modules/ws/binding.gyp | 16 + .../node_modules/ws/build/Makefile | 359 + .../Release/.deps/Release/bufferutil.node.d | 1 + .../obj.target/bufferutil/src/bufferutil.o.d | 23 + .../obj.target/validation/src/validation.o.d | 23 + .../Release/.deps/Release/validation.node.d | 1 + .../ws/build/Release/bufferutil.node | Bin 0 -> 18340 bytes .../node_modules/ws/build/Release/linker.lock | 0 .../obj.target/bufferutil/src/bufferutil.o | Bin 0 -> 81768 bytes .../obj.target/validation/src/validation.o | Bin 0 -> 77504 bytes .../ws/build/Release/validation.node | Bin 0 -> 18604 bytes .../node_modules/ws/build/binding.Makefile | 6 + .../ws/build/bufferutil.target.mk | 158 + .../node_modules/ws/build/config.gypi | 112 + .../node_modules/ws/build/gyp-mac-tool | 224 + .../ws/build/validation.target.mk | 158 + .../node_modules/ws/builderror.log | 2 + .../node_modules/ws/doc/ws.md | 181 + .../ws/examples/fileapi/.npmignore | 1 + .../ws/examples/fileapi/package.json | 18 + .../ws/examples/fileapi/public/app.js | 39 + .../ws/examples/fileapi/public/index.html | 22 + .../ws/examples/fileapi/public/uploader.js | 55 + .../ws/examples/fileapi/server.js | 103 + .../serverstats-express_3/package.json | 17 + .../serverstats-express_3/public/index.html | 33 + .../examples/serverstats-express_3/server.js | 21 + .../ws/examples/serverstats/package.json | 17 + .../ws/examples/serverstats/public/index.html | 33 + .../ws/examples/serverstats/server.js | 19 + .../node_modules/ws/examples/ssl.js | 59 + .../socket.io-client/node_modules/ws/index.js | 26 + .../node_modules/ws/lib/BufferPool.js | 59 + .../ws/lib/BufferUtil.fallback.js | 47 + .../node_modules/ws/lib/BufferUtil.js | 16 + .../node_modules/ws/lib/ErrorCodes.js | 24 + .../node_modules/ws/lib/Receiver.hixie.js | 180 + .../node_modules/ws/lib/Receiver.js | 591 + .../node_modules/ws/lib/Sender.hixie.js | 123 + .../node_modules/ws/lib/Sender.js | 227 + .../ws/lib/Validation.fallback.js | 12 + .../node_modules/ws/lib/Validation.js | 16 + .../node_modules/ws/lib/WebSocket.js | 802 + .../node_modules/ws/lib/WebSocketServer.js | 460 + .../node_modules/ws/lib/browser.js | 5 + .../ws/node_modules/commander/.npmignore | 4 + .../ws/node_modules/commander/.travis.yml | 4 + .../ws/node_modules/commander/History.md | 107 + .../ws/node_modules/commander/Makefile | 7 + .../ws/node_modules/commander/Readme.md | 262 + .../ws/node_modules/commander/index.js | 2 + .../node_modules/commander/lib/commander.js | 1026 + .../ws/node_modules/commander/package.json | 38 + .../ws/node_modules/nan/.index.js | 1 + .../node_modules/ws/node_modules/nan/LICENSE | 43 + .../ws/node_modules/nan/README.md | 703 + .../node_modules/ws/node_modules/nan/nan.h | 876 + .../ws/node_modules/nan/package.json | 39 + .../ws/node_modules/options/.npmignore | 5 + .../ws/node_modules/options/Makefile | 12 + .../ws/node_modules/options/README.md | 28 + .../ws/node_modules/options/lib/options.js | 86 + .../ws/node_modules/options/package.json | 32 + .../options/test/fixtures/test.conf | 4 + .../node_modules/options/test/options.test.js | 140 + .../ws/node_modules/tinycolor/.npmignore | 5 + .../ws/node_modules/tinycolor/README.md | 3 + .../ws/node_modules/tinycolor/example.js | 3 + .../ws/node_modules/tinycolor/package.json | 27 + .../ws/node_modules/tinycolor/tinycolor.js | 31 + .../node_modules/ws/package.json | 59 + .../node_modules/ws/src/bufferutil.cc | 117 + .../node_modules/ws/src/validation.cc | 145 + .../node_modules/ws/test/BufferPool.test.js | 63 + .../ws/test/Receiver.hixie.test.js | 158 + .../node_modules/ws/test/Receiver.test.js | 255 + .../node_modules/ws/test/Sender.hixie.test.js | 134 + .../node_modules/ws/test/Sender.test.js | 24 + .../node_modules/ws/test/Validation.test.js | 23 + .../ws/test/WebSocket.integration.js | 44 + .../node_modules/ws/test/WebSocket.test.js | 1698 + .../ws/test/WebSocketServer.test.js | 1103 + .../node_modules/ws/test/autobahn-server.js | 29 + .../node_modules/ws/test/autobahn.js | 52 + .../ws/test/fixtures/agent1-cert.pem | 16 + .../ws/test/fixtures/agent1-key.pem | 15 + .../ws/test/fixtures/ca1-cert.pem | 15 + .../node_modules/ws/test/fixtures/ca1-key.pem | 17 + .../ws/test/fixtures/certificate.pem | 13 + .../node_modules/ws/test/fixtures/key.pem | 15 + .../node_modules/ws/test/fixtures/request.pem | 11 + .../node_modules/ws/test/fixtures/textfile | 9 + .../node_modules/ws/test/hybi-common.js | 99 + .../node_modules/ws/test/testserver.js | 180 + .../node_modules/xmlhttprequest/README.md | 53 + .../xmlhttprequest/autotest.watchr | 8 + .../xmlhttprequest/example/demo.js | 16 + .../xmlhttprequest/lib/XMLHttpRequest.js | 548 + .../node_modules/xmlhttprequest/package.json | 42 + .../xmlhttprequest/tests/test-constants.js | 13 + .../xmlhttprequest/tests/test-events.js | 50 + .../xmlhttprequest/tests/test-exceptions.js | 62 + .../xmlhttprequest/tests/test-headers.js | 61 + .../tests/test-request-methods.js | 62 + .../tests/test-request-protocols.js | 34 + .../xmlhttprequest/tests/testdata.txt | 1 + .../socket.io-client/package.json | 71 + .../socket.io-client/test/events.test.js | 120 + .../socket.io-client/test/io.test.js | 31 + .../test/node/builder.common.js | 102 + .../test/node/builder.test.js | 131 + .../socket.io-client/test/parser.test.js | 360 + .../socket.io-client/test/socket.test.js | 422 + .../socket.io-client/test/util.test.js | 156 + .../socket.io-client/test/worker.js | 20 + .../node_modules/socket.io/package.json | 74 + sockio-server/package.json | 10 + sockio-server/views/index.html | 89 + 1064 files changed, 245497 insertions(+), 40198 deletions(-) delete mode 100644 ASIHTTP/ASIAuthenticationDialog.h delete mode 100644 ASIHTTP/ASIAuthenticationDialog.m delete mode 100644 ASIHTTP/ASICacheDelegate.h delete mode 100644 ASIHTTP/ASIDataCompressor.h delete mode 100644 ASIHTTP/ASIDataCompressor.m delete mode 100644 ASIHTTP/ASIDataDecompressor.h delete mode 100644 ASIHTTP/ASIDataDecompressor.m delete mode 100644 ASIHTTP/ASIDownloadCache.h delete mode 100644 ASIHTTP/ASIDownloadCache.m delete mode 100644 ASIHTTP/ASIFormDataRequest.h delete mode 100644 ASIHTTP/ASIFormDataRequest.m delete mode 100644 ASIHTTP/ASIHTTPRequest.h delete mode 100644 ASIHTTP/ASIHTTPRequest.m delete mode 100644 ASIHTTP/ASIHTTPRequestConfig.h delete mode 100644 ASIHTTP/ASIHTTPRequestDelegate.h delete mode 100644 ASIHTTP/ASIInputStream.h delete mode 100644 ASIHTTP/ASIInputStream.m delete mode 100644 ASIHTTP/ASINetworkQueue.h delete mode 100644 ASIHTTP/ASINetworkQueue.m delete mode 100644 ASIHTTP/ASIProgressDelegate.h delete mode 100644 ASIHTTP/ASIWebPageRequest.h delete mode 100644 ASIHTTP/ASIWebPageRequest.m delete mode 100755 AsyncSocket/AsyncSocket Documentation.html delete mode 100755 AsyncSocket/AsyncSocket.h delete mode 100755 AsyncSocket/AsyncSocket.m delete mode 100755 AsyncSocket/AsyncUdpSocket.h delete mode 100755 AsyncSocket/AsyncUdpSocket.m delete mode 100755 AsyncSocket/changes.txt delete mode 100644 JSON/NSObject+SBJson.h delete mode 100644 JSON/SBJson.h delete mode 100644 JSON/SBJsonTokeniser.h delete mode 100644 JSON/SBJsonTokeniser.m delete mode 100644 JSON/SBJsonUTF8Stream.h delete mode 100644 JSON/SBJsonUTF8Stream.m delete mode 100644 Reachability.h delete mode 100644 Reachability.m delete mode 100644 RegexKitLite.h delete mode 100644 RegexKitLite.m delete mode 100644 SocketIO.h delete mode 100644 SocketIO.m delete mode 100755 WebSocket.h delete mode 100755 WebSocket.m create mode 100644 socketIOexample/socketIOexample.xcodeproj/project.pbxproj rename {socketio.xcodeproj => socketIOexample/socketIOexample.xcodeproj}/project.xcworkspace/contents.xcworkspacedata (68%) create mode 100644 socketIOexample/socketIOexample.xcodeproj/project.xcworkspace/xcuserdata/lusi.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 socketIOexample/socketIOexample.xcodeproj/xcuserdata/lusi.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist rename socketio.xcodeproj/xcuserdata/htainlinshwe.xcuserdatad/xcschemes/socketio.xcscheme => socketIOexample/socketIOexample.xcodeproj/xcuserdata/lusi.xcuserdatad/xcschemes/socketIOexample.xcscheme (61%) rename {socketio.xcodeproj/xcuserdata/htainlinshwe.xcuserdatad => socketIOexample/socketIOexample.xcodeproj/xcuserdata/lusi.xcuserdatad}/xcschemes/xcschememanagement.plist (71%) create mode 100644 socketIOexample/socketIOexample/AppDelegate.h create mode 100644 socketIOexample/socketIOexample/AppDelegate.m create mode 100644 socketIOexample/socketIOexample/Base.lproj/Main.storyboard create mode 100644 socketIOexample/socketIOexample/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 socketIOexample/socketIOexample/Images.xcassets/LaunchImage.launchimage/Contents.json create mode 100644 socketIOexample/socketIOexample/ViewController.h create mode 100644 socketIOexample/socketIOexample/ViewController.m rename {socketio => socketIOexample/socketIOexample}/en.lproj/InfoPlist.strings (100%) create mode 100644 socketIOexample/socketIOexample/main.m rename socketio/socketio-Info.plist => socketIOexample/socketIOexample/socketIOexample-Info.plist (83%) create mode 100644 socketIOexample/socketIOexample/socketIOexample-Prefix.pch create mode 100644 socketIOexample/socketIOexampleTests/en.lproj/InfoPlist.strings create mode 100644 socketIOexample/socketIOexampleTests/socketIOexampleTests-Info.plist create mode 100644 socketIOexample/socketIOexampleTests/socketIOexampleTests.m rename JSON/NSObject+SBJson.m => socketIOexample/socketio.objc/JSON-Framework/SBJson.h (67%) mode change 100644 => 100755 rename {JSON => socketIOexample/socketio.objc/JSON-Framework}/SBJsonParser.h (73%) mode change 100644 => 100755 rename {JSON => socketIOexample/socketio.objc/JSON-Framework}/SBJsonParser.m (73%) mode change 100644 => 100755 rename {JSON => socketIOexample/socketio.objc/JSON-Framework}/SBJsonStreamParser.h (68%) mode change 100644 => 100755 rename {JSON => socketIOexample/socketio.objc/JSON-Framework}/SBJsonStreamParser.m (56%) mode change 100644 => 100755 rename {JSON => socketIOexample/socketio.objc/JSON-Framework}/SBJsonStreamParserAccumulator.h (100%) mode change 100644 => 100755 rename {JSON => socketIOexample/socketio.objc/JSON-Framework}/SBJsonStreamParserAccumulator.m (93%) mode change 100644 => 100755 rename {JSON => socketIOexample/socketio.objc/JSON-Framework}/SBJsonStreamParserAdapter.h (70%) mode change 100644 => 100755 rename {JSON => socketIOexample/socketio.objc/JSON-Framework}/SBJsonStreamParserAdapter.m (94%) mode change 100644 => 100755 rename {JSON => socketIOexample/socketio.objc/JSON-Framework}/SBJsonStreamParserState.h (98%) mode change 100644 => 100755 rename {JSON => socketIOexample/socketio.objc/JSON-Framework}/SBJsonStreamParserState.m (84%) mode change 100644 => 100755 create mode 100755 socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamTokeniser.h create mode 100755 socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamTokeniser.m rename {JSON => socketIOexample/socketio.objc/JSON-Framework}/SBJsonStreamWriter.h (75%) mode change 100644 => 100755 rename {JSON => socketIOexample/socketio.objc/JSON-Framework}/SBJsonStreamWriter.m (92%) mode change 100644 => 100755 rename {JSON => socketIOexample/socketio.objc/JSON-Framework}/SBJsonStreamWriterAccumulator.h (100%) mode change 100644 => 100755 rename {JSON => socketIOexample/socketio.objc/JSON-Framework}/SBJsonStreamWriterAccumulator.m (95%) mode change 100644 => 100755 rename {JSON => socketIOexample/socketio.objc/JSON-Framework}/SBJsonStreamWriterState.h (100%) mode change 100644 => 100755 rename {JSON => socketIOexample/socketio.objc/JSON-Framework}/SBJsonStreamWriterState.m (93%) mode change 100644 => 100755 rename {JSON => socketIOexample/socketio.objc/JSON-Framework}/SBJsonWriter.h (74%) mode change 100644 => 100755 rename {JSON => socketIOexample/socketio.objc/JSON-Framework}/SBJsonWriter.m (78%) mode change 100644 => 100755 create mode 100755 socketIOexample/socketio.objc/SocketIO.h create mode 100755 socketIOexample/socketio.objc/SocketIO.m create mode 100644 socketIOexample/socketio.objc/SocketIOJSONSerialization.h create mode 100644 socketIOexample/socketio.objc/SocketIOJSONSerialization.m create mode 100644 socketIOexample/socketio.objc/SocketIOPacket.h create mode 100644 socketIOexample/socketio.objc/SocketIOPacket.m create mode 100644 socketIOexample/socketio.objc/SocketIOTransport.h create mode 100644 socketIOexample/socketio.objc/SocketIOTransportWebsocket.h create mode 100644 socketIOexample/socketio.objc/SocketIOTransportWebsocket.m create mode 100644 socketIOexample/socketio.objc/SocketIOTransportXHR.h create mode 100644 socketIOexample/socketio.objc/SocketIOTransportXHR.m create mode 100755 socketIOexample/socketio.objc/SocketRocket/NSData+SRB64Additions.h create mode 100755 socketIOexample/socketio.objc/SocketRocket/NSData+SRB64Additions.m create mode 100755 socketIOexample/socketio.objc/SocketRocket/SRWebSocket.h create mode 100755 socketIOexample/socketio.objc/SocketRocket/SRWebSocket.m create mode 100755 socketIOexample/socketio.objc/SocketRocket/SocketRocket-Prefix.pch create mode 100755 socketIOexample/socketio.objc/SocketRocket/base64.c create mode 100755 socketIOexample/socketio.objc/SocketRocket/base64.h delete mode 100644 socketio.xcodeproj/project.pbxproj delete mode 100644 socketio.xcodeproj/project.xcworkspace/xcuserdata/htainlinshwe.xcuserdatad/UserInterfaceState.xcuserstate delete mode 100644 socketio.xcodeproj/project.xcworkspace/xcuserdata/saturngod.xcuserdatad/UserInterfaceState.xcuserstate delete mode 100644 socketio.xcodeproj/xcuserdata/saturngod.xcuserdatad/xcschemes/socketio.xcscheme delete mode 100644 socketio.xcodeproj/xcuserdata/saturngod.xcuserdatad/xcschemes/xcschememanagement.plist delete mode 100644 socketio/Classes-iOS/GHUNIT/GHAsyncTestCase.h delete mode 100644 socketio/Classes-iOS/GHUNIT/GHAsyncTestCase.m delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/GHTest+JUnitXML.h delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/GHTest+JUnitXML.m delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/GHTest.h delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/GHTest.m delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/GHTestGroup+JUnitXML.h delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/GHTestGroup+JUnitXML.m delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/GHTestGroup.h delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/GHTestGroup.m delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/GHTestOperation.h delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/GHTestOperation.m delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/GHTestRunner.h delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/GHTestRunner.m delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/GHTestSuite.h delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/GHTestSuite.m delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/GHTesting.h delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/GHTesting.m delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/NSException+GHTestFailureExceptions.h delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/NSException+GHTestFailureExceptions.m delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/NSValue+GHValueFormatter.h delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTest/NSValue+GHValueFormatter.m delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTestCase.h delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTestCase.m delete mode 100644 socketio/Classes-iOS/GHUNIT/GHTestMacros.h delete mode 100644 socketio/Classes-iOS/GHUNIT/GHUnit.h delete mode 100644 socketio/Classes-iOS/GHUNIT/Mock/GHMockNSHTTPURLResponse.h delete mode 100644 socketio/Classes-iOS/GHUNIT/Mock/GHMockNSHTTPURLResponse.m delete mode 100644 socketio/Classes-iOS/GHUNIT/Mock/GHMockNSURLConnection.h delete mode 100644 socketio/Classes-iOS/GHUNIT/Mock/GHMockNSURLConnection.m delete mode 100644 socketio/Classes-iOS/GHUNIT/Mock/GHNSLocale+Mock.h delete mode 100644 socketio/Classes-iOS/GHUNIT/Mock/GHNSLocale+Mock.m delete mode 100644 socketio/Classes-iOS/GHUNIT/Mock/GHUNSObject+Swizzle.h delete mode 100644 socketio/Classes-iOS/GHUNIT/Mock/GHUNSObject+Swizzle.m delete mode 100644 socketio/Classes-iOS/GHUNIT/SharedUI/GHTestViewModel.h delete mode 100644 socketio/Classes-iOS/GHUNIT/SharedUI/GHTestViewModel.m delete mode 100644 socketio/External/GHUnit/README-GHUnit delete mode 100644 socketio/External/Reachability/Reachability.h delete mode 100644 socketio/External/Reachability/Reachability.m delete mode 100644 socketio/Tests/ASICloudFilesRequestTests.h delete mode 100644 socketio/Tests/ASICloudFilesRequestTests.m delete mode 100644 socketio/Tests/ASIDataCompressorTests.h delete mode 100644 socketio/Tests/ASIDataCompressorTests.m delete mode 100644 socketio/Tests/ASIDownloadCacheTests.h delete mode 100644 socketio/Tests/ASIDownloadCacheTests.m delete mode 100644 socketio/Tests/ASIFormDataRequestTests.h delete mode 100644 socketio/Tests/ASIFormDataRequestTests.m delete mode 100644 socketio/Tests/ASIHTTPRequestTests.h delete mode 100644 socketio/Tests/ASIHTTPRequestTests.m delete mode 100755 socketio/Tests/ASINetworkQueueTests.h delete mode 100755 socketio/Tests/ASINetworkQueueTests.m delete mode 100644 socketio/Tests/ASIS3RequestTests.h delete mode 100644 socketio/Tests/ASIS3RequestTests.m delete mode 100644 socketio/Tests/ASITestCase.h delete mode 100644 socketio/Tests/ASITestCase.m delete mode 100644 socketio/Tests/ASIWebPageRequestTests.h delete mode 100644 socketio/Tests/ASIWebPageRequestTests.m delete mode 100644 socketio/Tests/BlocksTests.h delete mode 100644 socketio/Tests/BlocksTests.m delete mode 100644 socketio/Tests/ClientCertificateTests.h delete mode 100644 socketio/Tests/ClientCertificateTests.m delete mode 100644 socketio/Tests/GHUnitTestMain.m delete mode 100644 socketio/Tests/PerformanceTests.h delete mode 100644 socketio/Tests/PerformanceTests.m delete mode 100644 socketio/Tests/ProxyTests.h delete mode 100644 socketio/Tests/ProxyTests.m delete mode 100644 socketio/Tests/StressTests.h delete mode 100644 socketio/Tests/StressTests.m delete mode 100644 socketio/en.lproj/MainWindow.xib delete mode 100644 socketio/en.lproj/socketioViewController.xib delete mode 100644 socketio/main.m delete mode 100644 socketio/socketio-Prefix.pch delete mode 100644 socketio/socketioAppDelegate.h delete mode 100644 socketio/socketioAppDelegate.m delete mode 100644 socketio/socketioViewController.h delete mode 100644 socketio/socketioViewController.m create mode 100644 sockio-server/app.js create mode 100644 sockio-server/assets/css/bootstrap.min.css create mode 100644 sockio-server/assets/css/custom.css create mode 100644 sockio-server/assets/js/jquery-1.10.1.min.js create mode 120000 sockio-server/node_modules/.bin/express create mode 100644 sockio-server/node_modules/express/.npmignore create mode 100644 sockio-server/node_modules/express/.travis.yml create mode 100644 sockio-server/node_modules/express/History.md create mode 100644 sockio-server/node_modules/express/LICENSE create mode 100644 sockio-server/node_modules/express/Makefile create mode 100644 sockio-server/node_modules/express/Readme.md create mode 100755 sockio-server/node_modules/express/bin/express create mode 100644 sockio-server/node_modules/express/index.js create mode 100644 sockio-server/node_modules/express/lib/application.js create mode 100644 sockio-server/node_modules/express/lib/express.js create mode 100644 sockio-server/node_modules/express/lib/middleware.js create mode 100644 sockio-server/node_modules/express/lib/request.js create mode 100644 sockio-server/node_modules/express/lib/response.js create mode 100644 sockio-server/node_modules/express/lib/router/index.js create mode 100644 sockio-server/node_modules/express/lib/router/route.js create mode 100644 sockio-server/node_modules/express/lib/utils.js create mode 100644 sockio-server/node_modules/express/lib/view.js create mode 100644 sockio-server/node_modules/express/node_modules/buffer-crc32/.npmignore create mode 100644 sockio-server/node_modules/express/node_modules/buffer-crc32/.travis.yml create mode 100644 sockio-server/node_modules/express/node_modules/buffer-crc32/README.md create mode 100644 sockio-server/node_modules/express/node_modules/buffer-crc32/index.js create mode 100644 sockio-server/node_modules/express/node_modules/buffer-crc32/package.json create mode 100644 sockio-server/node_modules/express/node_modules/buffer-crc32/tests/crc.test.js create mode 100644 sockio-server/node_modules/express/node_modules/commander/History.md create mode 100644 sockio-server/node_modules/express/node_modules/commander/Readme.md create mode 100644 sockio-server/node_modules/express/node_modules/commander/index.js create mode 100644 sockio-server/node_modules/express/node_modules/commander/node_modules/keypress/README.md create mode 100644 sockio-server/node_modules/express/node_modules/commander/node_modules/keypress/index.js create mode 100644 sockio-server/node_modules/express/node_modules/commander/node_modules/keypress/package.json create mode 100644 sockio-server/node_modules/express/node_modules/commander/node_modules/keypress/test.js create mode 100644 sockio-server/node_modules/express/node_modules/commander/package.json create mode 100644 sockio-server/node_modules/express/node_modules/connect/.npmignore create mode 100644 sockio-server/node_modules/express/node_modules/connect/.travis.yml create mode 100644 sockio-server/node_modules/express/node_modules/connect/LICENSE create mode 100644 sockio-server/node_modules/express/node_modules/connect/Readme.md create mode 100644 sockio-server/node_modules/express/node_modules/connect/index.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/cache.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/connect.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/index.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/basicAuth.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/bodyParser.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/compress.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/cookieParser.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/cookieSession.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/csrf.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/directory.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/errorHandler.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/favicon.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/json.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/limit.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/logger.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/methodOverride.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/multipart.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/query.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/responseTime.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/session.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/session/cookie.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/session/memory.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/session/session.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/session/store.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/static.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/staticCache.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/timeout.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/urlencoded.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/middleware/vhost.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/patch.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/proto.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/directory.html create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/error.html create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/favicon.ico create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_add.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_attach.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_code.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_copy.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_delete.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_edit.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_error.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_excel.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_find.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_gear.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_go.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_green.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_key.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_lightning.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_link.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_paintbrush.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_paste.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_red.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_refresh.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_save.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_acrobat.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_actionscript.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_add.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_c.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_camera.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_cd.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_code.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_code_red.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_coldfusion.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_compressed.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_copy.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_cplusplus.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_csharp.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_cup.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_database.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_delete.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_dvd.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_edit.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_error.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_excel.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_find.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_flash.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_freehand.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_gear.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_get.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_go.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_h.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_horizontal.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_key.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_lightning.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_link.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_magnify.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_medal.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_office.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_paint.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_paintbrush.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_paste.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_php.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_picture.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_powerpoint.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_put.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_ruby.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_stack.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_star.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_swoosh.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_text.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_text_width.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_tux.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_vector.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_visualstudio.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_width.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_word.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_world.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_wrench.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_white_zip.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_word.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/icons/page_world.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/public/style.css create mode 100644 sockio-server/node_modules/express/node_modules/connect/lib/utils.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/bytes/.npmignore create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/bytes/History.md create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/bytes/Makefile create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/bytes/Readme.md create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/bytes/component.json create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/bytes/index.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/bytes/package.json create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/.npmignore create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/.travis.yml create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/LICENSE create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/Readme.md create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/benchmark/bench-multipart-parser.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/example/json.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/example/post.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/example/upload.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/index.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/lib/file.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/lib/incoming_form.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/lib/index.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/lib/json_parser.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/lib/multipart_parser.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/lib/octet_parser.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/lib/querystring_parser.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/package.json create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/common.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/beta-sticker-1.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/binaryfile.tar.gz create mode 100755 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/blank.gif create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/funkyfilename.txt create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/menu_separator.png create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/plain.txt create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/info.md create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/encoding.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/misc.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/no-filename.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/preamble.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/special-chars-in-filename.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/workarounds.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/multipart.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/integration/test-fixtures.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/integration/test-json.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/integration/test-octet-stream.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/common.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/integration/test-multipart-parser.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-file.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-incoming-form.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-multipart-parser.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-querystring-parser.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/system/test-multi-video-upload.js create mode 100755 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/run.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/standalone/test-connection-aborted.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/standalone/test-content-transfer-encoding.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/standalone/test-issue-46.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/tools/base64.html create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/unit/test-file.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/test/unit/test-incoming-form.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/formidable/tool/record.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/pause/.npmignore create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/pause/History.md create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/pause/Makefile create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/pause/Readme.md create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/pause/index.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/pause/package.json create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/qs/.gitmodules create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/qs/.npmignore create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/qs/Readme.md create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/qs/index.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/qs/package.json create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/uid2/index.js create mode 100644 sockio-server/node_modules/express/node_modules/connect/node_modules/uid2/package.json create mode 100644 sockio-server/node_modules/express/node_modules/connect/package.json create mode 100644 sockio-server/node_modules/express/node_modules/cookie-signature/.npmignore create mode 100644 sockio-server/node_modules/express/node_modules/cookie-signature/History.md create mode 100644 sockio-server/node_modules/express/node_modules/cookie-signature/Makefile create mode 100644 sockio-server/node_modules/express/node_modules/cookie-signature/Readme.md create mode 100644 sockio-server/node_modules/express/node_modules/cookie-signature/index.js create mode 100644 sockio-server/node_modules/express/node_modules/cookie-signature/package.json create mode 100644 sockio-server/node_modules/express/node_modules/cookie/.npmignore create mode 100644 sockio-server/node_modules/express/node_modules/cookie/.travis.yml create mode 100644 sockio-server/node_modules/express/node_modules/cookie/LICENSE create mode 100644 sockio-server/node_modules/express/node_modules/cookie/README.md create mode 100644 sockio-server/node_modules/express/node_modules/cookie/index.js create mode 100644 sockio-server/node_modules/express/node_modules/cookie/package.json create mode 100644 sockio-server/node_modules/express/node_modules/cookie/test/mocha.opts create mode 100644 sockio-server/node_modules/express/node_modules/cookie/test/parse.js create mode 100644 sockio-server/node_modules/express/node_modules/cookie/test/serialize.js create mode 100644 sockio-server/node_modules/express/node_modules/debug/.npmignore create mode 100644 sockio-server/node_modules/express/node_modules/debug/History.md create mode 100644 sockio-server/node_modules/express/node_modules/debug/Readme.md create mode 100644 sockio-server/node_modules/express/node_modules/debug/component.json create mode 100644 sockio-server/node_modules/express/node_modules/debug/debug.js create mode 100644 sockio-server/node_modules/express/node_modules/debug/example/app.js create mode 100644 sockio-server/node_modules/express/node_modules/debug/example/browser.html create mode 100644 sockio-server/node_modules/express/node_modules/debug/example/wildcards.js create mode 100644 sockio-server/node_modules/express/node_modules/debug/example/worker.js create mode 100644 sockio-server/node_modules/express/node_modules/debug/index.js create mode 100644 sockio-server/node_modules/express/node_modules/debug/lib/debug.js create mode 100644 sockio-server/node_modules/express/node_modules/debug/package.json create mode 100644 sockio-server/node_modules/express/node_modules/fresh/.npmignore create mode 100644 sockio-server/node_modules/express/node_modules/fresh/History.md create mode 100644 sockio-server/node_modules/express/node_modules/fresh/Makefile create mode 100644 sockio-server/node_modules/express/node_modules/fresh/Readme.md create mode 100644 sockio-server/node_modules/express/node_modules/fresh/index.js create mode 100644 sockio-server/node_modules/express/node_modules/fresh/package.json create mode 100644 sockio-server/node_modules/express/node_modules/methods/index.js create mode 100644 sockio-server/node_modules/express/node_modules/methods/package.json create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/.npmignore create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/.travis.yml create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/LICENSE create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/examples/pow.js create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/index.js create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/package.json create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/readme.markdown create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/test/chmod.js create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/test/clobber.js create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/test/mkdirp.js create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/test/perm.js create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/test/perm_sync.js create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/test/race.js create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/test/rel.js create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/test/return.js create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/test/return_sync.js create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/test/root.js create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/test/sync.js create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/test/umask.js create mode 100644 sockio-server/node_modules/express/node_modules/mkdirp/test/umask_sync.js create mode 100644 sockio-server/node_modules/express/node_modules/range-parser/.npmignore create mode 100644 sockio-server/node_modules/express/node_modules/range-parser/History.md create mode 100644 sockio-server/node_modules/express/node_modules/range-parser/Makefile create mode 100644 sockio-server/node_modules/express/node_modules/range-parser/Readme.md create mode 100644 sockio-server/node_modules/express/node_modules/range-parser/index.js create mode 100644 sockio-server/node_modules/express/node_modules/range-parser/package.json create mode 100644 sockio-server/node_modules/express/node_modules/send/.npmignore create mode 100644 sockio-server/node_modules/express/node_modules/send/History.md create mode 100644 sockio-server/node_modules/express/node_modules/send/Makefile create mode 100644 sockio-server/node_modules/express/node_modules/send/Readme.md create mode 100644 sockio-server/node_modules/express/node_modules/send/index.js create mode 100644 sockio-server/node_modules/express/node_modules/send/lib/send.js create mode 100644 sockio-server/node_modules/express/node_modules/send/lib/utils.js create mode 100644 sockio-server/node_modules/express/node_modules/send/node_modules/mime/LICENSE create mode 100644 sockio-server/node_modules/express/node_modules/send/node_modules/mime/README.md create mode 100644 sockio-server/node_modules/express/node_modules/send/node_modules/mime/mime.js create mode 100644 sockio-server/node_modules/express/node_modules/send/node_modules/mime/package.json create mode 100644 sockio-server/node_modules/express/node_modules/send/node_modules/mime/test.js create mode 100644 sockio-server/node_modules/express/node_modules/send/node_modules/mime/types/mime.types create mode 100644 sockio-server/node_modules/express/node_modules/send/node_modules/mime/types/node.types create mode 100644 sockio-server/node_modules/express/node_modules/send/package.json create mode 100644 sockio-server/node_modules/express/package.json create mode 100644 sockio-server/node_modules/express/test.js create mode 100644 sockio-server/node_modules/redis/.npmignore create mode 100644 sockio-server/node_modules/redis/README.md create mode 100644 sockio-server/node_modules/redis/benches/buffer_bench.js create mode 100644 sockio-server/node_modules/redis/benches/hiredis_parser.js create mode 100644 sockio-server/node_modules/redis/benches/re_sub_test.js create mode 100644 sockio-server/node_modules/redis/benches/reconnect_test.js create mode 100644 sockio-server/node_modules/redis/benches/stress/codec.js create mode 100644 sockio-server/node_modules/redis/benches/stress/pubsub/pub.js create mode 100755 sockio-server/node_modules/redis/benches/stress/pubsub/run create mode 100644 sockio-server/node_modules/redis/benches/stress/pubsub/server.js create mode 100644 sockio-server/node_modules/redis/benches/stress/rpushblpop/pub.js create mode 100755 sockio-server/node_modules/redis/benches/stress/rpushblpop/run create mode 100644 sockio-server/node_modules/redis/benches/stress/rpushblpop/server.js create mode 100644 sockio-server/node_modules/redis/benches/stress/speed/00 create mode 100755 sockio-server/node_modules/redis/benches/stress/speed/plot create mode 100644 sockio-server/node_modules/redis/benches/stress/speed/size-rate.png create mode 100644 sockio-server/node_modules/redis/benches/stress/speed/speed.js create mode 100644 sockio-server/node_modules/redis/benches/sub_quit_test.js create mode 100644 sockio-server/node_modules/redis/changelog.md create mode 100755 sockio-server/node_modules/redis/diff_multi_bench_output.js create mode 100644 sockio-server/node_modules/redis/examples/auth.js create mode 100644 sockio-server/node_modules/redis/examples/backpressure_drain.js create mode 100644 sockio-server/node_modules/redis/examples/eval.js create mode 100644 sockio-server/node_modules/redis/examples/extend.js create mode 100644 sockio-server/node_modules/redis/examples/file.js create mode 100644 sockio-server/node_modules/redis/examples/mget.js create mode 100644 sockio-server/node_modules/redis/examples/monitor.js create mode 100644 sockio-server/node_modules/redis/examples/multi.js create mode 100644 sockio-server/node_modules/redis/examples/multi2.js create mode 100644 sockio-server/node_modules/redis/examples/psubscribe.js create mode 100644 sockio-server/node_modules/redis/examples/pub_sub.js create mode 100644 sockio-server/node_modules/redis/examples/simple.js create mode 100644 sockio-server/node_modules/redis/examples/sort.js create mode 100644 sockio-server/node_modules/redis/examples/subqueries.js create mode 100644 sockio-server/node_modules/redis/examples/subquery.js create mode 100644 sockio-server/node_modules/redis/examples/unix_socket.js create mode 100644 sockio-server/node_modules/redis/examples/web_server.js create mode 100644 sockio-server/node_modules/redis/generate_commands.js create mode 100644 sockio-server/node_modules/redis/index.js create mode 100644 sockio-server/node_modules/redis/lib/commands.js create mode 100644 sockio-server/node_modules/redis/lib/parser/hiredis.js create mode 100644 sockio-server/node_modules/redis/lib/parser/javascript.js create mode 100644 sockio-server/node_modules/redis/lib/queue.js create mode 100644 sockio-server/node_modules/redis/lib/to_array.js create mode 100644 sockio-server/node_modules/redis/lib/util.js create mode 100644 sockio-server/node_modules/redis/multi_bench.js create mode 100644 sockio-server/node_modules/redis/package.json create mode 100644 sockio-server/node_modules/redis/test.js create mode 100644 sockio-server/node_modules/socket.io/.npmignore create mode 100644 sockio-server/node_modules/socket.io/.travis.yml create mode 100644 sockio-server/node_modules/socket.io/History.md create mode 100644 sockio-server/node_modules/socket.io/LICENSE create mode 100644 sockio-server/node_modules/socket.io/Makefile create mode 100644 sockio-server/node_modules/socket.io/Readme.md create mode 100644 sockio-server/node_modules/socket.io/benchmarks/decode.bench.js create mode 100644 sockio-server/node_modules/socket.io/benchmarks/encode.bench.js create mode 100644 sockio-server/node_modules/socket.io/benchmarks/runner.js create mode 100644 sockio-server/node_modules/socket.io/index.js create mode 100644 sockio-server/node_modules/socket.io/lib/logger.js create mode 100644 sockio-server/node_modules/socket.io/lib/manager.js create mode 100644 sockio-server/node_modules/socket.io/lib/namespace.js create mode 100644 sockio-server/node_modules/socket.io/lib/parser.js create mode 100644 sockio-server/node_modules/socket.io/lib/socket.io.js create mode 100644 sockio-server/node_modules/socket.io/lib/socket.js create mode 100644 sockio-server/node_modules/socket.io/lib/static.js create mode 100644 sockio-server/node_modules/socket.io/lib/store.js create mode 100644 sockio-server/node_modules/socket.io/lib/stores/memory.js create mode 100644 sockio-server/node_modules/socket.io/lib/stores/redis.js create mode 100644 sockio-server/node_modules/socket.io/lib/transport.js create mode 100644 sockio-server/node_modules/socket.io/lib/transports/flashsocket.js create mode 100644 sockio-server/node_modules/socket.io/lib/transports/htmlfile.js create mode 100644 sockio-server/node_modules/socket.io/lib/transports/http-polling.js create mode 100644 sockio-server/node_modules/socket.io/lib/transports/http.js create mode 100644 sockio-server/node_modules/socket.io/lib/transports/index.js create mode 100644 sockio-server/node_modules/socket.io/lib/transports/jsonp-polling.js create mode 100644 sockio-server/node_modules/socket.io/lib/transports/websocket.js create mode 100644 sockio-server/node_modules/socket.io/lib/transports/websocket/default.js create mode 100644 sockio-server/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js create mode 100644 sockio-server/node_modules/socket.io/lib/transports/websocket/hybi-16.js create mode 100644 sockio-server/node_modules/socket.io/lib/transports/websocket/index.js create mode 100644 sockio-server/node_modules/socket.io/lib/transports/xhr-polling.js create mode 100644 sockio-server/node_modules/socket.io/lib/util.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/base64id/.npmignore create mode 100644 sockio-server/node_modules/socket.io/node_modules/base64id/README.md create mode 100644 sockio-server/node_modules/socket.io/node_modules/base64id/lib/base64id.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/base64id/package.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/policyfile/.npmignore create mode 100644 sockio-server/node_modules/socket.io/node_modules/policyfile/LICENSE create mode 100644 sockio-server/node_modules/socket.io/node_modules/policyfile/Makefile create mode 100644 sockio-server/node_modules/socket.io/node_modules/policyfile/README.md create mode 100644 sockio-server/node_modules/socket.io/node_modules/policyfile/doc/index.html create mode 100644 sockio-server/node_modules/socket.io/node_modules/policyfile/examples/basic.fallback.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/policyfile/examples/basic.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/policyfile/index.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/policyfile/lib/server.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/policyfile/package.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/policyfile/tests/ssl/ssl.crt create mode 100644 sockio-server/node_modules/socket.io/node_modules/policyfile/tests/ssl/ssl.private.key create mode 100644 sockio-server/node_modules/socket.io/node_modules/policyfile/tests/unit.test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/.npmignore create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/README.md create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/benches/buffer_bench.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/benches/hiredis_parser.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/benches/re_sub_test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/benches/reconnect_test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/benches/stress/codec.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/benches/stress/pubsub/pub.js create mode 100755 sockio-server/node_modules/socket.io/node_modules/redis/benches/stress/pubsub/run create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/benches/stress/pubsub/server.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/benches/stress/rpushblpop/pub.js create mode 100755 sockio-server/node_modules/socket.io/node_modules/redis/benches/stress/rpushblpop/run create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/benches/stress/rpushblpop/server.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/benches/stress/speed/00 create mode 100755 sockio-server/node_modules/socket.io/node_modules/redis/benches/stress/speed/plot create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/benches/stress/speed/size-rate.png create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/benches/stress/speed/speed.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/benches/sub_quit_test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/changelog.md create mode 100755 sockio-server/node_modules/socket.io/node_modules/redis/diff_multi_bench_output.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/examples/auth.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/examples/backpressure_drain.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/examples/eval.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/examples/extend.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/examples/file.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/examples/mget.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/examples/monitor.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/examples/multi.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/examples/multi2.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/examples/psubscribe.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/examples/pub_sub.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/examples/simple.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/examples/sort.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/examples/subqueries.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/examples/subquery.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/examples/unix_socket.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/examples/web_server.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/generate_commands.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/index.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/lib/commands.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/lib/parser/hiredis.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/lib/parser/javascript.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/lib/queue.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/lib/to_array.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/lib/util.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/mem.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/multi_bench.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/package.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/redis/test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/.npmignore create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/History.md create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/Makefile create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/README.md create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/bin/builder.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/component-bind/component.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/component-bind/index.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/component-emitter/component.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/component-emitter/index.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/component-json-fallback/component.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/component-json-fallback/index.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/component-json/component.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/component-json/index.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/learnboost-engine.io-client/component.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/learnboost-engine.io-client/lib/emitter.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/learnboost-engine.io-client/lib/index.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/learnboost-engine.io-client/lib/parser.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/learnboost-engine.io-client/lib/socket.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/learnboost-engine.io-client/lib/transport.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/learnboost-engine.io-client/lib/transports/flashsocket.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/learnboost-engine.io-client/lib/transports/index.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/learnboost-engine.io-client/lib/transports/polling-jsonp.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/learnboost-engine.io-client/lib/transports/polling-xhr.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/learnboost-engine.io-client/lib/transports/polling.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/learnboost-engine.io-client/lib/transports/websocket.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/learnboost-engine.io-client/lib/util.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/learnboost-socket.io-protocol/component.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/learnboost-socket.io-protocol/index.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/timoxley-to-array/component.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/timoxley-to-array/index.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/visionmedia-debug/component.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/visionmedia-debug/debug.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/components/visionmedia-debug/index.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/dist/WebSocketMain.swf create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/dist/WebSocketMainInsecure.swf create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/dist/socket.io.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/dist/socket.io.min.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/events.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/io.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/json.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/namespace.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/parser.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/socket.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/transport.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/transports/flashsocket.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/transports/htmlfile.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/transports/jsonp-polling.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/transports/websocket.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/transports/xhr-polling.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/transports/xhr.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/util.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/.npmignore create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/README.md create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/WebSocketMain.swf create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/WebSocketMainInsecure.zip create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/IWebSocketLogger.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/WebSocket.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/WebSocketEvent.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/WebSocketMain.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/WebSocketMainInsecure.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/build.sh create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/adobe/net/proxies/RFC2817Socket.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/gsolo/encryption/MD5.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/Crypto.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/cert/MozillaRootCertificates.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/cert/X509Certificate.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/cert/X509CertificateCollection.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/HMAC.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/IHMAC.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/IHash.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/MAC.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/MD2.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/MD5.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/SHA1.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/SHA224.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/SHA256.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/SHABase.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/prng/ARC4.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/prng/IPRNG.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/prng/Random.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/prng/TLSPRF.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/rsa/RSAKey.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/AESKey.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/BlowFishKey.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/CBCMode.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/CFB8Mode.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/CFBMode.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/CTRMode.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/DESKey.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/ECBMode.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/ICipher.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/IMode.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/IPad.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/IStreamCipher.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/ISymmetricKey.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/IVMode.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/NullPad.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/OFBMode.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/PKCS5.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/SSLPad.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/SimpleIVMode.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/TLSPad.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/TripleDESKey.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/XTeaKey.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/aeskey.pl create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/dump.txt create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/AESKeyTest.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/ARC4Test.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/BigIntegerTest.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/BlowFishKeyTest.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/CBCModeTest.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/CFB8ModeTest.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/CFBModeTest.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/CTRModeTest.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/DESKeyTest.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/ECBModeTest.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/HMACTest.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/ITestHarness.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/MD2Test.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/MD5Test.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/OFBModeTest.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/RSAKeyTest.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/SHA1Test.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/SHA224Test.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/SHA256Test.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/TLSPRFTest.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/TestCase.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/TripleDESKeyTest.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/XTeaKeyTest.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/BulkCiphers.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/CipherSuites.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/IConnectionState.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/ISecurityParameters.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/KeyExchanges.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/MACs.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/SSLConnectionState.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/SSLEvent.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/SSLSecurityParameters.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSConfig.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSConnectionState.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSEngine.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSError.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSEvent.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSSecurityParameters.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSSocket.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSSocketEvent.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSTest.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/math/BarrettReduction.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/math/BigInteger.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/math/ClassicReduction.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/math/IReduction.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/math/MontgomeryReduction.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/math/NullReduction.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/math/bi_internal.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/util/ArrayUtil.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/util/Base64.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/util/Hex.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/util/Memory.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/util/der/ByteString.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/util/der/DER.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/util/der/IAsn1Type.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/util/der/Integer.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/util/der/OID.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/util/der/ObjectIdentifier.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/util/der/PEM.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/util/der/PrintableString.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/util/der/Sequence.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/util/der/Set.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/util/der/Type.as create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/util/der/UTCTime.as create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/sample.html create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/swfobject.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/lib/vendor/web-socket-js/web_socket.js create mode 120000 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/.bin/uglifyjs create mode 120000 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/.bin/wscat create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/.npmignore create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/Readme.md create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/index.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/.npmignore create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/LICENSE create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/README create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/Tokenizer.js create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/ZeParser.js create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/benchmark.html create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/index.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/package.json create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/test-parser.html create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/test-tokenizer.html create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/tests.js create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/unicodecategories.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/package.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/.npmignore create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/README.html create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/README.org create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/bin/uglifyjs create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/docstyle.css create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/lib/object-ast.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/lib/parse-js.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/lib/process.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/lib/squeeze-more.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/package.json create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/beautify.js create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/testparser.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/array1.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/array2.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/array3.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/array4.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/assignment.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/concatstring.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/const.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/empty-blocks.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/forstatement.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/if.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/ifreturn.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/ifreturn2.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue10.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue11.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue13.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue14.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue16.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue17.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue20.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue21.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue25.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue27.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue278.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue28.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue29.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue30.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue34.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue4.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue48.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue50.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue53.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue54.1.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue68.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue69.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/issue9.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/mangle.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/null_string.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/strict-equals.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/var.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/whitespace.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/expected/with.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/array1.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/array2.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/array3.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/array4.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/assignment.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/concatstring.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/const.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/empty-blocks.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/forstatement.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/if.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/ifreturn.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/ifreturn2.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue10.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue11.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue13.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue14.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue16.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue17.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue20.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue21.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue25.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue27.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue278.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue28.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue29.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue30.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue34.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue4.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue48.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue50.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue53.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue54.1.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue68.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue69.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue9.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/mangle.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/null_string.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/strict-equals.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/var.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/whitespace.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/with.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/scripts.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/269.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/app.js create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/embed-tokens.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/goto.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/goto2.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/hoist.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/instrument.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/instrument2.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/liftvars.js create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/uglify-hangs.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/uglify-hangs2.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/uglify-js.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/.npmignore create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/.travis.yml create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/History.md create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/Makefile create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/README.md create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bench/parser.benchmark.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bench/sender.benchmark.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bench/speed.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bench/util.js create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bin/wscat create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/binding.gyp create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Makefile create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/.deps/Release/bufferutil.node.d create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/.deps/Release/obj.target/bufferutil/src/bufferutil.o.d create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/.deps/Release/obj.target/validation/src/validation.o.d create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/.deps/Release/validation.node.d create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/bufferutil.node create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/linker.lock create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/obj.target/bufferutil/src/bufferutil.o create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/obj.target/validation/src/validation.o create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/validation.node create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/binding.Makefile create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/bufferutil.target.mk create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/config.gypi create mode 100755 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/gyp-mac-tool create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/validation.target.mk create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/builderror.log create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/doc/ws.md create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/.npmignore create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/package.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/public/app.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/public/index.html create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/public/uploader.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/server.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats-express_3/package.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats-express_3/public/index.html create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats-express_3/server.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats/package.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats/public/index.html create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats/server.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/ssl.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/index.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/BufferPool.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/BufferUtil.fallback.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/BufferUtil.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/ErrorCodes.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Receiver.hixie.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Receiver.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Sender.hixie.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Sender.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Validation.fallback.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Validation.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/WebSocket.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/WebSocketServer.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/browser.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/.npmignore create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/.travis.yml create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/History.md create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/Makefile create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/Readme.md create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/index.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/lib/commander.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/package.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/.index.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/LICENSE create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/README.md create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/nan.h create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/package.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/.npmignore create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/Makefile create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/README.md create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/lib/options.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/package.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/test/fixtures/test.conf create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/test/options.test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/.npmignore create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/README.md create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/example.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/package.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/tinycolor.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/package.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/src/bufferutil.cc create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/src/validation.cc create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/BufferPool.test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Receiver.hixie.test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Receiver.test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Sender.hixie.test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Sender.test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Validation.test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/WebSocket.integration.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/WebSocket.test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/WebSocketServer.test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/autobahn-server.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/autobahn.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/agent1-cert.pem create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/agent1-key.pem create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/ca1-cert.pem create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/ca1-key.pem create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/certificate.pem create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/key.pem create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/request.pem create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/textfile create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/hybi-common.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/testserver.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/README.md create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/autotest.watchr create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/example/demo.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/lib/XMLHttpRequest.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/package.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-constants.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-events.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-exceptions.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-headers.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-request-methods.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-request-protocols.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/testdata.txt create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/package.json create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/events.test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/io.test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/node/builder.common.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/node/builder.test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/parser.test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/socket.test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/util.test.js create mode 100644 sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/worker.js create mode 100644 sockio-server/node_modules/socket.io/package.json create mode 100644 sockio-server/package.json create mode 100644 sockio-server/views/index.html diff --git a/ASIHTTP/ASIAuthenticationDialog.h b/ASIHTTP/ASIAuthenticationDialog.h deleted file mode 100644 index 6bbb282..0000000 --- a/ASIHTTP/ASIAuthenticationDialog.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// ASIAuthenticationDialog.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 21/08/2009. -// Copyright 2009 All-Seeing Interactive. All rights reserved. -// - -#import -#import -@class ASIHTTPRequest; - -typedef enum _ASIAuthenticationType { - ASIStandardAuthenticationType = 0, - ASIProxyAuthenticationType = 1 -} ASIAuthenticationType; - -@interface ASIAutorotatingViewController : UIViewController -@end - -@interface ASIAuthenticationDialog : ASIAutorotatingViewController { - ASIHTTPRequest *request; - ASIAuthenticationType type; - UITableView *tableView; - UIViewController *presentingController; - BOOL didEnableRotationNotifications; -} -+ (void)presentAuthenticationDialogForRequest:(ASIHTTPRequest *)request; -+ (void)dismiss; - -@property (retain) ASIHTTPRequest *request; -@property (assign) ASIAuthenticationType type; -@property (assign) BOOL didEnableRotationNotifications; -@property (retain, nonatomic) UIViewController *presentingController; -@end diff --git a/ASIHTTP/ASIAuthenticationDialog.m b/ASIHTTP/ASIAuthenticationDialog.m deleted file mode 100644 index 47ea120..0000000 --- a/ASIHTTP/ASIAuthenticationDialog.m +++ /dev/null @@ -1,487 +0,0 @@ -// -// ASIAuthenticationDialog.m -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 21/08/2009. -// Copyright 2009 All-Seeing Interactive. All rights reserved. -// - -#import "ASIAuthenticationDialog.h" -#import "ASIHTTPRequest.h" -#import - -static ASIAuthenticationDialog *sharedDialog = nil; -BOOL isDismissing = NO; -static NSMutableArray *requestsNeedingAuthentication = nil; - -static const NSUInteger kUsernameRow = 0; -static const NSUInteger kUsernameSection = 0; -static const NSUInteger kPasswordRow = 1; -static const NSUInteger kPasswordSection = 0; -static const NSUInteger kDomainRow = 0; -static const NSUInteger kDomainSection = 1; - - -@implementation ASIAutorotatingViewController - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation -{ - return YES; -} - -@end - - -@interface ASIAuthenticationDialog () -- (void)showTitle; -- (void)show; -- (NSArray *)requestsRequiringTheseCredentials; -- (void)presentNextDialog; -- (void)keyboardWillShow:(NSNotification *)notification; -- (void)orientationChanged:(NSNotification *)notification; -- (void)cancelAuthenticationFromDialog:(id)sender; -- (void)loginWithCredentialsFromDialog:(id)sender; -@property (retain) UITableView *tableView; -@end - -@implementation ASIAuthenticationDialog - -#pragma mark init / dealloc - -+ (void)initialize -{ - if (self == [ASIAuthenticationDialog class]) { - requestsNeedingAuthentication = [[NSMutableArray array] retain]; - } -} - -+ (void)presentAuthenticationDialogForRequest:(ASIHTTPRequest *)theRequest -{ - // No need for a lock here, this will always be called on the main thread - if (!sharedDialog) { - sharedDialog = [[self alloc] init]; - [sharedDialog setRequest:theRequest]; - if ([theRequest authenticationNeeded] == ASIProxyAuthenticationNeeded) { - [sharedDialog setType:ASIProxyAuthenticationType]; - } else { - [sharedDialog setType:ASIStandardAuthenticationType]; - } - [sharedDialog show]; - } else { - [requestsNeedingAuthentication addObject:theRequest]; - } -} - -- (id)init -{ - if ((self = [self initWithNibName:nil bundle:nil])) { - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; - -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2 - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { -#endif - if (![UIDevice currentDevice].generatesDeviceOrientationNotifications) { - [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; - [self setDidEnableRotationNotifications:YES]; - } - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil]; -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2 - } -#endif - } - return self; -} - -- (void)dealloc -{ - if ([self didEnableRotationNotifications]) { - [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil]; - } - [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; - - [request release]; - [tableView release]; - [presentingController.view removeFromSuperview]; - [presentingController release]; - [super dealloc]; -} - -#pragma mark keyboard notifications - -- (void)keyboardWillShow:(NSNotification *)notification -{ -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2 - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { -#endif -#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_3_2 - NSValue *keyboardBoundsValue = [[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey]; -#else - NSValue *keyboardBoundsValue = [[notification userInfo] objectForKey:UIKeyboardBoundsUserInfoKey]; -#endif - CGRect keyboardBounds; - [keyboardBoundsValue getValue:&keyboardBounds]; - UIEdgeInsets e = UIEdgeInsetsMake(0, 0, keyboardBounds.size.height, 0); - [[self tableView] setScrollIndicatorInsets:e]; - [[self tableView] setContentInset:e]; -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2 - } -#endif -} - -// Manually handles orientation changes on iPhone -- (void)orientationChanged:(NSNotification *)notification -{ - [self showTitle]; - - UIInterfaceOrientation o = (UIInterfaceOrientation)[[UIApplication sharedApplication] statusBarOrientation]; - CGFloat angle = 0; - switch (o) { - case UIDeviceOrientationLandscapeLeft: angle = 90; break; - case UIDeviceOrientationLandscapeRight: angle = -90; break; - case UIDeviceOrientationPortraitUpsideDown: angle = 180; break; - default: break; - } - - CGRect f = [[UIScreen mainScreen] applicationFrame]; - - // Swap the frame height and width if necessary - if (UIDeviceOrientationIsLandscape(o)) { - CGFloat t; - t = f.size.width; - f.size.width = f.size.height; - f.size.height = t; - } - - CGAffineTransform previousTransform = self.view.layer.affineTransform; - CGAffineTransform newTransform = CGAffineTransformMakeRotation((CGFloat)(angle * M_PI / 180.0)); - - // Reset the transform so we can set the size - self.view.layer.affineTransform = CGAffineTransformIdentity; - self.view.frame = (CGRect){ { 0, 0 }, f.size}; - - // Revert to the previous transform for correct animation - self.view.layer.affineTransform = previousTransform; - - [UIView beginAnimations:nil context:NULL]; - [UIView setAnimationDuration:0.3]; - - // Set the new transform - self.view.layer.affineTransform = newTransform; - - // Fix the view origin - self.view.frame = (CGRect){ { f.origin.x, f.origin.y },self.view.frame.size}; - [UIView commitAnimations]; -} - -#pragma mark utilities - -- (UIViewController *)presentingController -{ - if (!presentingController) { - presentingController = [[ASIAutorotatingViewController alloc] initWithNibName:nil bundle:nil]; - - // Attach to the window, but don't interfere. - UIWindow *window = [[[UIApplication sharedApplication] windows] objectAtIndex:0]; - [window addSubview:[presentingController view]]; - [[presentingController view] setFrame:CGRectZero]; - [[presentingController view] setUserInteractionEnabled:NO]; - } - - return presentingController; -} - -- (UITextField *)textFieldInRow:(NSUInteger)row section:(NSUInteger)section -{ - return [[[[[self tableView] cellForRowAtIndexPath: - [NSIndexPath indexPathForRow:row inSection:section]] - contentView] subviews] objectAtIndex:0]; -} - -- (UITextField *)usernameField -{ - return [self textFieldInRow:kUsernameRow section:kUsernameSection]; -} - -- (UITextField *)passwordField -{ - return [self textFieldInRow:kPasswordRow section:kPasswordSection]; -} - -- (UITextField *)domainField -{ - return [self textFieldInRow:kDomainRow section:kDomainSection]; -} - -#pragma mark show / dismiss - -+ (void)dismiss -{ - [[sharedDialog parentViewController] dismissModalViewControllerAnimated:YES]; -} - -- (void)viewDidDisappear:(BOOL)animated -{ - [self retain]; - [sharedDialog release]; - sharedDialog = nil; - [self performSelector:@selector(presentNextDialog) withObject:nil afterDelay:0]; - [self release]; -} - -- (void)dismiss -{ - if (self == sharedDialog) { - [[self class] dismiss]; - } else { - [[self parentViewController] dismissModalViewControllerAnimated:YES]; - } -} - -- (void)showTitle -{ - UINavigationBar *navigationBar = [[[self view] subviews] objectAtIndex:0]; - UINavigationItem *navItem = [[navigationBar items] objectAtIndex:0]; - if (UIInterfaceOrientationIsPortrait([[UIDevice currentDevice] orientation])) { - // Setup the title - if ([self type] == ASIProxyAuthenticationType) { - [navItem setPrompt:@"Login to this secure proxy server."]; - } else { - [navItem setPrompt:@"Login to this secure server."]; - } - } else { - [navItem setPrompt:nil]; - } - [navigationBar sizeToFit]; - CGRect f = [[self view] bounds]; - f.origin.y = [navigationBar frame].size.height; - f.size.height -= f.origin.y; - [[self tableView] setFrame:f]; -} - -- (void)show -{ - // Remove all subviews - UIView *v; - while ((v = [[[self view] subviews] lastObject])) { - [v removeFromSuperview]; - } - - // Setup toolbar - UINavigationBar *bar = [[[UINavigationBar alloc] init] autorelease]; - [bar setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; - - UINavigationItem *navItem = [[[UINavigationItem alloc] init] autorelease]; - bar.items = [NSArray arrayWithObject:navItem]; - - [[self view] addSubview:bar]; - - [self showTitle]; - - // Setup toolbar buttons - if ([self type] == ASIProxyAuthenticationType) { - [navItem setTitle:[[self request] proxyHost]]; - } else { - [navItem setTitle:[[[self request] url] host]]; - } - - [navItem setLeftBarButtonItem:[[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelAuthenticationFromDialog:)] autorelease]]; - [navItem setRightBarButtonItem:[[[UIBarButtonItem alloc] initWithTitle:@"Login" style:UIBarButtonItemStyleDone target:self action:@selector(loginWithCredentialsFromDialog:)] autorelease]]; - - // We show the login form in a table view, similar to Safari's authentication dialog - [bar sizeToFit]; - CGRect f = [[self view] bounds]; - f.origin.y = [bar frame].size.height; - f.size.height -= f.origin.y; - - [self setTableView:[[[UITableView alloc] initWithFrame:f style:UITableViewStyleGrouped] autorelease]]; - [[self tableView] setDelegate:self]; - [[self tableView] setDataSource:self]; - [[self tableView] setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; - [[self view] addSubview:[self tableView]]; - - // Force reload the table content, and focus the first field to show the keyboard - [[self tableView] reloadData]; - [[[[[self tableView] cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]].contentView subviews] objectAtIndex:0] becomeFirstResponder]; - -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2 - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { - [self setModalPresentationStyle:UIModalPresentationFormSheet]; - } -#endif - - [[self presentingController] presentModalViewController:self animated:YES]; -} - -#pragma mark button callbacks - -- (void)cancelAuthenticationFromDialog:(id)sender -{ - for (ASIHTTPRequest *theRequest in [self requestsRequiringTheseCredentials]) { - [theRequest cancelAuthentication]; - [requestsNeedingAuthentication removeObject:theRequest]; - } - [self dismiss]; -} - -- (NSArray *)requestsRequiringTheseCredentials -{ - NSMutableArray *requestsRequiringTheseCredentials = [NSMutableArray array]; - NSURL *requestURL = [[self request] url]; - for (ASIHTTPRequest *otherRequest in requestsNeedingAuthentication) { - NSURL *theURL = [otherRequest url]; - if (([otherRequest authenticationNeeded] == [[self request] authenticationNeeded]) && [[theURL host] isEqualToString:[requestURL host]] && ([theURL port] == [requestURL port] || ([requestURL port] && [[theURL port] isEqualToNumber:[requestURL port]])) && [[theURL scheme] isEqualToString:[requestURL scheme]] && ((![otherRequest authenticationRealm] && ![[self request] authenticationRealm]) || ([otherRequest authenticationRealm] && [[self request] authenticationRealm] && [[[self request] authenticationRealm] isEqualToString:[otherRequest authenticationRealm]]))) { - [requestsRequiringTheseCredentials addObject:otherRequest]; - } - } - [requestsRequiringTheseCredentials addObject:[self request]]; - return requestsRequiringTheseCredentials; -} - -- (void)presentNextDialog -{ - if ([requestsNeedingAuthentication count]) { - ASIHTTPRequest *nextRequest = [requestsNeedingAuthentication objectAtIndex:0]; - [requestsNeedingAuthentication removeObjectAtIndex:0]; - [[self class] presentAuthenticationDialogForRequest:nextRequest]; - } -} - - -- (void)loginWithCredentialsFromDialog:(id)sender -{ - for (ASIHTTPRequest *theRequest in [self requestsRequiringTheseCredentials]) { - - NSString *username = [[self usernameField] text]; - NSString *password = [[self passwordField] text]; - - if (username == nil) { username = @""; } - if (password == nil) { password = @""; } - - if ([self type] == ASIProxyAuthenticationType) { - [theRequest setProxyUsername:username]; - [theRequest setProxyPassword:password]; - } else { - [theRequest setUsername:username]; - [theRequest setPassword:password]; - } - - // Handle NTLM domains - NSString *scheme = ([self type] == ASIStandardAuthenticationType) ? [[self request] authenticationScheme] : [[self request] proxyAuthenticationScheme]; - if ([scheme isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeNTLM]) { - NSString *domain = [[self domainField] text]; - if ([self type] == ASIProxyAuthenticationType) { - [theRequest setProxyDomain:domain]; - } else { - [theRequest setDomain:domain]; - } - } - - [theRequest retryUsingSuppliedCredentials]; - [requestsNeedingAuthentication removeObject:theRequest]; - } - [self dismiss]; -} - -#pragma mark table view data source - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)aTableView -{ - NSString *scheme = ([self type] == ASIStandardAuthenticationType) ? [[self request] authenticationScheme] : [[self request] proxyAuthenticationScheme]; - if ([scheme isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeNTLM]) { - return 2; - } - return 1; -} - -- (CGFloat)tableView:(UITableView *)aTableView heightForFooterInSection:(NSInteger)section -{ - if (section == [self numberOfSectionsInTableView:aTableView]-1) { - return 30; - } - return 0; -} - -- (CGFloat)tableView:(UITableView *)aTableView heightForHeaderInSection:(NSInteger)section -{ - if (section == 0) { -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2 - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { - return 54; - } -#endif - return 30; - } - return 0; -} - -- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section -{ - if (section == 0) { - return [[self request] authenticationRealm]; - } - return nil; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ -#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_3_0 - UITableViewCell *cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease]; -#else - UITableViewCell *cell = [[[UITableViewCell alloc] initWithFrame:CGRectMake(0,0,0,0) reuseIdentifier:nil] autorelease]; -#endif - - [cell setSelectionStyle:UITableViewCellSelectionStyleNone]; - - CGRect f = CGRectInset([cell bounds], 10, 10); - UITextField *textField = [[[UITextField alloc] initWithFrame:f] autorelease]; - [textField setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; - [textField setAutocapitalizationType:UITextAutocapitalizationTypeNone]; - [textField setAutocorrectionType:UITextAutocorrectionTypeNo]; - - NSUInteger s = [indexPath section]; - NSUInteger r = [indexPath row]; - - if (s == kUsernameSection && r == kUsernameRow) { - [textField setPlaceholder:@"User"]; - } else if (s == kPasswordSection && r == kPasswordRow) { - [textField setPlaceholder:@"Password"]; - [textField setSecureTextEntry:YES]; - } else if (s == kDomainSection && r == kDomainRow) { - [textField setPlaceholder:@"Domain"]; - } - [cell.contentView addSubview:textField]; - - return cell; -} - -- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section -{ - if (section == 0) { - return 2; - } else { - return 1; - } -} - -- (NSString *)tableView:(UITableView *)aTableView titleForFooterInSection:(NSInteger)section -{ - if (section == [self numberOfSectionsInTableView:aTableView]-1) { - // If we're using Basic authentication and the connection is not using SSL, we'll show the plain text message - if ([[[self request] authenticationScheme] isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeBasic] && ![[[[self request] url] scheme] isEqualToString:@"https"]) { - return @"Password will be sent in the clear."; - // We are using Digest, NTLM, or any scheme over SSL - } else { - return @"Password will be sent securely."; - } - } - return nil; -} - -#pragma mark - - -@synthesize request; -@synthesize type; -@synthesize tableView; -@synthesize didEnableRotationNotifications; -@synthesize presentingController; -@end diff --git a/ASIHTTP/ASICacheDelegate.h b/ASIHTTP/ASICacheDelegate.h deleted file mode 100644 index 060cda5..0000000 --- a/ASIHTTP/ASICacheDelegate.h +++ /dev/null @@ -1,103 +0,0 @@ -// -// ASICacheDelegate.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 01/05/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// - -#import -@class ASIHTTPRequest; - -// Cache policies control the behaviour of a cache and how requests use the cache -// When setting a cache policy, you can use a combination of these values as a bitmask -// For example: [request setCachePolicy:ASIAskServerIfModifiedCachePolicy|ASIFallbackToCacheIfLoadFailsCachePolicy|ASIDoNotWriteToCacheCachePolicy]; -// Note that some of the behaviours below are mutally exclusive - you cannot combine ASIAskServerIfModifiedWhenStaleCachePolicy and ASIAskServerIfModifiedCachePolicy, for example. -typedef enum _ASICachePolicy { - - // The default cache policy. When you set a request to use this, it will use the cache's defaultCachePolicy - // ASIDownloadCache's default cache policy is 'ASIAskServerIfModifiedWhenStaleCachePolicy' - ASIUseDefaultCachePolicy = 0, - - // Tell the request not to read from the cache - ASIDoNotReadFromCacheCachePolicy = 1, - - // The the request not to write to the cache - ASIDoNotWriteToCacheCachePolicy = 2, - - // Ask the server if there is an updated version of this resource (using a conditional GET) ONLY when the cached data is stale - ASIAskServerIfModifiedWhenStaleCachePolicy = 4, - - // Always ask the server if there is an updated version of this resource (using a conditional GET) - ASIAskServerIfModifiedCachePolicy = 8, - - // If cached data exists, use it even if it is stale. This means requests will not talk to the server unless the resource they are requesting is not in the cache - ASIOnlyLoadIfNotCachedCachePolicy = 16, - - // If cached data exists, use it even if it is stale. If cached data does not exist, stop (will not set an error on the request) - ASIDontLoadCachePolicy = 32, - - // Specifies that cached data may be used if the request fails. If cached data is used, the request will succeed without error. Usually used in combination with other options above. - ASIFallbackToCacheIfLoadFailsCachePolicy = 64 -} ASICachePolicy; - -// Cache storage policies control whether cached data persists between application launches (ASICachePermanentlyCacheStoragePolicy) or not (ASICacheForSessionDurationCacheStoragePolicy) -// Calling [ASIHTTPRequest clearSession] will remove any data stored using ASICacheForSessionDurationCacheStoragePolicy -typedef enum _ASICacheStoragePolicy { - ASICacheForSessionDurationCacheStoragePolicy = 0, - ASICachePermanentlyCacheStoragePolicy = 1 -} ASICacheStoragePolicy; - - -@protocol ASICacheDelegate - -@required - -// Should return the cache policy that will be used when requests have their cache policy set to ASIUseDefaultCachePolicy -- (ASICachePolicy)defaultCachePolicy; - -// Returns the date a cached response should expire on. Pass a non-zero max age to specify a custom date. -- (NSDate *)expiryDateForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge; - -// Updates cached response headers with a new expiry date. Pass a non-zero max age to specify a custom date. -- (void)updateExpiryForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge; - -// Looks at the request's cache policy and any cached headers to determine if the cache data is still valid -- (BOOL)canUseCachedDataForRequest:(ASIHTTPRequest *)request; - -// Removes cached data for a particular request -- (void)removeCachedDataForRequest:(ASIHTTPRequest *)request; - -// Should return YES if the cache considers its cached response current for the request -// Should return NO is the data is not cached, or (for example) if the cached headers state the request should have expired -- (BOOL)isCachedDataCurrentForRequest:(ASIHTTPRequest *)request; - -// Should store the response for the passed request in the cache -// When a non-zero maxAge is passed, it should be used as the expiry time for the cached response -- (void)storeResponseForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge; - -// Removes cached data for a particular url -- (void)removeCachedDataForURL:(NSURL *)url; - -// Should return an NSDictionary of cached headers for the passed URL, if it is stored in the cache -- (NSDictionary *)cachedResponseHeadersForURL:(NSURL *)url; - -// Should return the cached body of a response for the passed URL, if it is stored in the cache -- (NSData *)cachedResponseDataForURL:(NSURL *)url; - -// Returns a path to the cached response data, if it exists -- (NSString *)pathToCachedResponseDataForURL:(NSURL *)url; - -// Returns a path to the cached response headers, if they url -- (NSString *)pathToCachedResponseHeadersForURL:(NSURL *)url; - -// Returns the location to use to store cached response headers for a particular request -- (NSString *)pathToStoreCachedResponseHeadersForRequest:(ASIHTTPRequest *)request; - -// Returns the location to use to store a cached response body for a particular request -- (NSString *)pathToStoreCachedResponseDataForRequest:(ASIHTTPRequest *)request; - -// Clear cached data stored for the passed storage policy -- (void)clearCachedResponsesForStoragePolicy:(ASICacheStoragePolicy)cachePolicy; - -@end diff --git a/ASIHTTP/ASIDataCompressor.h b/ASIHTTP/ASIDataCompressor.h deleted file mode 100644 index ae0e441..0000000 --- a/ASIHTTP/ASIDataCompressor.h +++ /dev/null @@ -1,42 +0,0 @@ -// -// ASIDataCompressor.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 17/08/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// - -// This is a helper class used by ASIHTTPRequest to handle deflating (compressing) data in memory and on disk -// You may also find it helpful if you need to deflate data and files yourself - see the class methods below -// Most of the zlib stuff is based on the sample code by Mark Adler available at http://zlib.net - -#import -#import - -@interface ASIDataCompressor : NSObject { - BOOL streamReady; - z_stream zStream; -} - -// Convenience constructor will call setupStream for you -+ (id)compressor; - -// Compress the passed chunk of data -// Passing YES for shouldFinish will finalize the deflated data - you must pass YES when you are on the last chunk of data -- (NSData *)compressBytes:(Bytef *)bytes length:(NSUInteger)length error:(NSError **)err shouldFinish:(BOOL)shouldFinish; - -// Convenience method - pass it some data, and you'll get deflated data back -+ (NSData *)compressData:(NSData*)uncompressedData error:(NSError **)err; - -// Convenience method - pass it a file containing the data to compress in sourcePath, and it will write deflated data to destinationPath -+ (BOOL)compressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath error:(NSError **)err; - -// Sets up zlib to handle the inflating. You only need to call this yourself if you aren't using the convenience constructor 'compressor' -- (NSError *)setupStream; - -// Tells zlib to clean up. You need to call this if you need to cancel deflating part way through -// If deflating finishes or fails, this method will be called automatically -- (NSError *)closeStream; - -@property (assign, readonly) BOOL streamReady; -@end diff --git a/ASIHTTP/ASIDataCompressor.m b/ASIHTTP/ASIDataCompressor.m deleted file mode 100644 index d34687b..0000000 --- a/ASIHTTP/ASIDataCompressor.m +++ /dev/null @@ -1,219 +0,0 @@ -// -// ASIDataCompressor.m -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 17/08/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// - -#import "ASIDataCompressor.h" -#import "ASIHTTPRequest.h" - -#define DATA_CHUNK_SIZE 262144 // Deal with gzipped data in 256KB chunks -#define COMPRESSION_AMOUNT Z_DEFAULT_COMPRESSION - -@interface ASIDataCompressor () -+ (NSError *)deflateErrorWithCode:(int)code; -@end - -@implementation ASIDataCompressor - -+ (id)compressor -{ - ASIDataCompressor *compressor = [[[self alloc] init] autorelease]; - [compressor setupStream]; - return compressor; -} - -- (void)dealloc -{ - if (streamReady) { - [self closeStream]; - } - [super dealloc]; -} - -- (NSError *)setupStream -{ - if (streamReady) { - return nil; - } - // Setup the inflate stream - zStream.zalloc = Z_NULL; - zStream.zfree = Z_NULL; - zStream.opaque = Z_NULL; - zStream.avail_in = 0; - zStream.next_in = 0; - int status = deflateInit2(&zStream, COMPRESSION_AMOUNT, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY); - if (status != Z_OK) { - return [[self class] deflateErrorWithCode:status]; - } - streamReady = YES; - return nil; -} - -- (NSError *)closeStream -{ - if (!streamReady) { - return nil; - } - // Close the deflate stream - streamReady = NO; - int status = deflateEnd(&zStream); - if (status != Z_OK) { - return [[self class] deflateErrorWithCode:status]; - } - return nil; -} - -- (NSData *)compressBytes:(Bytef *)bytes length:(NSUInteger)length error:(NSError **)err shouldFinish:(BOOL)shouldFinish -{ - if (length == 0) return nil; - - NSUInteger halfLength = length/2; - - // We'll take a guess that the compressed data will fit in half the size of the original (ie the max to compress at once is half DATA_CHUNK_SIZE), if not, we'll increase it below - NSMutableData *outputData = [NSMutableData dataWithLength:length/2]; - - int status; - - zStream.next_in = bytes; - zStream.avail_in = (unsigned int)length; - zStream.avail_out = 0; - - NSInteger bytesProcessedAlready = zStream.total_out; - while (zStream.avail_out == 0) { - - if (zStream.total_out-bytesProcessedAlready >= [outputData length]) { - [outputData increaseLengthBy:halfLength]; - } - - zStream.next_out = (Bytef*)[outputData mutableBytes] + zStream.total_out-bytesProcessedAlready; - zStream.avail_out = (unsigned int)([outputData length] - (zStream.total_out-bytesProcessedAlready)); - status = deflate(&zStream, shouldFinish ? Z_FINISH : Z_NO_FLUSH); - - if (status == Z_STREAM_END) { - break; - } else if (status != Z_OK) { - if (err) { - *err = [[self class] deflateErrorWithCode:status]; - } - return NO; - } - } - - // Set real length - [outputData setLength: zStream.total_out-bytesProcessedAlready]; - return outputData; -} - - -+ (NSData *)compressData:(NSData*)uncompressedData error:(NSError **)err -{ - NSError *theError = nil; - NSData *outputData = [[ASIDataCompressor compressor] compressBytes:(Bytef *)[uncompressedData bytes] length:[uncompressedData length] error:&theError shouldFinish:YES]; - if (theError) { - if (err) { - *err = theError; - } - return nil; - } - return outputData; -} - - - -+ (BOOL)compressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath error:(NSError **)err -{ - NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease]; - - // Create an empty file at the destination path - if (![fileManager createFileAtPath:destinationPath contents:[NSData data] attributes:nil]) { - if (err) { - *err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Compression of %@ failed because we were to create a file at %@",sourcePath,destinationPath],NSLocalizedDescriptionKey,nil]]; - } - return NO; - } - - // Ensure the source file exists - if (![fileManager fileExistsAtPath:sourcePath]) { - if (err) { - *err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Compression of %@ failed the file does not exist",sourcePath],NSLocalizedDescriptionKey,nil]]; - } - return NO; - } - - UInt8 inputData[DATA_CHUNK_SIZE]; - NSData *outputData; - NSInteger readLength; - NSError *theError = nil; - - ASIDataCompressor *compressor = [ASIDataCompressor compressor]; - - NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:sourcePath]; - [inputStream open]; - NSOutputStream *outputStream = [NSOutputStream outputStreamToFileAtPath:destinationPath append:NO]; - [outputStream open]; - - while ([compressor streamReady]) { - - // Read some data from the file - readLength = [inputStream read:inputData maxLength:DATA_CHUNK_SIZE]; - - // Make sure nothing went wrong - if ([inputStream streamStatus] == NSStreamEventErrorOccurred) { - if (err) { - *err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Compression of %@ failed because we were unable to read from the source data file",sourcePath],NSLocalizedDescriptionKey,[inputStream streamError],NSUnderlyingErrorKey,nil]]; - } - [compressor closeStream]; - return NO; - } - // Have we reached the end of the input data? - if (!readLength) { - break; - } - - // Attempt to deflate the chunk of data - outputData = [compressor compressBytes:inputData length:readLength error:&theError shouldFinish:readLength < DATA_CHUNK_SIZE ]; - if (theError) { - if (err) { - *err = theError; - } - [compressor closeStream]; - return NO; - } - - // Write the deflated data out to the destination file - [outputStream write:(const uint8_t *)[outputData bytes] maxLength:[outputData length]]; - - // Make sure nothing went wrong - if ([inputStream streamStatus] == NSStreamEventErrorOccurred) { - if (err) { - *err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Compression of %@ failed because we were unable to write to the destination data file at %@",sourcePath,destinationPath],NSLocalizedDescriptionKey,[outputStream streamError],NSUnderlyingErrorKey,nil]]; - } - [compressor closeStream]; - return NO; - } - - } - [inputStream close]; - [outputStream close]; - - NSError *error = [compressor closeStream]; - if (error) { - if (err) { - *err = error; - } - return NO; - } - - return YES; -} - -+ (NSError *)deflateErrorWithCode:(int)code -{ - return [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Compression of data failed with code %hi",code],NSLocalizedDescriptionKey,nil]]; -} - -@synthesize streamReady; -@end diff --git a/ASIHTTP/ASIDataDecompressor.h b/ASIHTTP/ASIDataDecompressor.h deleted file mode 100644 index 8be8f9b..0000000 --- a/ASIHTTP/ASIDataDecompressor.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// ASIDataDecompressor.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 17/08/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// - -// This is a helper class used by ASIHTTPRequest to handle inflating (decompressing) data in memory and on disk -// You may also find it helpful if you need to inflate data and files yourself - see the class methods below -// Most of the zlib stuff is based on the sample code by Mark Adler available at http://zlib.net - -#import -#import - -@interface ASIDataDecompressor : NSObject { - BOOL streamReady; - z_stream zStream; -} - -// Convenience constructor will call setupStream for you -+ (id)decompressor; - -// Uncompress the passed chunk of data -- (NSData *)uncompressBytes:(Bytef *)bytes length:(NSUInteger)length error:(NSError **)err; - -// Convenience method - pass it some deflated data, and you'll get inflated data back -+ (NSData *)uncompressData:(NSData*)compressedData error:(NSError **)err; - -// Convenience method - pass it a file containing deflated data in sourcePath, and it will write inflated data to destinationPath -+ (BOOL)uncompressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath error:(NSError **)err; - -// Sets up zlib to handle the inflating. You only need to call this yourself if you aren't using the convenience constructor 'decompressor' -- (NSError *)setupStream; - -// Tells zlib to clean up. You need to call this if you need to cancel inflating part way through -// If inflating finishes or fails, this method will be called automatically -- (NSError *)closeStream; - -@property (assign, readonly) BOOL streamReady; -@end diff --git a/ASIHTTP/ASIDataDecompressor.m b/ASIHTTP/ASIDataDecompressor.m deleted file mode 100644 index 25e7e55..0000000 --- a/ASIHTTP/ASIDataDecompressor.m +++ /dev/null @@ -1,218 +0,0 @@ -// -// ASIDataDecompressor.m -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 17/08/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// - -#import "ASIDataDecompressor.h" -#import "ASIHTTPRequest.h" - -#define DATA_CHUNK_SIZE 262144 // Deal with gzipped data in 256KB chunks - -@interface ASIDataDecompressor () -+ (NSError *)inflateErrorWithCode:(int)code; -@end; - -@implementation ASIDataDecompressor - -+ (id)decompressor -{ - ASIDataDecompressor *decompressor = [[[self alloc] init] autorelease]; - [decompressor setupStream]; - return decompressor; -} - -- (void)dealloc -{ - if (streamReady) { - [self closeStream]; - } - [super dealloc]; -} - -- (NSError *)setupStream -{ - if (streamReady) { - return nil; - } - // Setup the inflate stream - zStream.zalloc = Z_NULL; - zStream.zfree = Z_NULL; - zStream.opaque = Z_NULL; - zStream.avail_in = 0; - zStream.next_in = 0; - int status = inflateInit2(&zStream, (15+32)); - if (status != Z_OK) { - return [[self class] inflateErrorWithCode:status]; - } - streamReady = YES; - return nil; -} - -- (NSError *)closeStream -{ - if (!streamReady) { - return nil; - } - // Close the inflate stream - streamReady = NO; - int status = inflateEnd(&zStream); - if (status != Z_OK) { - return [[self class] inflateErrorWithCode:status]; - } - return nil; -} - -- (NSData *)uncompressBytes:(Bytef *)bytes length:(NSUInteger)length error:(NSError **)err -{ - if (length == 0) return nil; - - NSUInteger halfLength = length/2; - NSMutableData *outputData = [NSMutableData dataWithLength:length+halfLength]; - - int status; - - zStream.next_in = bytes; - zStream.avail_in = (unsigned int)length; - zStream.avail_out = 0; - - NSInteger bytesProcessedAlready = zStream.total_out; - while (zStream.avail_in != 0) { - - if (zStream.total_out-bytesProcessedAlready >= [outputData length]) { - [outputData increaseLengthBy:halfLength]; - } - - zStream.next_out = (Bytef*)[outputData mutableBytes] + zStream.total_out-bytesProcessedAlready; - zStream.avail_out = (unsigned int)([outputData length] - (zStream.total_out-bytesProcessedAlready)); - - status = inflate(&zStream, Z_NO_FLUSH); - - if (status == Z_STREAM_END) { - break; - } else if (status != Z_OK) { - if (err) { - *err = [[self class] inflateErrorWithCode:status]; - } - return nil; - } - } - - // Set real length - [outputData setLength: zStream.total_out-bytesProcessedAlready]; - return outputData; -} - - -+ (NSData *)uncompressData:(NSData*)compressedData error:(NSError **)err -{ - NSError *theError = nil; - NSData *outputData = [[ASIDataDecompressor decompressor] uncompressBytes:(Bytef *)[compressedData bytes] length:[compressedData length] error:&theError]; - if (theError) { - if (err) { - *err = theError; - } - return nil; - } - return outputData; -} - -+ (BOOL)uncompressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath error:(NSError **)err -{ - NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease]; - - // Create an empty file at the destination path - if (![fileManager createFileAtPath:destinationPath contents:[NSData data] attributes:nil]) { - if (err) { - *err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of %@ failed because we were to create a file at %@",sourcePath,destinationPath],NSLocalizedDescriptionKey,nil]]; - } - return NO; - } - - // Ensure the source file exists - if (![fileManager fileExistsAtPath:sourcePath]) { - if (err) { - *err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of %@ failed the file does not exist",sourcePath],NSLocalizedDescriptionKey,nil]]; - } - return NO; - } - - UInt8 inputData[DATA_CHUNK_SIZE]; - NSData *outputData; - NSInteger readLength; - NSError *theError = nil; - - - ASIDataDecompressor *decompressor = [ASIDataDecompressor decompressor]; - - NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:sourcePath]; - [inputStream open]; - NSOutputStream *outputStream = [NSOutputStream outputStreamToFileAtPath:destinationPath append:NO]; - [outputStream open]; - - while ([decompressor streamReady]) { - - // Read some data from the file - readLength = [inputStream read:inputData maxLength:DATA_CHUNK_SIZE]; - - // Make sure nothing went wrong - if ([inputStream streamStatus] == NSStreamEventErrorOccurred) { - if (err) { - *err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of %@ failed because we were unable to read from the source data file",sourcePath],NSLocalizedDescriptionKey,[inputStream streamError],NSUnderlyingErrorKey,nil]]; - } - [decompressor closeStream]; - return NO; - } - // Have we reached the end of the input data? - if (!readLength) { - break; - } - - // Attempt to inflate the chunk of data - outputData = [decompressor uncompressBytes:inputData length:readLength error:&theError]; - if (theError) { - if (err) { - *err = theError; - } - [decompressor closeStream]; - return NO; - } - - // Write the inflated data out to the destination file - [outputStream write:(Bytef*)[outputData bytes] maxLength:[outputData length]]; - - // Make sure nothing went wrong - if ([inputStream streamStatus] == NSStreamEventErrorOccurred) { - if (err) { - *err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of %@ failed because we were unable to write to the destination data file at %@",sourcePath,destinationPath],NSLocalizedDescriptionKey,[outputStream streamError],NSUnderlyingErrorKey,nil]]; - } - [decompressor closeStream]; - return NO; - } - - } - - [inputStream close]; - [outputStream close]; - - NSError *error = [decompressor closeStream]; - if (error) { - if (err) { - *err = error; - } - return NO; - } - - return YES; -} - - -+ (NSError *)inflateErrorWithCode:(int)code -{ - return [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of data failed with code %hi",code],NSLocalizedDescriptionKey,nil]]; -} - -@synthesize streamReady; -@end diff --git a/ASIHTTP/ASIDownloadCache.h b/ASIHTTP/ASIDownloadCache.h deleted file mode 100644 index a2df908..0000000 --- a/ASIHTTP/ASIDownloadCache.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// ASIDownloadCache.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 01/05/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// - -#import -#import "ASICacheDelegate.h" - -@interface ASIDownloadCache : NSObject { - - // The default cache policy for this cache - // Requests that store data in the cache will use this cache policy if their cache policy is set to ASIUseDefaultCachePolicy - // Defaults to ASIAskServerIfModifiedWhenStaleCachePolicy - ASICachePolicy defaultCachePolicy; - - // The directory in which cached data will be stored - // Defaults to a directory called 'ASIHTTPRequestCache' in the temporary directory - NSString *storagePath; - - // Mediates access to the cache - NSRecursiveLock *accessLock; - - // When YES, the cache will look for cache-control / pragma: no-cache headers, and won't reuse store responses if it finds them - BOOL shouldRespectCacheControlHeaders; -} - -// Returns a static instance of an ASIDownloadCache -// In most circumstances, it will make sense to use this as a global cache, rather than creating your own cache -// To make ASIHTTPRequests use it automatically, use [ASIHTTPRequest setDefaultCache:[ASIDownloadCache sharedCache]]; -+ (id)sharedCache; - -// A helper function that determines if the server has requested data should not be cached by looking at the request's response headers -+ (BOOL)serverAllowsResponseCachingForRequest:(ASIHTTPRequest *)request; - -// A list of file extensions that we know won't be readable by a webview when accessed locally -// If we're asking for a path to cache a particular url and it has one of these extensions, we change it to '.html' -+ (NSArray *)fileExtensionsToHandleAsHTML; - -@property (assign, nonatomic) ASICachePolicy defaultCachePolicy; -@property (retain, nonatomic) NSString *storagePath; -@property (retain) NSRecursiveLock *accessLock; -@property (assign) BOOL shouldRespectCacheControlHeaders; -@end diff --git a/ASIHTTP/ASIDownloadCache.m b/ASIHTTP/ASIDownloadCache.m deleted file mode 100644 index 93da36f..0000000 --- a/ASIHTTP/ASIDownloadCache.m +++ /dev/null @@ -1,514 +0,0 @@ -// -// ASIDownloadCache.m -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 01/05/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// - -#import "ASIDownloadCache.h" -#import "ASIHTTPRequest.h" -#import - -static ASIDownloadCache *sharedCache = nil; - -static NSString *sessionCacheFolder = @"SessionStore"; -static NSString *permanentCacheFolder = @"PermanentStore"; -static NSArray *fileExtensionsToHandleAsHTML = nil; - -@interface ASIDownloadCache () -+ (NSString *)keyForURL:(NSURL *)url; -- (NSString *)pathToFile:(NSString *)file; -@end - -@implementation ASIDownloadCache - -+ (void)initialize -{ - if (self == [ASIDownloadCache class]) { - // Obviously this is not an exhaustive list, but hopefully these are the most commonly used and this will 'just work' for the widest range of people - // I imagine many web developers probably use url rewriting anyway - fileExtensionsToHandleAsHTML = [[NSArray alloc] initWithObjects:@"asp",@"aspx",@"jsp",@"php",@"rb",@"py",@"pl",@"cgi", nil]; - } -} - -- (id)init -{ - self = [super init]; - [self setShouldRespectCacheControlHeaders:YES]; - [self setDefaultCachePolicy:ASIUseDefaultCachePolicy]; - [self setAccessLock:[[[NSRecursiveLock alloc] init] autorelease]]; - return self; -} - -+ (id)sharedCache -{ - if (!sharedCache) { - @synchronized(self) { - if (!sharedCache) { - sharedCache = [[self alloc] init]; - [sharedCache setStoragePath:[[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"ASIHTTPRequestCache"]]; - } - } - } - return sharedCache; -} - -- (void)dealloc -{ - [storagePath release]; - [accessLock release]; - [super dealloc]; -} - -- (NSString *)storagePath -{ - [[self accessLock] lock]; - NSString *p = [[storagePath retain] autorelease]; - [[self accessLock] unlock]; - return p; -} - - -- (void)setStoragePath:(NSString *)path -{ - [[self accessLock] lock]; - [self clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy]; - [storagePath release]; - storagePath = [path retain]; - - NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease]; - - BOOL isDirectory = NO; - NSArray *directories = [NSArray arrayWithObjects:path,[path stringByAppendingPathComponent:sessionCacheFolder],[path stringByAppendingPathComponent:permanentCacheFolder],nil]; - for (NSString *directory in directories) { - BOOL exists = [fileManager fileExistsAtPath:directory isDirectory:&isDirectory]; - if (exists && !isDirectory) { - [[self accessLock] unlock]; - [NSException raise:@"FileExistsAtCachePath" format:@"Cannot create a directory for the cache at '%@', because a file already exists",directory]; - } else if (!exists) { - [fileManager createDirectoryAtPath:directory withIntermediateDirectories:NO attributes:nil error:nil]; - if (![fileManager fileExistsAtPath:directory]) { - [[self accessLock] unlock]; - [NSException raise:@"FailedToCreateCacheDirectory" format:@"Failed to create a directory for the cache at '%@'",directory]; - } - } - } - [self clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy]; - [[self accessLock] unlock]; -} - -- (void)updateExpiryForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge -{ - NSString *headerPath = [self pathToStoreCachedResponseHeadersForRequest:request]; - NSMutableDictionary *cachedHeaders = [NSMutableDictionary dictionaryWithContentsOfFile:headerPath]; - if (!cachedHeaders) { - return; - } - NSDate *expires = [self expiryDateForRequest:request maxAge:maxAge]; - if (!expires) { - return; - } - [cachedHeaders setObject:[NSNumber numberWithDouble:[expires timeIntervalSince1970]] forKey:@"X-ASIHTTPRequest-Expires"]; - [cachedHeaders writeToFile:headerPath atomically:NO]; -} - -- (NSDate *)expiryDateForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge -{ - return [ASIHTTPRequest expiryDateForRequest:request maxAge:maxAge]; -} - -- (void)storeResponseForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge -{ - [[self accessLock] lock]; - - if ([request error] || ![request responseHeaders] || ([request cachePolicy] & ASIDoNotWriteToCacheCachePolicy)) { - [[self accessLock] unlock]; - return; - } - - // We only cache 200/OK or redirect reponses (redirect responses are cached so the cache works better with no internet connection) - int responseCode = [request responseStatusCode]; - if (responseCode != 200 && responseCode != 301 && responseCode != 302 && responseCode != 303 && responseCode != 307) { - [[self accessLock] unlock]; - return; - } - - if ([self shouldRespectCacheControlHeaders] && ![[self class] serverAllowsResponseCachingForRequest:request]) { - [[self accessLock] unlock]; - return; - } - - NSString *headerPath = [self pathToStoreCachedResponseHeadersForRequest:request]; - NSString *dataPath = [self pathToStoreCachedResponseDataForRequest:request]; - - NSMutableDictionary *responseHeaders = [NSMutableDictionary dictionaryWithDictionary:[request responseHeaders]]; - if ([request isResponseCompressed]) { - [responseHeaders removeObjectForKey:@"Content-Encoding"]; - } - - // Create a special 'X-ASIHTTPRequest-Expires' header - // This is what we use for deciding if cached data is current, rather than parsing the expires / max-age headers individually each time - // We store this as a timestamp to make reading it easier as NSDateFormatter is quite expensive - - NSDate *expires = [self expiryDateForRequest:request maxAge:maxAge]; - if (expires) { - [responseHeaders setObject:[NSNumber numberWithDouble:[expires timeIntervalSince1970]] forKey:@"X-ASIHTTPRequest-Expires"]; - } - - // Store the response code in a custom header so we can reuse it later - - // We'll change 304/Not Modified to 200/OK because this is likely to be us updating the cached headers with a conditional GET - int statusCode = [request responseStatusCode]; - if (statusCode == 304) { - statusCode = 200; - } - [responseHeaders setObject:[NSNumber numberWithInt:statusCode] forKey:@"X-ASIHTTPRequest-Response-Status-Code"]; - - [responseHeaders writeToFile:headerPath atomically:NO]; - - if ([request responseData]) { - [[request responseData] writeToFile:dataPath atomically:NO]; - } else if ([request downloadDestinationPath] && ![[request downloadDestinationPath] isEqualToString:dataPath]) { - NSError *error = nil; - NSFileManager* manager = [[NSFileManager alloc] init]; - if ([manager fileExistsAtPath:dataPath]) { - [manager removeItemAtPath:dataPath error:&error]; - } - [manager copyItemAtPath:[request downloadDestinationPath] toPath:dataPath error:&error]; - [manager release]; - } - [[self accessLock] unlock]; -} - -- (NSDictionary *)cachedResponseHeadersForURL:(NSURL *)url -{ - NSString *path = [self pathToCachedResponseHeadersForURL:url]; - if (path) { - return [NSDictionary dictionaryWithContentsOfFile:path]; - } - return nil; -} - -- (NSData *)cachedResponseDataForURL:(NSURL *)url -{ - NSString *path = [self pathToCachedResponseDataForURL:url]; - if (path) { - return [NSData dataWithContentsOfFile:path]; - } - return nil; -} - -- (NSString *)pathToCachedResponseDataForURL:(NSURL *)url -{ - // Grab the file extension, if there is one. We do this so we can save the cached response with the same file extension - this is important if you want to display locally cached data in a web view - NSString *extension = [[url path] pathExtension]; - - // If the url doesn't have an extension, we'll add one so a webview can read it when locally cached - // If the url has the extension of a common web scripting language, we'll change the extension on the cached path to html for the same reason - if (![extension length] || [[[self class] fileExtensionsToHandleAsHTML] containsObject:[extension lowercaseString]]) { - extension = @"html"; - } - return [self pathToFile:[[[self class] keyForURL:url] stringByAppendingPathExtension:extension]]; -} - -+ (NSArray *)fileExtensionsToHandleAsHTML -{ - return fileExtensionsToHandleAsHTML; -} - - -- (NSString *)pathToCachedResponseHeadersForURL:(NSURL *)url -{ - return [self pathToFile:[[[self class] keyForURL:url] stringByAppendingPathExtension:@"cachedheaders"]]; -} - -- (NSString *)pathToFile:(NSString *)file -{ - [[self accessLock] lock]; - if (![self storagePath]) { - [[self accessLock] unlock]; - return nil; - } - - NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease]; - - // Look in the session store - NSString *dataPath = [[[self storagePath] stringByAppendingPathComponent:sessionCacheFolder] stringByAppendingPathComponent:file]; - if ([fileManager fileExistsAtPath:dataPath]) { - [[self accessLock] unlock]; - return dataPath; - } - // Look in the permanent store - dataPath = [[[self storagePath] stringByAppendingPathComponent:permanentCacheFolder] stringByAppendingPathComponent:file]; - if ([fileManager fileExistsAtPath:dataPath]) { - [[self accessLock] unlock]; - return dataPath; - } - [[self accessLock] unlock]; - return nil; -} - - -- (NSString *)pathToStoreCachedResponseDataForRequest:(ASIHTTPRequest *)request -{ - [[self accessLock] lock]; - if (![self storagePath]) { - [[self accessLock] unlock]; - return nil; - } - - NSString *path = [[self storagePath] stringByAppendingPathComponent:([request cacheStoragePolicy] == ASICacheForSessionDurationCacheStoragePolicy ? sessionCacheFolder : permanentCacheFolder)]; - - // Grab the file extension, if there is one. We do this so we can save the cached response with the same file extension - this is important if you want to display locally cached data in a web view - NSString *extension = [[[request url] path] pathExtension]; - - // If the url doesn't have an extension, we'll add one so a webview can read it when locally cached - // If the url has the extension of a common web scripting language, we'll change the extension on the cached path to html for the same reason - if (![extension length] || [[[self class] fileExtensionsToHandleAsHTML] containsObject:[extension lowercaseString]]) { - extension = @"html"; - } - path = [path stringByAppendingPathComponent:[[[self class] keyForURL:[request url]] stringByAppendingPathExtension:extension]]; - [[self accessLock] unlock]; - return path; -} - -- (NSString *)pathToStoreCachedResponseHeadersForRequest:(ASIHTTPRequest *)request -{ - [[self accessLock] lock]; - if (![self storagePath]) { - [[self accessLock] unlock]; - return nil; - } - NSString *path = [[self storagePath] stringByAppendingPathComponent:([request cacheStoragePolicy] == ASICacheForSessionDurationCacheStoragePolicy ? sessionCacheFolder : permanentCacheFolder)]; - path = [path stringByAppendingPathComponent:[[[self class] keyForURL:[request url]] stringByAppendingPathExtension:@"cachedheaders"]]; - [[self accessLock] unlock]; - return path; -} - -- (void)removeCachedDataForURL:(NSURL *)url -{ - [[self accessLock] lock]; - if (![self storagePath]) { - [[self accessLock] unlock]; - return; - } - NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease]; - - NSString *path = [self pathToCachedResponseHeadersForURL:url]; - if (path) { - [fileManager removeItemAtPath:path error:NULL]; - } - - path = [self pathToCachedResponseDataForURL:url]; - if (path) { - [fileManager removeItemAtPath:path error:NULL]; - } - [[self accessLock] unlock]; -} - -- (void)removeCachedDataForRequest:(ASIHTTPRequest *)request -{ - [self removeCachedDataForURL:[request url]]; -} - -- (BOOL)isCachedDataCurrentForRequest:(ASIHTTPRequest *)request -{ - [[self accessLock] lock]; - if (![self storagePath]) { - [[self accessLock] unlock]; - return NO; - } - NSDictionary *cachedHeaders = [self cachedResponseHeadersForURL:[request url]]; - if (!cachedHeaders) { - [[self accessLock] unlock]; - return NO; - } - NSString *dataPath = [self pathToCachedResponseDataForURL:[request url]]; - if (!dataPath) { - [[self accessLock] unlock]; - return NO; - } - - // New content is not different - if ([request responseStatusCode] == 304) { - [[self accessLock] unlock]; - return YES; - } - - // If we already have response headers for this request, check to see if the new content is different - // We check [request complete] so that we don't end up comparing response headers from a redirection with these - if ([request responseHeaders] && [request complete]) { - - // If the Etag or Last-Modified date are different from the one we have, we'll have to fetch this resource again - NSArray *headersToCompare = [NSArray arrayWithObjects:@"Etag",@"Last-Modified",nil]; - for (NSString *header in headersToCompare) { - if (![[[request responseHeaders] objectForKey:header] isEqualToString:[cachedHeaders objectForKey:header]]) { - [[self accessLock] unlock]; - return NO; - } - } - } - - if ([self shouldRespectCacheControlHeaders]) { - - // Look for X-ASIHTTPRequest-Expires header to see if the content is out of date - NSNumber *expires = [cachedHeaders objectForKey:@"X-ASIHTTPRequest-Expires"]; - if (expires) { - if ([[NSDate dateWithTimeIntervalSince1970:[expires doubleValue]] timeIntervalSinceNow] >= 0) { - [[self accessLock] unlock]; - return YES; - } - } - - // No explicit expiration time sent by the server - [[self accessLock] unlock]; - return NO; - } - - - [[self accessLock] unlock]; - return YES; -} - -- (ASICachePolicy)defaultCachePolicy -{ - [[self accessLock] lock]; - ASICachePolicy cp = defaultCachePolicy; - [[self accessLock] unlock]; - return cp; -} - - -- (void)setDefaultCachePolicy:(ASICachePolicy)cachePolicy -{ - [[self accessLock] lock]; - if (!cachePolicy) { - defaultCachePolicy = ASIAskServerIfModifiedWhenStaleCachePolicy; - } else { - defaultCachePolicy = cachePolicy; - } - [[self accessLock] unlock]; -} - -- (void)clearCachedResponsesForStoragePolicy:(ASICacheStoragePolicy)storagePolicy -{ - [[self accessLock] lock]; - if (![self storagePath]) { - [[self accessLock] unlock]; - return; - } - NSString *path = [[self storagePath] stringByAppendingPathComponent:(storagePolicy == ASICacheForSessionDurationCacheStoragePolicy ? sessionCacheFolder : permanentCacheFolder)]; - - NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease]; - - BOOL isDirectory = NO; - BOOL exists = [fileManager fileExistsAtPath:path isDirectory:&isDirectory]; - if (!exists || !isDirectory) { - [[self accessLock] unlock]; - return; - } - NSError *error = nil; - NSArray *cacheFiles = [fileManager contentsOfDirectoryAtPath:path error:&error]; - if (error) { - [[self accessLock] unlock]; - [NSException raise:@"FailedToTraverseCacheDirectory" format:@"Listing cache directory failed at path '%@'",path]; - } - for (NSString *file in cacheFiles) { - [fileManager removeItemAtPath:[path stringByAppendingPathComponent:file] error:&error]; - if (error) { - [[self accessLock] unlock]; - [NSException raise:@"FailedToRemoveCacheFile" format:@"Failed to remove cached data at path '%@'",path]; - } - } - [[self accessLock] unlock]; -} - -+ (BOOL)serverAllowsResponseCachingForRequest:(ASIHTTPRequest *)request -{ - NSString *cacheControl = [[[request responseHeaders] objectForKey:@"Cache-Control"] lowercaseString]; - if (cacheControl) { - if ([cacheControl isEqualToString:@"no-cache"] || [cacheControl isEqualToString:@"no-store"]) { - return NO; - } - } - NSString *pragma = [[[request responseHeaders] objectForKey:@"Pragma"] lowercaseString]; - if (pragma) { - if ([pragma isEqualToString:@"no-cache"]) { - return NO; - } - } - return YES; -} - -+ (NSString *)keyForURL:(NSURL *)url -{ - NSString *urlString = [url absoluteString]; - if ([urlString length] == 0) { - return nil; - } - - // Strip trailing slashes so http://allseeing-i.com/ASIHTTPRequest/ is cached the same as http://allseeing-i.com/ASIHTTPRequest - if ([[urlString substringFromIndex:[urlString length]-1] isEqualToString:@"/"]) { - urlString = [urlString substringToIndex:[urlString length]-1]; - } - - // Borrowed from: http://stackoverflow.com/questions/652300/using-md5-hash-on-a-string-in-cocoa - const char *cStr = [urlString UTF8String]; - unsigned char result[16]; - CC_MD5(cStr, (CC_LONG)strlen(cStr), result); - return [NSString stringWithFormat:@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7],result[8], result[9], result[10], result[11],result[12], result[13], result[14], result[15]]; -} - -- (BOOL)canUseCachedDataForRequest:(ASIHTTPRequest *)request -{ - // Ensure the request is allowed to read from the cache - if ([request cachePolicy] & ASIDoNotReadFromCacheCachePolicy) { - return NO; - - // If we don't want to load the request whatever happens, always pretend we have cached data even if we don't - } else if ([request cachePolicy] & ASIDontLoadCachePolicy) { - return YES; - } - - NSDictionary *headers = [self cachedResponseHeadersForURL:[request url]]; - if (!headers) { - return NO; - } - NSString *dataPath = [self pathToCachedResponseDataForURL:[request url]]; - if (!dataPath) { - return NO; - } - - // If we get here, we have cached data - - // If we have cached data, we can use it - if ([request cachePolicy] & ASIOnlyLoadIfNotCachedCachePolicy) { - return YES; - - // If we want to fallback to the cache after an error - } else if ([request complete] && [request cachePolicy] & ASIFallbackToCacheIfLoadFailsCachePolicy) { - return YES; - - // If we have cached data that is current, we can use it - } else if ([request cachePolicy] & ASIAskServerIfModifiedWhenStaleCachePolicy) { - if ([self isCachedDataCurrentForRequest:request]) { - return YES; - } - - // If we've got headers from a conditional GET and the cached data is still current, we can use it - } else if ([request cachePolicy] & ASIAskServerIfModifiedCachePolicy) { - if (![request responseHeaders]) { - return NO; - } else if ([self isCachedDataCurrentForRequest:request]) { - return YES; - } - } - return NO; -} - -@synthesize storagePath; -@synthesize defaultCachePolicy; -@synthesize accessLock; -@synthesize shouldRespectCacheControlHeaders; -@end diff --git a/ASIHTTP/ASIFormDataRequest.h b/ASIHTTP/ASIFormDataRequest.h deleted file mode 100644 index 670995f..0000000 --- a/ASIHTTP/ASIFormDataRequest.h +++ /dev/null @@ -1,76 +0,0 @@ -// -// ASIFormDataRequest.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 07/11/2008. -// Copyright 2008-2009 All-Seeing Interactive. All rights reserved. -// - -#import -#import "ASIHTTPRequest.h" -#import "ASIHTTPRequestConfig.h" - -typedef enum _ASIPostFormat { - ASIMultipartFormDataPostFormat = 0, - ASIURLEncodedPostFormat = 1 - -} ASIPostFormat; - -@interface ASIFormDataRequest : ASIHTTPRequest { - - // Parameters that will be POSTed to the url - NSMutableArray *postData; - - // Files that will be POSTed to the url - NSMutableArray *fileData; - - ASIPostFormat postFormat; - - NSStringEncoding stringEncoding; - -#if DEBUG_FORM_DATA_REQUEST - // Will store a string version of the request body that will be printed to the console when ASIHTTPREQUEST_DEBUG is set in GCC_PREPROCESSOR_DEFINITIONS - NSString *debugBodyString; -#endif - -} - -#pragma mark utilities -- (NSString*)encodeURL:(NSString *)string; - -#pragma mark setup request - -// Add a POST variable to the request -- (void)addPostValue:(id )value forKey:(NSString *)key; - -// Set a POST variable for this request, clearing any others with the same key -- (void)setPostValue:(id )value forKey:(NSString *)key; - -// Add the contents of a local file to the request -- (void)addFile:(NSString *)filePath forKey:(NSString *)key; - -// Same as above, but you can specify the content-type and file name -- (void)addFile:(NSString *)filePath withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key; - -// Add the contents of a local file to the request, clearing any others with the same key -- (void)setFile:(NSString *)filePath forKey:(NSString *)key; - -// Same as above, but you can specify the content-type and file name -- (void)setFile:(NSString *)filePath withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key; - -// Add the contents of an NSData object to the request -- (void)addData:(NSData *)data forKey:(NSString *)key; - -// Same as above, but you can specify the content-type and file name -- (void)addData:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key; - -// Add the contents of an NSData object to the request, clearing any others with the same key -- (void)setData:(NSData *)data forKey:(NSString *)key; - -// Same as above, but you can specify the content-type and file name -- (void)setData:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key; - - -@property (assign) ASIPostFormat postFormat; -@property (assign) NSStringEncoding stringEncoding; -@end diff --git a/ASIHTTP/ASIFormDataRequest.m b/ASIHTTP/ASIFormDataRequest.m deleted file mode 100644 index 950a6de..0000000 --- a/ASIHTTP/ASIFormDataRequest.m +++ /dev/null @@ -1,361 +0,0 @@ -// -// ASIFormDataRequest.m -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 07/11/2008. -// Copyright 2008-2009 All-Seeing Interactive. All rights reserved. -// - -#import "ASIFormDataRequest.h" - - -// Private stuff -@interface ASIFormDataRequest () -- (void)buildMultipartFormDataPostBody; -- (void)buildURLEncodedPostBody; -- (void)appendPostString:(NSString *)string; - -@property (retain) NSMutableArray *postData; -@property (retain) NSMutableArray *fileData; - -#if DEBUG_FORM_DATA_REQUEST -- (void)addToDebugBody:(NSString *)string; -@property (retain, nonatomic) NSString *debugBodyString; -#endif - -@end - -@implementation ASIFormDataRequest - -#pragma mark utilities -- (NSString*)encodeURL:(NSString *)string -{ - NSString *newString = [NSMakeCollectable(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, CFSTR(":/?#[]@!$ &'()*+,;=\"<>%{}|\\^~`"), CFStringConvertNSStringEncodingToEncoding([self stringEncoding]))) autorelease]; - if (newString) { - return newString; - } - return @""; -} - -#pragma mark init / dealloc - -+ (id)requestWithURL:(NSURL *)newURL -{ - return [[[self alloc] initWithURL:newURL] autorelease]; -} - -- (id)initWithURL:(NSURL *)newURL -{ - self = [super initWithURL:newURL]; - [self setPostFormat:ASIURLEncodedPostFormat]; - [self setStringEncoding:NSUTF8StringEncoding]; - return self; -} - -- (void)dealloc -{ -#if DEBUG_FORM_DATA_REQUEST - [debugBodyString release]; -#endif - - [postData release]; - [fileData release]; - [super dealloc]; -} - -#pragma mark setup request - -- (void)addPostValue:(id )value forKey:(NSString *)key -{ - if (!key) { - return; - } - if (![self postData]) { - [self setPostData:[NSMutableArray array]]; - } - NSMutableDictionary *keyValuePair = [NSMutableDictionary dictionaryWithCapacity:2]; - [keyValuePair setValue:key forKey:@"key"]; - [keyValuePair setValue:[value description] forKey:@"value"]; - [[self postData] addObject:keyValuePair]; -} - -- (void)setPostValue:(id )value forKey:(NSString *)key -{ - // Remove any existing value - NSUInteger i; - for (i=0; i<[[self postData] count]; i++) { - NSDictionary *val = [[self postData] objectAtIndex:i]; - if ([[val objectForKey:@"key"] isEqualToString:key]) { - [[self postData] removeObjectAtIndex:i]; - i--; - } - } - [self addPostValue:value forKey:key]; -} - - -- (void)addFile:(NSString *)filePath forKey:(NSString *)key -{ - [self addFile:filePath withFileName:nil andContentType:nil forKey:key]; -} - -- (void)addFile:(NSString *)filePath withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key -{ - BOOL isDirectory = NO; - BOOL fileExists = [[[[NSFileManager alloc] init] autorelease] fileExistsAtPath:filePath isDirectory:&isDirectory]; - if (!fileExists || isDirectory) { - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileBuildingRequestType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"No file exists at %@",filePath],NSLocalizedDescriptionKey,nil]]]; - } - - // If the caller didn't specify a custom file name, we'll use the file name of the file we were passed - if (!fileName) { - fileName = [filePath lastPathComponent]; - } - - // If we were given the path to a file, and the user didn't specify a mime type, we can detect it from the file extension - if (!contentType) { - contentType = [ASIHTTPRequest mimeTypeForFileAtPath:filePath]; - } - [self addData:filePath withFileName:fileName andContentType:contentType forKey:key]; -} - -- (void)setFile:(NSString *)filePath forKey:(NSString *)key -{ - [self setFile:filePath withFileName:nil andContentType:nil forKey:key]; -} - -- (void)setFile:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key -{ - // Remove any existing value - NSUInteger i; - for (i=0; i<[[self fileData] count]; i++) { - NSDictionary *val = [[self fileData] objectAtIndex:i]; - if ([[val objectForKey:@"key"] isEqualToString:key]) { - [[self fileData] removeObjectAtIndex:i]; - i--; - } - } - [self addFile:data withFileName:fileName andContentType:contentType forKey:key]; -} - -- (void)addData:(NSData *)data forKey:(NSString *)key -{ - [self addData:data withFileName:@"file" andContentType:nil forKey:key]; -} - -- (void)addData:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key -{ - if (![self fileData]) { - [self setFileData:[NSMutableArray array]]; - } - if (!contentType) { - contentType = @"application/octet-stream"; - } - - NSMutableDictionary *fileInfo = [NSMutableDictionary dictionaryWithCapacity:4]; - [fileInfo setValue:key forKey:@"key"]; - [fileInfo setValue:fileName forKey:@"fileName"]; - [fileInfo setValue:contentType forKey:@"contentType"]; - [fileInfo setValue:data forKey:@"data"]; - - [[self fileData] addObject:fileInfo]; -} - -- (void)setData:(NSData *)data forKey:(NSString *)key -{ - [self setData:data withFileName:@"file" andContentType:nil forKey:key]; -} - -- (void)setData:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key -{ - // Remove any existing value - NSUInteger i; - for (i=0; i<[[self fileData] count]; i++) { - NSDictionary *val = [[self fileData] objectAtIndex:i]; - if ([[val objectForKey:@"key"] isEqualToString:key]) { - [[self fileData] removeObjectAtIndex:i]; - i--; - } - } - [self addData:data withFileName:fileName andContentType:contentType forKey:key]; -} - -- (void)buildPostBody -{ - if ([self haveBuiltPostBody]) { - return; - } - -#if DEBUG_FORM_DATA_REQUEST - [self setDebugBodyString:@""]; -#endif - - if (![self postData] && ![self fileData]) { - [super buildPostBody]; - return; - } - if ([[self fileData] count] > 0) { - [self setShouldStreamPostDataFromDisk:YES]; - } - - if ([self postFormat] == ASIURLEncodedPostFormat) { - [self buildURLEncodedPostBody]; - } else { - [self buildMultipartFormDataPostBody]; - } - - [super buildPostBody]; - -#if DEBUG_FORM_DATA_REQUEST - ASI_DEBUG_LOG(@"%@",[self debugBodyString]); - [self setDebugBodyString:nil]; -#endif -} - - -- (void)buildMultipartFormDataPostBody -{ -#if DEBUG_FORM_DATA_REQUEST - [self addToDebugBody:@"\r\n==== Building a multipart/form-data body ====\r\n"]; -#endif - - NSString *charset = (NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding([self stringEncoding])); - - // We don't bother to check if post data contains the boundary, since it's pretty unlikely that it does. - CFUUIDRef uuid = CFUUIDCreate(nil); - NSString *uuidString = [(NSString*)CFUUIDCreateString(nil, uuid) autorelease]; - CFRelease(uuid); - NSString *stringBoundary = [NSString stringWithFormat:@"0xKhTmLbOuNdArY-%@",uuidString]; - - [self addRequestHeader:@"Content-Type" value:[NSString stringWithFormat:@"multipart/form-data; charset=%@; boundary=%@", charset, stringBoundary]]; - - [self appendPostString:[NSString stringWithFormat:@"--%@\r\n",stringBoundary]]; - - // Adds post data - NSString *endItemBoundary = [NSString stringWithFormat:@"\r\n--%@\r\n",stringBoundary]; - NSUInteger i=0; - for (NSDictionary *val in [self postData]) { - [self appendPostString:[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n",[val objectForKey:@"key"]]]; - [self appendPostString:[val objectForKey:@"value"]]; - i++; - if (i != [[self postData] count] || [[self fileData] count] > 0) { //Only add the boundary if this is not the last item in the post body - [self appendPostString:endItemBoundary]; - } - } - - // Adds files to upload - i=0; - for (NSDictionary *val in [self fileData]) { - - [self appendPostString:[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", [val objectForKey:@"key"], [val objectForKey:@"fileName"]]]; - [self appendPostString:[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", [val objectForKey:@"contentType"]]]; - - id data = [val objectForKey:@"data"]; - if ([data isKindOfClass:[NSString class]]) { - [self appendPostDataFromFile:data]; - } else { - [self appendPostData:data]; - } - i++; - // Only add the boundary if this is not the last item in the post body - if (i != [[self fileData] count]) { - [self appendPostString:endItemBoundary]; - } - } - - [self appendPostString:[NSString stringWithFormat:@"\r\n--%@--\r\n",stringBoundary]]; - -#if DEBUG_FORM_DATA_REQUEST - [self addToDebugBody:@"==== End of multipart/form-data body ====\r\n"]; -#endif -} - -- (void)buildURLEncodedPostBody -{ - - // We can't post binary data using application/x-www-form-urlencoded - if ([[self fileData] count] > 0) { - [self setPostFormat:ASIMultipartFormDataPostFormat]; - [self buildMultipartFormDataPostBody]; - return; - } - -#if DEBUG_FORM_DATA_REQUEST - [self addToDebugBody:@"\r\n==== Building an application/x-www-form-urlencoded body ====\r\n"]; -#endif - - - NSString *charset = (NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding([self stringEncoding])); - - [self addRequestHeader:@"Content-Type" value:[NSString stringWithFormat:@"application/x-www-form-urlencoded; charset=%@",charset]]; - - - NSUInteger i=0; - NSUInteger count = [[self postData] count]-1; - for (NSDictionary *val in [self postData]) { - NSString *data = [NSString stringWithFormat:@"%@=%@%@", [self encodeURL:[val objectForKey:@"key"]], [self encodeURL:[val objectForKey:@"value"]],(i -#if TARGET_OS_IPHONE - #import - #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 - #import // Necessary for background task support - #endif -#endif - -#import -#import "ASIHTTPRequestConfig.h" -#import "ASIHTTPRequestDelegate.h" -#import "ASIProgressDelegate.h" -#import "ASICacheDelegate.h" - -@class ASIDataDecompressor; - -extern NSString *ASIHTTPRequestVersion; - -// Make targeting different platforms more reliable -// See: http://www.blumtnwerx.com/blog/2009/06/cross-sdk-code-hygiene-in-xcode/ -#ifndef __IPHONE_3_2 - #define __IPHONE_3_2 30200 -#endif -#ifndef __IPHONE_4_0 - #define __IPHONE_4_0 40000 -#endif -#ifndef __MAC_10_5 - #define __MAC_10_5 1050 -#endif -#ifndef __MAC_10_6 - #define __MAC_10_6 1060 -#endif - -typedef enum _ASIAuthenticationState { - ASINoAuthenticationNeededYet = 0, - ASIHTTPAuthenticationNeeded = 1, - ASIProxyAuthenticationNeeded = 2 -} ASIAuthenticationState; - -typedef enum _ASINetworkErrorType { - ASIConnectionFailureErrorType = 1, - ASIRequestTimedOutErrorType = 2, - ASIAuthenticationErrorType = 3, - ASIRequestCancelledErrorType = 4, - ASIUnableToCreateRequestErrorType = 5, - ASIInternalErrorWhileBuildingRequestType = 6, - ASIInternalErrorWhileApplyingCredentialsType = 7, - ASIFileManagementError = 8, - ASITooMuchRedirectionErrorType = 9, - ASIUnhandledExceptionError = 10, - ASICompressionError = 11 - -} ASINetworkErrorType; - - -// The error domain that all errors generated by ASIHTTPRequest use -extern NSString* const NetworkRequestErrorDomain; - -// You can use this number to throttle upload and download bandwidth in iPhone OS apps send or receive a large amount of data -// This may help apps that might otherwise be rejected for inclusion into the app store for using excessive bandwidth -// This number is not official, as far as I know there is no officially documented bandwidth limit -extern unsigned long const ASIWWANBandwidthThrottleAmount; - -#if NS_BLOCKS_AVAILABLE -typedef void (^ASIBasicBlock)(void); -typedef void (^ASIHeadersBlock)(NSDictionary *responseHeaders); -typedef void (^ASISizeBlock)(long long size); -typedef void (^ASIProgressBlock)(unsigned long long size, unsigned long long total); -typedef void (^ASIDataBlock)(NSData *data); -#endif - -@interface ASIHTTPRequest : NSOperation { - - // The url for this operation, should include GET params in the query string where appropriate - NSURL *url; - - // Will always contain the original url used for making the request (the value of url can change when a request is redirected) - NSURL *originalURL; - - // Temporarily stores the url we are about to redirect to. Will be nil again when we do redirect - NSURL *redirectURL; - - // The delegate - will be notified of various changes in state via the ASIHTTPRequestDelegate protocol - id delegate; - - // Another delegate that is also notified of request status changes and progress updates - // Generally, you won't use this directly, but ASINetworkQueue sets itself as the queue so it can proxy updates to its own delegates - // NOTE: WILL BE RETAINED BY THE REQUEST - id queue; - - // HTTP method to use (eg: GET / POST / PUT / DELETE / HEAD etc). Defaults to GET - NSString *requestMethod; - - // Request body - only used when the whole body is stored in memory (shouldStreamPostDataFromDisk is false) - NSMutableData *postBody; - - // gzipped request body used when shouldCompressRequestBody is YES - NSData *compressedPostBody; - - // When true, post body will be streamed from a file on disk, rather than loaded into memory at once (useful for large uploads) - // Automatically set to true in ASIFormDataRequests when using setFile:forKey: - BOOL shouldStreamPostDataFromDisk; - - // Path to file used to store post body (when shouldStreamPostDataFromDisk is true) - // You can set this yourself - useful if you want to PUT a file from local disk - NSString *postBodyFilePath; - - // Path to a temporary file used to store a deflated post body (when shouldCompressPostBody is YES) - NSString *compressedPostBodyFilePath; - - // Set to true when ASIHTTPRequest automatically created a temporary file containing the request body (when true, the file at postBodyFilePath will be deleted at the end of the request) - BOOL didCreateTemporaryPostDataFile; - - // Used when writing to the post body when shouldStreamPostDataFromDisk is true (via appendPostData: or appendPostDataFromFile:) - NSOutputStream *postBodyWriteStream; - - // Used for reading from the post body when sending the request - NSInputStream *postBodyReadStream; - - // Dictionary for custom HTTP request headers - NSMutableDictionary *requestHeaders; - - // Set to YES when the request header dictionary has been populated, used to prevent this happening more than once - BOOL haveBuiltRequestHeaders; - - // Will be populated with HTTP response headers from the server - NSDictionary *responseHeaders; - - // Can be used to manually insert cookie headers to a request, but it's more likely that sessionCookies will do this for you - NSMutableArray *requestCookies; - - // Will be populated with cookies - NSArray *responseCookies; - - // If use useCookiePersistence is true, network requests will present valid cookies from previous requests - BOOL useCookiePersistence; - - // If useKeychainPersistence is true, network requests will attempt to read credentials from the keychain, and will save them in the keychain when they are successfully presented - BOOL useKeychainPersistence; - - // If useSessionPersistence is true, network requests will save credentials and reuse for the duration of the session (until clearSession is called) - BOOL useSessionPersistence; - - // If allowCompressedResponse is true, requests will inform the server they can accept compressed data, and will automatically decompress gzipped responses. Default is true. - BOOL allowCompressedResponse; - - // If shouldCompressRequestBody is true, the request body will be gzipped. Default is false. - // You will probably need to enable this feature on your webserver to make this work. Tested with apache only. - BOOL shouldCompressRequestBody; - - // When downloadDestinationPath is set, the result of this request will be downloaded to the file at this location - // If downloadDestinationPath is not set, download data will be stored in memory - NSString *downloadDestinationPath; - - // The location that files will be downloaded to. Once a download is complete, files will be decompressed (if necessary) and moved to downloadDestinationPath - NSString *temporaryFileDownloadPath; - - // If the response is gzipped and shouldWaitToInflateCompressedResponses is NO, a file will be created at this path containing the inflated response as it comes in - NSString *temporaryUncompressedDataDownloadPath; - - // Used for writing data to a file when downloadDestinationPath is set - NSOutputStream *fileDownloadOutputStream; - - NSOutputStream *inflatedFileDownloadOutputStream; - - // When the request fails or completes successfully, complete will be true - BOOL complete; - - // external "finished" indicator, subject of KVO notifications; updates after 'complete' - BOOL finished; - - // True if our 'cancel' selector has been called - BOOL cancelled; - - // If an error occurs, error will contain an NSError - // If error code is = ASIConnectionFailureErrorType (1, Connection failure occurred) - inspect [[error userInfo] objectForKey:NSUnderlyingErrorKey] for more information - NSError *error; - - // Username and password used for authentication - NSString *username; - NSString *password; - - // User-Agent for this request - NSString *userAgent; - - // Domain used for NTLM authentication - NSString *domain; - - // Username and password used for proxy authentication - NSString *proxyUsername; - NSString *proxyPassword; - - // Domain used for NTLM proxy authentication - NSString *proxyDomain; - - // Delegate for displaying upload progress (usually an NSProgressIndicator, but you can supply a different object and handle this yourself) - id uploadProgressDelegate; - - // Delegate for displaying download progress (usually an NSProgressIndicator, but you can supply a different object and handle this yourself) - id downloadProgressDelegate; - - // Whether we've seen the headers of the response yet - BOOL haveExaminedHeaders; - - // Data we receive will be stored here. Data may be compressed unless allowCompressedResponse is false - you should use [request responseData] instead in most cases - NSMutableData *rawResponseData; - - // Used for sending and receiving data - CFHTTPMessageRef request; - NSInputStream *readStream; - - // Used for authentication - CFHTTPAuthenticationRef requestAuthentication; - NSDictionary *requestCredentials; - - // Used during NTLM authentication - int authenticationRetryCount; - - // Authentication scheme (Basic, Digest, NTLM) - // If you are using Basic authentication and want to force ASIHTTPRequest to send an authorization header without waiting for a 401, you must set this to (NSString *)kCFHTTPAuthenticationSchemeBasic - NSString *authenticationScheme; - - // Realm for authentication when credentials are required - NSString *authenticationRealm; - - // When YES, ASIHTTPRequest will present a dialog allowing users to enter credentials when no-matching credentials were found for a server that requires authentication - // The dialog will not be shown if your delegate responds to authenticationNeededForRequest: - // Default is NO. - BOOL shouldPresentAuthenticationDialog; - - // When YES, ASIHTTPRequest will present a dialog allowing users to enter credentials when no-matching credentials were found for a proxy server that requires authentication - // The dialog will not be shown if your delegate responds to proxyAuthenticationNeededForRequest: - // Default is YES (basically, because most people won't want the hassle of adding support for authenticating proxies to their apps) - BOOL shouldPresentProxyAuthenticationDialog; - - // Used for proxy authentication - CFHTTPAuthenticationRef proxyAuthentication; - NSDictionary *proxyCredentials; - - // Used during authentication with an NTLM proxy - int proxyAuthenticationRetryCount; - - // Authentication scheme for the proxy (Basic, Digest, NTLM) - NSString *proxyAuthenticationScheme; - - // Realm for proxy authentication when credentials are required - NSString *proxyAuthenticationRealm; - - // HTTP status code, eg: 200 = OK, 404 = Not found etc - int responseStatusCode; - - // Description of the HTTP status code - NSString *responseStatusMessage; - - // Size of the response - unsigned long long contentLength; - - // Size of the partially downloaded content - unsigned long long partialDownloadSize; - - // Size of the POST payload - unsigned long long postLength; - - // The total amount of downloaded data - unsigned long long totalBytesRead; - - // The total amount of uploaded data - unsigned long long totalBytesSent; - - // Last amount of data read (used for incrementing progress) - unsigned long long lastBytesRead; - - // Last amount of data sent (used for incrementing progress) - unsigned long long lastBytesSent; - - // This lock prevents the operation from being cancelled at an inopportune moment - NSRecursiveLock *cancelledLock; - - // Called on the delegate (if implemented) when the request starts. Default is requestStarted: - SEL didStartSelector; - - // Called on the delegate (if implemented) when the request receives response headers. Default is request:didReceiveResponseHeaders: - SEL didReceiveResponseHeadersSelector; - - // Called on the delegate (if implemented) when the request receives a Location header and shouldRedirect is YES - // The delegate can then change the url if needed, and can restart the request by calling [request redirectToURL:], or simply cancel it - SEL willRedirectSelector; - - // Called on the delegate (if implemented) when the request completes successfully. Default is requestFinished: - SEL didFinishSelector; - - // Called on the delegate (if implemented) when the request fails. Default is requestFailed: - SEL didFailSelector; - - // Called on the delegate (if implemented) when the request receives data. Default is request:didReceiveData: - // If you set this and implement the method in your delegate, you must handle the data yourself - ASIHTTPRequest will not populate responseData or write the data to downloadDestinationPath - SEL didReceiveDataSelector; - - // Used for recording when something last happened during the request, we will compare this value with the current date to time out requests when appropriate - NSDate *lastActivityTime; - - // Number of seconds to wait before timing out - default is 10 - NSTimeInterval timeOutSeconds; - - // Will be YES when a HEAD request will handle the content-length before this request starts - BOOL shouldResetUploadProgress; - BOOL shouldResetDownloadProgress; - - // Used by HEAD requests when showAccurateProgress is YES to preset the content-length for this request - ASIHTTPRequest *mainRequest; - - // When NO, this request will only update the progress indicator when it completes - // When YES, this request will update the progress indicator according to how much data it has received so far - // The default for requests is YES - // Also see the comments in ASINetworkQueue.h - BOOL showAccurateProgress; - - // Used to ensure the progress indicator is only incremented once when showAccurateProgress = NO - BOOL updatedProgress; - - // Prevents the body of the post being built more than once (largely for subclasses) - BOOL haveBuiltPostBody; - - // Used internally, may reflect the size of the internal buffer used by CFNetwork - // POST / PUT operations with body sizes greater than uploadBufferSize will not timeout unless more than uploadBufferSize bytes have been sent - // Likely to be 32KB on iPhone 3.0, 128KB on Mac OS X Leopard and iPhone 2.2.x - unsigned long long uploadBufferSize; - - // Text encoding for responses that do not send a Content-Type with a charset value. Defaults to NSISOLatin1StringEncoding - NSStringEncoding defaultResponseEncoding; - - // The text encoding of the response, will be defaultResponseEncoding if the server didn't specify. Can't be set. - NSStringEncoding responseEncoding; - - // Tells ASIHTTPRequest not to delete partial downloads, and allows it to use an existing file to resume a download. Defaults to NO. - BOOL allowResumeForFileDownloads; - - // Custom user information associated with the request (not sent to the server) - NSDictionary *userInfo; - NSInteger tag; - - // Use HTTP 1.0 rather than 1.1 (defaults to false) - BOOL useHTTPVersionOne; - - // When YES, requests will automatically redirect when they get a HTTP 30x header (defaults to YES) - BOOL shouldRedirect; - - // Used internally to tell the main loop we need to stop and retry with a new url - BOOL needsRedirect; - - // Incremented every time this request redirects. When it reaches 5, we give up - int redirectCount; - - // When NO, requests will not check the secure certificate is valid (use for self-signed certificates during development, DO NOT USE IN PRODUCTION) Default is YES - BOOL validatesSecureCertificate; - - // If not nil and the URL scheme is https, CFNetwork configured to supply a client certificate - SecIdentityRef clientCertificateIdentity; - NSArray *clientCertificates; - - // Details on the proxy to use - you could set these yourself, but it's probably best to let ASIHTTPRequest detect the system proxy settings - NSString *proxyHost; - int proxyPort; - - // ASIHTTPRequest will assume kCFProxyTypeHTTP if the proxy type could not be automatically determined - // Set to kCFProxyTypeSOCKS if you are manually configuring a SOCKS proxy - NSString *proxyType; - - // URL for a PAC (Proxy Auto Configuration) file. If you want to set this yourself, it's probably best if you use a local file - NSURL *PACurl; - - // See ASIAuthenticationState values above. 0 == default == No authentication needed yet - ASIAuthenticationState authenticationNeeded; - - // When YES, ASIHTTPRequests will present credentials from the session store for requests to the same server before being asked for them - // This avoids an extra round trip for requests after authentication has succeeded, which is much for efficient for authenticated requests with large bodies, or on slower connections - // Set to NO to only present credentials when explicitly asked for them - // This only affects credentials stored in the session cache when useSessionPersistence is YES. Credentials from the keychain are never presented unless the server asks for them - // Default is YES - // For requests using Basic authentication, set authenticationScheme to (NSString *)kCFHTTPAuthenticationSchemeBasic, and credentials can be sent on the very first request when shouldPresentCredentialsBeforeChallenge is YES - BOOL shouldPresentCredentialsBeforeChallenge; - - // YES when the request hasn't finished yet. Will still be YES even if the request isn't doing anything (eg it's waiting for delegate authentication). READ-ONLY - BOOL inProgress; - - // Used internally to track whether the stream is scheduled on the run loop or not - // Bandwidth throttling can unschedule the stream to slow things down while a request is in progress - BOOL readStreamIsScheduled; - - // Set to allow a request to automatically retry itself on timeout - // Default is zero - timeout will stop the request - int numberOfTimesToRetryOnTimeout; - - // The number of times this request has retried (when numberOfTimesToRetryOnTimeout > 0) - int retryCount; - - // Temporarily set to YES when a closed connection forces a retry (internally, this stops ASIHTTPRequest cleaning up a temporary post body) - BOOL willRetryRequest; - - // When YES, requests will keep the connection to the server alive for a while to allow subsequent requests to re-use it for a substantial speed-boost - // Persistent connections will not be used if the server explicitly closes the connection - // Default is YES - BOOL shouldAttemptPersistentConnection; - - // Number of seconds to keep an inactive persistent connection open on the client side - // Default is 60 - // If we get a keep-alive header, this is this value is replaced with how long the server told us to keep the connection around - // A future date is created from this and used for expiring the connection, this is stored in connectionInfo's expires value - NSTimeInterval persistentConnectionTimeoutSeconds; - - // Set to yes when an appropriate keep-alive header is found - BOOL connectionCanBeReused; - - // Stores information about the persistent connection that is currently in use. - // It may contain: - // * The id we set for a particular connection, incremented every time we want to specify that we need a new connection - // * The date that connection should expire - // * A host, port and scheme for the connection. These are used to determine whether that connection can be reused by a subsequent request (all must match the new request) - // * An id for the request that is currently using the connection. This is used for determining if a connection is available or not (we store a number rather than a reference to the request so we don't need to hang onto a request until the connection expires) - // * A reference to the stream that is currently using the connection. This is necessary because we need to keep the old stream open until we've opened a new one. - // The stream will be closed + released either when another request comes to use the connection, or when the timer fires to tell the connection to expire - NSMutableDictionary *connectionInfo; - - // When set to YES, 301 and 302 automatic redirects will use the original method and and body, according to the HTTP 1.1 standard - // Default is NO (to follow the behaviour of most browsers) - BOOL shouldUseRFC2616RedirectBehaviour; - - // Used internally to record when a request has finished downloading data - BOOL downloadComplete; - - // An ID that uniquely identifies this request - primarily used for debugging persistent connections - NSNumber *requestID; - - // Will be ASIHTTPRequestRunLoopMode for synchronous requests, NSDefaultRunLoopMode for all other requests - NSString *runLoopMode; - - // This timer checks up on the request every 0.25 seconds, and updates progress - NSTimer *statusTimer; - - // The download cache that will be used for this request (use [ASIHTTPRequest setDefaultCache:cache] to configure a default cache - id downloadCache; - - // The cache policy that will be used for this request - See ASICacheDelegate.h for possible values - ASICachePolicy cachePolicy; - - // The cache storage policy that will be used for this request - See ASICacheDelegate.h for possible values - ASICacheStoragePolicy cacheStoragePolicy; - - // Will be true when the response was pulled from the cache rather than downloaded - BOOL didUseCachedResponse; - - // Set secondsToCache to use a custom time interval for expiring the response when it is stored in a cache - NSTimeInterval secondsToCache; - - #if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 - BOOL shouldContinueWhenAppEntersBackground; - UIBackgroundTaskIdentifier backgroundTask; - #endif - - // When downloading a gzipped response, the request will use this helper object to inflate the response - ASIDataDecompressor *dataDecompressor; - - // Controls how responses with a gzipped encoding are inflated (decompressed) - // When set to YES (This is the default): - // * gzipped responses for requests without a downloadDestinationPath will be inflated only when [request responseData] / [request responseString] is called - // * gzipped responses for requests with a downloadDestinationPath set will be inflated only when the request completes - // - // When set to NO - // All requests will inflate the response as it comes in - // * If the request has no downloadDestinationPath set, the raw (compressed) response is discarded and rawResponseData will contain the decompressed response - // * If the request has a downloadDestinationPath, the raw response will be stored in temporaryFileDownloadPath as normal, the inflated response will be stored in temporaryUncompressedDataDownloadPath - // Once the request completes successfully, the contents of temporaryUncompressedDataDownloadPath are moved into downloadDestinationPath - // - // Setting this to NO may be especially useful for users using ASIHTTPRequest in conjunction with a streaming parser, as it will allow partial gzipped responses to be inflated and passed on to the parser while the request is still running - BOOL shouldWaitToInflateCompressedResponses; - - // Will be YES if this is a request created behind the scenes to download a PAC file - these requests do not attempt to configure their own proxies - BOOL isPACFileRequest; - - // Used for downloading PAC files from http / https webservers - ASIHTTPRequest *PACFileRequest; - - // Used for asynchronously reading PAC files from file:// URLs - NSInputStream *PACFileReadStream; - - // Used for storing PAC data from file URLs as it is downloaded - NSMutableData *PACFileData; - - // Set to YES in startSynchronous. Currently used by proxy detection to download PAC files synchronously when appropriate - BOOL isSynchronous; - - #if NS_BLOCKS_AVAILABLE - //block to execute when request starts - ASIBasicBlock startedBlock; - - //block to execute when headers are received - ASIHeadersBlock headersReceivedBlock; - - //block to execute when request completes successfully - ASIBasicBlock completionBlock; - - //block to execute when request fails - ASIBasicBlock failureBlock; - - //block for when bytes are received - ASIProgressBlock bytesReceivedBlock; - - //block for when bytes are sent - ASIProgressBlock bytesSentBlock; - - //block for when download size is incremented - ASISizeBlock downloadSizeIncrementedBlock; - - //block for when upload size is incremented - ASISizeBlock uploadSizeIncrementedBlock; - - //block for handling raw bytes received - ASIDataBlock dataReceivedBlock; - - //block for handling authentication - ASIBasicBlock authenticationNeededBlock; - - //block for handling proxy authentication - ASIBasicBlock proxyAuthenticationNeededBlock; - - //block for handling redirections, if you want to - ASIBasicBlock requestRedirectedBlock; - #endif -} - -#pragma mark init / dealloc - -// Should be an HTTP or HTTPS url, may include username and password if appropriate -- (id)initWithURL:(NSURL *)newURL; - -// Convenience constructor -+ (id)requestWithURL:(NSURL *)newURL; - -+ (id)requestWithURL:(NSURL *)newURL usingCache:(id )cache; -+ (id)requestWithURL:(NSURL *)newURL usingCache:(id )cache andCachePolicy:(ASICachePolicy)policy; - -#if NS_BLOCKS_AVAILABLE -- (void)setStartedBlock:(ASIBasicBlock)aStartedBlock; -- (void)setHeadersReceivedBlock:(ASIHeadersBlock)aReceivedBlock; -- (void)setCompletionBlock:(ASIBasicBlock)aCompletionBlock; -- (void)setFailedBlock:(ASIBasicBlock)aFailedBlock; -- (void)setBytesReceivedBlock:(ASIProgressBlock)aBytesReceivedBlock; -- (void)setBytesSentBlock:(ASIProgressBlock)aBytesSentBlock; -- (void)setDownloadSizeIncrementedBlock:(ASISizeBlock) aDownloadSizeIncrementedBlock; -- (void)setUploadSizeIncrementedBlock:(ASISizeBlock) anUploadSizeIncrementedBlock; -- (void)setDataReceivedBlock:(ASIDataBlock)aReceivedBlock; -- (void)setAuthenticationNeededBlock:(ASIBasicBlock)anAuthenticationBlock; -- (void)setProxyAuthenticationNeededBlock:(ASIBasicBlock)aProxyAuthenticationBlock; -- (void)setRequestRedirectedBlock:(ASIBasicBlock)aRedirectBlock; -#endif - -#pragma mark setup request - -// Add a custom header to the request -- (void)addRequestHeader:(NSString *)header value:(NSString *)value; - -// Called during buildRequestHeaders and after a redirect to create a cookie header from request cookies and the global store -- (void)applyCookieHeader; - -// Populate the request headers dictionary. Called before a request is started, or by a HEAD request that needs to borrow them -- (void)buildRequestHeaders; - -// Used to apply authorization header to a request before it is sent (when shouldPresentCredentialsBeforeChallenge is YES) -- (void)applyAuthorizationHeader; - - -// Create the post body -- (void)buildPostBody; - -// Called to add data to the post body. Will append to postBody when shouldStreamPostDataFromDisk is false, or write to postBodyWriteStream when true -- (void)appendPostData:(NSData *)data; -- (void)appendPostDataFromFile:(NSString *)file; - -#pragma mark get information about this request - -// Returns the contents of the result as an NSString (not appropriate for binary data - used responseData instead) -- (NSString *)responseString; - -// Response data, automatically uncompressed where appropriate -- (NSData *)responseData; - -// Returns true if the response was gzip compressed -- (BOOL)isResponseCompressed; - -#pragma mark running a request - - -// Run a request synchronously, and return control when the request completes or fails -- (void)startSynchronous; - -// Run request in the background -- (void)startAsynchronous; - -// Clears all delegates and blocks, then cancels the request -- (void)clearDelegatesAndCancel; - -#pragma mark HEAD request - -// Used by ASINetworkQueue to create a HEAD request appropriate for this request with the same headers (though you can use it yourself) -- (ASIHTTPRequest *)HEADRequest; - -#pragma mark upload/download progress - -// Called approximately every 0.25 seconds to update the progress delegates -- (void)updateProgressIndicators; - -// Updates upload progress (notifies the queue and/or uploadProgressDelegate of this request) -- (void)updateUploadProgress; - -// Updates download progress (notifies the queue and/or uploadProgressDelegate of this request) -- (void)updateDownloadProgress; - -// Called when authorisation is needed, as we only find out we don't have permission to something when the upload is complete -- (void)removeUploadProgressSoFar; - -// Called when we get a content-length header and shouldResetDownloadProgress is true -- (void)incrementDownloadSizeBy:(long long)length; - -// Called when a request starts and shouldResetUploadProgress is true -// Also called (with a negative length) to remove the size of the underlying buffer used for uploading -- (void)incrementUploadSizeBy:(long long)length; - -// Helper method for interacting with progress indicators to abstract the details of different APIS (NSProgressIndicator and UIProgressView) -+ (void)updateProgressIndicator:(id *)indicator withProgress:(unsigned long long)progress ofTotal:(unsigned long long)total; - -// Helper method used for performing invocations on the main thread (used for progress) -+ (void)performSelector:(SEL)selector onTarget:(id *)target withObject:(id)object amount:(void *)amount callerToRetain:(id)caller; - -#pragma mark talking to delegates - -// Called when a request starts, lets the delegate know via didStartSelector -- (void)requestStarted; - -// Called when a request receives response headers, lets the delegate know via didReceiveResponseHeadersSelector -- (void)requestReceivedResponseHeaders:(NSDictionary *)newHeaders; - -// Called when a request completes successfully, lets the delegate know via didFinishSelector -- (void)requestFinished; - -// Called when a request fails, and lets the delegate know via didFailSelector -- (void)failWithError:(NSError *)theError; - -// Called to retry our request when our persistent connection is closed -// Returns YES if we haven't already retried, and connection will be restarted -// Otherwise, returns NO, and nothing will happen -- (BOOL)retryUsingNewConnection; - -// Can be called by delegates from inside their willRedirectSelector implementations to restart the request with a new url -- (void)redirectToURL:(NSURL *)newURL; - -#pragma mark parsing HTTP response headers - -// Reads the response headers to find the content length, encoding, cookies for the session -// Also initiates request redirection when shouldRedirect is true -// And works out if HTTP auth is required -- (void)readResponseHeaders; - -// Attempts to set the correct encoding by looking at the Content-Type header, if this is one -- (void)parseStringEncodingFromHeaders; - -+ (void)parseMimeType:(NSString **)mimeType andResponseEncoding:(NSStringEncoding *)stringEncoding fromContentType:(NSString *)contentType; - -#pragma mark http authentication stuff - -// Apply credentials to this request -- (BOOL)applyCredentials:(NSDictionary *)newCredentials; -- (BOOL)applyProxyCredentials:(NSDictionary *)newCredentials; - -// Attempt to obtain credentials for this request from the URL, username and password or keychain -- (NSMutableDictionary *)findCredentials; -- (NSMutableDictionary *)findProxyCredentials; - -// Unlock (unpause) the request thread so it can resume the request -// Should be called by delegates when they have populated the authentication information after an authentication challenge -- (void)retryUsingSuppliedCredentials; - -// Should be called by delegates when they wish to cancel authentication and stop -- (void)cancelAuthentication; - -// Apply authentication information and resume the request after an authentication challenge -- (void)attemptToApplyCredentialsAndResume; -- (void)attemptToApplyProxyCredentialsAndResume; - -// Attempt to show the built-in authentication dialog, returns YES if credentials were supplied, NO if user cancelled dialog / dialog is disabled / running on main thread -// Currently only used on iPhone OS -- (BOOL)showProxyAuthenticationDialog; -- (BOOL)showAuthenticationDialog; - -// Construct a basic authentication header from the username and password supplied, and add it to the request headers -// Used when shouldPresentCredentialsBeforeChallenge is YES -- (void)addBasicAuthenticationHeaderWithUsername:(NSString *)theUsername andPassword:(NSString *)thePassword; - -#pragma mark stream status handlers - -// CFnetwork event handlers -- (void)handleNetworkEvent:(CFStreamEventType)type; -- (void)handleBytesAvailable; -- (void)handleStreamComplete; -- (void)handleStreamError; - -#pragma mark cleanup - -// Cleans up and lets the queue know this operation is finished. -// Appears in this header for subclassing only, do not call this method from outside your request! -- (void)markAsFinished; - -// Cleans up temporary files. There's normally no reason to call these yourself, they are called automatically when a request completes or fails - -// Clean up the temporary file used to store the downloaded data when it comes in (if downloadDestinationPath is set) -- (BOOL)removeTemporaryDownloadFile; - -// Clean up the temporary file used to store data that is inflated (decompressed) as it comes in -- (BOOL)removeTemporaryUncompressedDownloadFile; - -// Clean up the temporary file used to store the request body (when shouldStreamPostDataFromDisk is YES) -- (BOOL)removeTemporaryUploadFile; - -// Clean up the temporary file used to store a deflated (compressed) request body when shouldStreamPostDataFromDisk is YES -- (BOOL)removeTemporaryCompressedUploadFile; - -// Remove a file on disk, returning NO and populating the passed error pointer if it fails -+ (BOOL)removeFileAtPath:(NSString *)path error:(NSError **)err; - -#pragma mark persistent connections - -// Get the ID of the connection this request used (only really useful in tests and debugging) -- (NSNumber *)connectionID; - -// Called automatically when a request is started to clean up any persistent connections that have expired -+ (void)expirePersistentConnections; - -#pragma mark default time out - -+ (NSTimeInterval)defaultTimeOutSeconds; -+ (void)setDefaultTimeOutSeconds:(NSTimeInterval)newTimeOutSeconds; - -#pragma mark client certificate - -- (void)setClientCertificateIdentity:(SecIdentityRef)anIdentity; - -#pragma mark session credentials - -+ (NSMutableArray *)sessionProxyCredentialsStore; -+ (NSMutableArray *)sessionCredentialsStore; - -+ (void)storeProxyAuthenticationCredentialsInSessionStore:(NSDictionary *)credentials; -+ (void)storeAuthenticationCredentialsInSessionStore:(NSDictionary *)credentials; - -+ (void)removeProxyAuthenticationCredentialsFromSessionStore:(NSDictionary *)credentials; -+ (void)removeAuthenticationCredentialsFromSessionStore:(NSDictionary *)credentials; - -- (NSDictionary *)findSessionProxyAuthenticationCredentials; -- (NSDictionary *)findSessionAuthenticationCredentials; - -#pragma mark keychain storage - -// Save credentials for this request to the keychain -- (void)saveCredentialsToKeychain:(NSDictionary *)newCredentials; - -// Save credentials to the keychain -+ (void)saveCredentials:(NSURLCredential *)credentials forHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm; -+ (void)saveCredentials:(NSURLCredential *)credentials forProxy:(NSString *)host port:(int)port realm:(NSString *)realm; - -// Return credentials from the keychain -+ (NSURLCredential *)savedCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm; -+ (NSURLCredential *)savedCredentialsForProxy:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm; - -// Remove credentials from the keychain -+ (void)removeCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm; -+ (void)removeCredentialsForProxy:(NSString *)host port:(int)port realm:(NSString *)realm; - -// We keep track of any cookies we accept, so that we can remove them from the persistent store later -+ (void)setSessionCookies:(NSMutableArray *)newSessionCookies; -+ (NSMutableArray *)sessionCookies; - -// Adds a cookie to our list of cookies we've accepted, checking first for an old version of the same cookie and removing that -+ (void)addSessionCookie:(NSHTTPCookie *)newCookie; - -// Dump all session data (authentication and cookies) -+ (void)clearSession; - -#pragma mark get user agent - -// Will be used as a user agent if requests do not specify a custom user agent -// Is only used when you have specified a Bundle Display Name (CFDisplayBundleName) or Bundle Name (CFBundleName) in your plist -+ (NSString *)defaultUserAgentString; -+ (void)setDefaultUserAgentString:(NSString *)agent; - -#pragma mark mime-type detection - -// Return the mime type for a file -+ (NSString *)mimeTypeForFileAtPath:(NSString *)path; - -#pragma mark bandwidth measurement / throttling - -// The maximum number of bytes ALL requests can send / receive in a second -// This is a rough figure. The actual amount used will be slightly more, this does not include HTTP headers -+ (unsigned long)maxBandwidthPerSecond; -+ (void)setMaxBandwidthPerSecond:(unsigned long)bytes; - -// Get a rough average (for the last 5 seconds) of how much bandwidth is being used, in bytes -+ (unsigned long)averageBandwidthUsedPerSecond; - -- (void)performThrottling; - -// Will return YES is bandwidth throttling is currently in use -+ (BOOL)isBandwidthThrottled; - -// Used internally to record bandwidth use, and by ASIInputStreams when uploading. It's probably best if you don't mess with this. -+ (void)incrementBandwidthUsedInLastSecond:(unsigned long)bytes; - -// On iPhone, ASIHTTPRequest can automatically turn throttling on and off as the connection type changes between WWAN and WiFi - -#if TARGET_OS_IPHONE -// Set to YES to automatically turn on throttling when WWAN is connected, and automatically turn it off when it isn't -+ (void)setShouldThrottleBandwidthForWWAN:(BOOL)throttle; - -// Turns on throttling automatically when WWAN is connected using a custom limit, and turns it off automatically when it isn't -+ (void)throttleBandwidthForWWANUsingLimit:(unsigned long)limit; - -#pragma mark reachability - -// Returns YES when an iPhone OS device is connected via WWAN, false when connected via WIFI or not connected -+ (BOOL)isNetworkReachableViaWWAN; - -#endif - -#pragma mark queue - -// Returns the shared queue -+ (NSOperationQueue *)sharedQueue; - -#pragma mark cache - -+ (void)setDefaultCache:(id )cache; -+ (id )defaultCache; - -// Returns the maximum amount of data we can read as part of the current measurement period, and sleeps this thread if our allowance is used up -+ (unsigned long)maxUploadReadLength; - -#pragma mark network activity - -+ (BOOL)isNetworkInUse; - -+ (void)setShouldUpdateNetworkActivityIndicator:(BOOL)shouldUpdate; - -// Shows the network activity spinner thing on iOS. You may wish to override this to do something else in Mac projects -+ (void)showNetworkActivityIndicator; - -// Hides the network activity spinner thing on iOS -+ (void)hideNetworkActivityIndicator; - -#pragma mark miscellany - -// Used for generating Authorization header when using basic authentication when shouldPresentCredentialsBeforeChallenge is true -// And also by ASIS3Request -+ (NSString *)base64forData:(NSData *)theData; - -// Returns the expiration date for the request. -// Calculated from the Expires response header property, unless maxAge is non-zero or -// there exists a non-zero max-age property in the Cache-Control response header. -+ (NSDate *)expiryDateForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge; - -// Returns a date from a string in RFC1123 format -+ (NSDate *)dateFromRFC1123String:(NSString *)string; - - -// Used for detecting multitasking support at runtime (for backgrounding requests) -#if TARGET_OS_IPHONE -+ (BOOL)isMultitaskingSupported; -#endif - -#pragma mark threading behaviour - -// In the default implementation, all requests run in a single background thread -// Advanced users only: Override this method in a subclass for a different threading behaviour -// Eg: return [NSThread mainThread] to run all requests in the main thread -// Alternatively, you can create a thread on demand, or manage a pool of threads -// Threads returned by this method will need to run the runloop in default mode (eg CFRunLoopRun()) -// Requests will stop the runloop when they complete -// If you have multiple requests sharing the thread you'll need to restart the runloop when this happens -+ (NSThread *)threadForRequest:(ASIHTTPRequest *)request; - - -#pragma mark === - -@property (retain) NSString *username; -@property (retain) NSString *password; -@property (retain) NSString *userAgent; -@property (retain) NSString *domain; - -@property (retain) NSString *proxyUsername; -@property (retain) NSString *proxyPassword; -@property (retain) NSString *proxyDomain; - -@property (retain) NSString *proxyHost; -@property (assign) int proxyPort; -@property (retain) NSString *proxyType; - -@property (retain,setter=setURL:, nonatomic) NSURL *url; -@property (retain) NSURL *originalURL; -@property (assign, nonatomic) id delegate; -@property (retain, nonatomic) id queue; -@property (assign, nonatomic) id uploadProgressDelegate; -@property (assign, nonatomic) id downloadProgressDelegate; -@property (assign) BOOL useKeychainPersistence; -@property (assign) BOOL useSessionPersistence; -@property (retain) NSString *downloadDestinationPath; -@property (retain) NSString *temporaryFileDownloadPath; -@property (retain) NSString *temporaryUncompressedDataDownloadPath; -@property (assign) SEL didStartSelector; -@property (assign) SEL didReceiveResponseHeadersSelector; -@property (assign) SEL willRedirectSelector; -@property (assign) SEL didFinishSelector; -@property (assign) SEL didFailSelector; -@property (assign) SEL didReceiveDataSelector; -@property (retain,readonly) NSString *authenticationRealm; -@property (retain,readonly) NSString *proxyAuthenticationRealm; -@property (retain) NSError *error; -@property (assign,readonly) BOOL complete; -@property (retain) NSDictionary *responseHeaders; -@property (retain) NSMutableDictionary *requestHeaders; -@property (retain) NSMutableArray *requestCookies; -@property (retain,readonly) NSArray *responseCookies; -@property (assign) BOOL useCookiePersistence; -@property (retain) NSDictionary *requestCredentials; -@property (retain) NSDictionary *proxyCredentials; -@property (assign,readonly) int responseStatusCode; -@property (retain,readonly) NSString *responseStatusMessage; -@property (retain) NSMutableData *rawResponseData; -@property (assign) NSTimeInterval timeOutSeconds; -@property (retain, nonatomic) NSString *requestMethod; -@property (retain) NSMutableData *postBody; -@property (assign) unsigned long long contentLength; -@property (assign) unsigned long long postLength; -@property (assign) BOOL shouldResetDownloadProgress; -@property (assign) BOOL shouldResetUploadProgress; -@property (assign) ASIHTTPRequest *mainRequest; -@property (assign) BOOL showAccurateProgress; -@property (assign) unsigned long long totalBytesRead; -@property (assign) unsigned long long totalBytesSent; -@property (assign) NSStringEncoding defaultResponseEncoding; -@property (assign) NSStringEncoding responseEncoding; -@property (assign) BOOL allowCompressedResponse; -@property (assign) BOOL allowResumeForFileDownloads; -@property (retain) NSDictionary *userInfo; -@property (assign) NSInteger tag; -@property (retain) NSString *postBodyFilePath; -@property (assign) BOOL shouldStreamPostDataFromDisk; -@property (assign) BOOL didCreateTemporaryPostDataFile; -@property (assign) BOOL useHTTPVersionOne; -@property (assign, readonly) unsigned long long partialDownloadSize; -@property (assign) BOOL shouldRedirect; -@property (assign) BOOL validatesSecureCertificate; -@property (assign) BOOL shouldCompressRequestBody; -@property (retain) NSURL *PACurl; -@property (retain) NSString *authenticationScheme; -@property (retain) NSString *proxyAuthenticationScheme; -@property (assign) BOOL shouldPresentAuthenticationDialog; -@property (assign) BOOL shouldPresentProxyAuthenticationDialog; -@property (assign, readonly) ASIAuthenticationState authenticationNeeded; -@property (assign) BOOL shouldPresentCredentialsBeforeChallenge; -@property (assign, readonly) int authenticationRetryCount; -@property (assign, readonly) int proxyAuthenticationRetryCount; -@property (assign) BOOL haveBuiltRequestHeaders; -@property (assign, nonatomic) BOOL haveBuiltPostBody; -@property (assign, readonly) BOOL inProgress; -@property (assign) int numberOfTimesToRetryOnTimeout; -@property (assign, readonly) int retryCount; -@property (assign) BOOL shouldAttemptPersistentConnection; -@property (assign) NSTimeInterval persistentConnectionTimeoutSeconds; -@property (assign) BOOL shouldUseRFC2616RedirectBehaviour; -@property (assign, readonly) BOOL connectionCanBeReused; -@property (retain, readonly) NSNumber *requestID; -@property (assign) id downloadCache; -@property (assign) ASICachePolicy cachePolicy; -@property (assign) ASICacheStoragePolicy cacheStoragePolicy; -@property (assign, readonly) BOOL didUseCachedResponse; -@property (assign) NSTimeInterval secondsToCache; -@property (retain) NSArray *clientCertificates; -#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 -@property (assign) BOOL shouldContinueWhenAppEntersBackground; -#endif -@property (retain) ASIDataDecompressor *dataDecompressor; -@property (assign) BOOL shouldWaitToInflateCompressedResponses; - -@end diff --git a/ASIHTTP/ASIHTTPRequest.m b/ASIHTTP/ASIHTTPRequest.m deleted file mode 100644 index c3ba4f6..0000000 --- a/ASIHTTP/ASIHTTPRequest.m +++ /dev/null @@ -1,5107 +0,0 @@ -// -// ASIHTTPRequest.m -// -// Created by Ben Copsey on 04/10/2007. -// Copyright 2007-2011 All-Seeing Interactive. All rights reserved. -// -// A guide to the main features is available at: -// http://allseeing-i.com/ASIHTTPRequest -// -// Portions are based on the ImageClient example from Apple: -// See: http://developer.apple.com/samplecode/ImageClient/listing37.html - -#import "ASIHTTPRequest.h" - -#if TARGET_OS_IPHONE -#import "Reachability.h" -#import "ASIAuthenticationDialog.h" -#import -#else -#import -#endif -#import "ASIInputStream.h" -#import "ASIDataDecompressor.h" -#import "ASIDataCompressor.h" - -// Automatically set on build -NSString *ASIHTTPRequestVersion = @"v1.8.1-61 2011-09-19"; - -static NSString *defaultUserAgent = nil; - -NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain"; - -static NSString *ASIHTTPRequestRunLoopMode = @"ASIHTTPRequestRunLoopMode"; - -static const CFOptionFlags kNetworkEvents = kCFStreamEventHasBytesAvailable | kCFStreamEventEndEncountered | kCFStreamEventErrorOccurred; - -// In memory caches of credentials, used on when useSessionPersistence is YES -static NSMutableArray *sessionCredentialsStore = nil; -static NSMutableArray *sessionProxyCredentialsStore = nil; - -// This lock mediates access to session credentials -static NSRecursiveLock *sessionCredentialsLock = nil; - -// We keep track of cookies we have received here so we can remove them from the sharedHTTPCookieStorage later -static NSMutableArray *sessionCookies = nil; - -// The number of times we will allow requests to redirect before we fail with a redirection error -const int RedirectionLimit = 5; - -// The default number of seconds to use for a timeout -static NSTimeInterval defaultTimeOutSeconds = 10; - -static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventType type, void *clientCallBackInfo) { - [((ASIHTTPRequest*)clientCallBackInfo) handleNetworkEvent: type]; -} - -// This lock prevents the operation from being cancelled while it is trying to update the progress, and vice versa -static NSRecursiveLock *progressLock; - -static NSError *ASIRequestCancelledError; -static NSError *ASIRequestTimedOutError; -static NSError *ASIAuthenticationError; -static NSError *ASIUnableToCreateRequestError; -static NSError *ASITooMuchRedirectionError; - -static NSMutableArray *bandwidthUsageTracker = nil; -static unsigned long averageBandwidthUsedPerSecond = 0; - -// These are used for queuing persistent connections on the same connection - -// Incremented every time we specify we want a new connection -static unsigned int nextConnectionNumberToCreate = 0; - -// An array of connectionInfo dictionaries. -// When attempting a persistent connection, we look here to try to find an existing connection to the same server that is currently not in use -static NSMutableArray *persistentConnectionsPool = nil; - -// Mediates access to the persistent connections pool -static NSRecursiveLock *connectionsLock = nil; - -// Each request gets a new id, we store this rather than a ref to the request itself in the connectionInfo dictionary. -// We do this so we don't have to keep the request around while we wait for the connection to expire -static unsigned int nextRequestID = 0; - -// Records how much bandwidth all requests combined have used in the last second -static unsigned long bandwidthUsedInLastSecond = 0; - -// A date one second in the future from the time it was created -static NSDate *bandwidthMeasurementDate = nil; - -// Since throttling variables are shared among all requests, we'll use a lock to mediate access -static NSLock *bandwidthThrottlingLock = nil; - -// the maximum number of bytes that can be transmitted in one second -static unsigned long maxBandwidthPerSecond = 0; - -// A default figure for throttling bandwidth on mobile devices -unsigned long const ASIWWANBandwidthThrottleAmount = 14800; - -#if TARGET_OS_IPHONE -// YES when bandwidth throttling is active -// This flag does not denote whether throttling is turned on - rather whether it is currently in use -// It will be set to NO when throttling was turned on with setShouldThrottleBandwidthForWWAN, but a WI-FI connection is active -static BOOL isBandwidthThrottled = NO; - -// When YES, bandwidth will be automatically throttled when using WWAN (3G/Edge/GPRS) -// Wifi will not be throttled -static BOOL shouldThrottleBandwidthForWWANOnly = NO; -#endif - -// Mediates access to the session cookies so requests -static NSRecursiveLock *sessionCookiesLock = nil; - -// This lock ensures delegates only receive one notification that authentication is required at once -// When using ASIAuthenticationDialogs, it also ensures only one dialog is shown at once -// If a request can't acquire the lock immediately, it means a dialog is being shown or a delegate is handling the authentication challenge -// Once it gets the lock, it will try to look for existing credentials again rather than showing the dialog / notifying the delegate -// This is so it can make use of any credentials supplied for the other request, if they are appropriate -static NSRecursiveLock *delegateAuthenticationLock = nil; - -// When throttling bandwidth, Set to a date in future that we will allow all requests to wake up and reschedule their streams -static NSDate *throttleWakeUpTime = nil; - -static id defaultCache = nil; - -// Used for tracking when requests are using the network -static unsigned int runningRequestCount = 0; - -// You can use [ASIHTTPRequest setShouldUpdateNetworkActivityIndicator:NO] if you want to manage it yourself -// Alternatively, override showNetworkActivityIndicator / hideNetworkActivityIndicator -// By default this does nothing on Mac OS X, but again override the above methods for a different behaviour -static BOOL shouldUpdateNetworkActivityIndicator = YES; - -// The thread all requests will run on -// Hangs around forever, but will be blocked unless there are requests underway -static NSThread *networkThread = nil; - -static NSOperationQueue *sharedQueue = nil; - -// Private stuff -@interface ASIHTTPRequest () - -- (void)cancelLoad; - -- (void)destroyReadStream; -- (void)scheduleReadStream; -- (void)unscheduleReadStream; - -- (BOOL)willAskDelegateForCredentials; -- (BOOL)willAskDelegateForProxyCredentials; -- (void)askDelegateForProxyCredentials; -- (void)askDelegateForCredentials; -- (void)failAuthentication; - -+ (void)measureBandwidthUsage; -+ (void)recordBandwidthUsage; - -- (void)startRequest; -- (void)updateStatus:(NSTimer *)timer; -- (void)checkRequestStatus; -- (void)reportFailure; -- (void)reportFinished; -- (void)markAsFinished; -- (void)performRedirect; -- (BOOL)shouldTimeOut; -- (BOOL)willRedirect; -- (BOOL)willAskDelegateToConfirmRedirect; - -+ (void)performInvocation:(NSInvocation *)invocation onTarget:(id *)target releasingObject:(id)objectToRelease; -+ (void)hideNetworkActivityIndicatorAfterDelay; -+ (void)hideNetworkActivityIndicatorIfNeeeded; -+ (void)runRequests; - -// Handling Proxy autodetection and PAC file downloads -- (BOOL)configureProxies; -- (void)fetchPACFile; -- (void)finishedDownloadingPACFile:(ASIHTTPRequest *)theRequest; -- (void)runPACScript:(NSString *)script; -- (void)timeOutPACRead; - -- (void)useDataFromCache; - -// Called to update the size of a partial download when starting a request, or retrying after a timeout -- (void)updatePartialDownloadSize; - -#if TARGET_OS_IPHONE -+ (void)registerForNetworkReachabilityNotifications; -+ (void)unsubscribeFromNetworkReachabilityNotifications; -// Called when the status of the network changes -+ (void)reachabilityChanged:(NSNotification *)note; -#endif - -#if NS_BLOCKS_AVAILABLE -- (void)performBlockOnMainThread:(ASIBasicBlock)block; -- (void)releaseBlocksOnMainThread; -+ (void)releaseBlocks:(NSArray *)blocks; -- (void)callBlock:(ASIBasicBlock)block; -#endif - - - - - -@property (assign) BOOL complete; -@property (retain) NSArray *responseCookies; -@property (assign) int responseStatusCode; -@property (retain, nonatomic) NSDate *lastActivityTime; - -@property (assign) unsigned long long partialDownloadSize; -@property (assign, nonatomic) unsigned long long uploadBufferSize; -@property (retain, nonatomic) NSOutputStream *postBodyWriteStream; -@property (retain, nonatomic) NSInputStream *postBodyReadStream; -@property (assign, nonatomic) unsigned long long lastBytesRead; -@property (assign, nonatomic) unsigned long long lastBytesSent; -@property (retain) NSRecursiveLock *cancelledLock; -@property (retain, nonatomic) NSOutputStream *fileDownloadOutputStream; -@property (retain, nonatomic) NSOutputStream *inflatedFileDownloadOutputStream; -@property (assign) int authenticationRetryCount; -@property (assign) int proxyAuthenticationRetryCount; -@property (assign, nonatomic) BOOL updatedProgress; -@property (assign, nonatomic) BOOL needsRedirect; -@property (assign, nonatomic) int redirectCount; -@property (retain, nonatomic) NSData *compressedPostBody; -@property (retain, nonatomic) NSString *compressedPostBodyFilePath; -@property (retain) NSString *authenticationRealm; -@property (retain) NSString *proxyAuthenticationRealm; -@property (retain) NSString *responseStatusMessage; -@property (assign) BOOL inProgress; -@property (assign) int retryCount; -@property (assign) BOOL willRetryRequest; -@property (assign) BOOL connectionCanBeReused; -@property (retain, nonatomic) NSMutableDictionary *connectionInfo; -@property (retain, nonatomic) NSInputStream *readStream; -@property (assign) ASIAuthenticationState authenticationNeeded; -@property (assign, nonatomic) BOOL readStreamIsScheduled; -@property (assign, nonatomic) BOOL downloadComplete; -@property (retain) NSNumber *requestID; -@property (assign, nonatomic) NSString *runLoopMode; -@property (retain, nonatomic) NSTimer *statusTimer; -@property (assign) BOOL didUseCachedResponse; -@property (retain, nonatomic) NSURL *redirectURL; - -@property (assign, nonatomic) BOOL isPACFileRequest; -@property (retain, nonatomic) ASIHTTPRequest *PACFileRequest; -@property (retain, nonatomic) NSInputStream *PACFileReadStream; -@property (retain, nonatomic) NSMutableData *PACFileData; - -@property (assign, nonatomic, setter=setSynchronous:) BOOL isSynchronous; -@end - - -@implementation ASIHTTPRequest - -#pragma mark init / dealloc - -+ (void)initialize -{ - if (self == [ASIHTTPRequest class]) { - persistentConnectionsPool = [[NSMutableArray alloc] init]; - connectionsLock = [[NSRecursiveLock alloc] init]; - progressLock = [[NSRecursiveLock alloc] init]; - bandwidthThrottlingLock = [[NSLock alloc] init]; - sessionCookiesLock = [[NSRecursiveLock alloc] init]; - sessionCredentialsLock = [[NSRecursiveLock alloc] init]; - delegateAuthenticationLock = [[NSRecursiveLock alloc] init]; - bandwidthUsageTracker = [[NSMutableArray alloc] initWithCapacity:5]; - ASIRequestTimedOutError = [[NSError alloc] initWithDomain:NetworkRequestErrorDomain code:ASIRequestTimedOutErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"The request timed out",NSLocalizedDescriptionKey,nil]]; - ASIAuthenticationError = [[NSError alloc] initWithDomain:NetworkRequestErrorDomain code:ASIAuthenticationErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Authentication needed",NSLocalizedDescriptionKey,nil]]; - ASIRequestCancelledError = [[NSError alloc] initWithDomain:NetworkRequestErrorDomain code:ASIRequestCancelledErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"The request was cancelled",NSLocalizedDescriptionKey,nil]]; - ASIUnableToCreateRequestError = [[NSError alloc] initWithDomain:NetworkRequestErrorDomain code:ASIUnableToCreateRequestErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to create request (bad url?)",NSLocalizedDescriptionKey,nil]]; - ASITooMuchRedirectionError = [[NSError alloc] initWithDomain:NetworkRequestErrorDomain code:ASITooMuchRedirectionErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"The request failed because it redirected too many times",NSLocalizedDescriptionKey,nil]]; - sharedQueue = [[NSOperationQueue alloc] init]; - [sharedQueue setMaxConcurrentOperationCount:4]; - - } -} - - -- (id)initWithURL:(NSURL *)newURL -{ - self = [self init]; - [self setRequestMethod:@"GET"]; - - [self setRunLoopMode:NSDefaultRunLoopMode]; - [self setShouldAttemptPersistentConnection:YES]; - [self setPersistentConnectionTimeoutSeconds:60.0]; - [self setShouldPresentCredentialsBeforeChallenge:YES]; - [self setShouldRedirect:YES]; - [self setShowAccurateProgress:YES]; - [self setShouldResetDownloadProgress:YES]; - [self setShouldResetUploadProgress:YES]; - [self setAllowCompressedResponse:YES]; - [self setShouldWaitToInflateCompressedResponses:YES]; - [self setDefaultResponseEncoding:NSISOLatin1StringEncoding]; - [self setShouldPresentProxyAuthenticationDialog:YES]; - - [self setTimeOutSeconds:[ASIHTTPRequest defaultTimeOutSeconds]]; - [self setUseSessionPersistence:YES]; - [self setUseCookiePersistence:YES]; - [self setValidatesSecureCertificate:YES]; - [self setRequestCookies:[[[NSMutableArray alloc] init] autorelease]]; - [self setDidStartSelector:@selector(requestStarted:)]; - [self setDidReceiveResponseHeadersSelector:@selector(request:didReceiveResponseHeaders:)]; - [self setWillRedirectSelector:@selector(request:willRedirectToURL:)]; - [self setDidFinishSelector:@selector(requestFinished:)]; - [self setDidFailSelector:@selector(requestFailed:)]; - [self setDidReceiveDataSelector:@selector(request:didReceiveData:)]; - [self setURL:newURL]; - [self setCancelledLock:[[[NSRecursiveLock alloc] init] autorelease]]; - [self setDownloadCache:[[self class] defaultCache]]; - return self; -} - -+ (id)requestWithURL:(NSURL *)newURL -{ - return [[[self alloc] initWithURL:newURL] autorelease]; -} - -+ (id)requestWithURL:(NSURL *)newURL usingCache:(id )cache -{ - return [self requestWithURL:newURL usingCache:cache andCachePolicy:ASIUseDefaultCachePolicy]; -} - -+ (id)requestWithURL:(NSURL *)newURL usingCache:(id )cache andCachePolicy:(ASICachePolicy)policy -{ - ASIHTTPRequest *request = [[[self alloc] initWithURL:newURL] autorelease]; - [request setDownloadCache:cache]; - [request setCachePolicy:policy]; - return request; -} - -- (void)dealloc -{ - [self setAuthenticationNeeded:ASINoAuthenticationNeededYet]; - if (requestAuthentication) { - CFRelease(requestAuthentication); - } - if (proxyAuthentication) { - CFRelease(proxyAuthentication); - } - if (request) { - CFRelease(request); - } - if (clientCertificateIdentity) { - CFRelease(clientCertificateIdentity); - } - [self cancelLoad]; - [redirectURL release]; - [statusTimer invalidate]; - [statusTimer release]; - [queue release]; - [userInfo release]; - [postBody release]; - [compressedPostBody release]; - [error release]; - [requestHeaders release]; - [requestCookies release]; - [downloadDestinationPath release]; - [temporaryFileDownloadPath release]; - [temporaryUncompressedDataDownloadPath release]; - [fileDownloadOutputStream release]; - [inflatedFileDownloadOutputStream release]; - [username release]; - [password release]; - [domain release]; - [authenticationRealm release]; - [authenticationScheme release]; - [requestCredentials release]; - [proxyHost release]; - [proxyType release]; - [proxyUsername release]; - [proxyPassword release]; - [proxyDomain release]; - [proxyAuthenticationRealm release]; - [proxyAuthenticationScheme release]; - [proxyCredentials release]; - [url release]; - [originalURL release]; - [lastActivityTime release]; - [responseCookies release]; - [rawResponseData release]; - [responseHeaders release]; - [requestMethod release]; - [cancelledLock release]; - [postBodyFilePath release]; - [compressedPostBodyFilePath release]; - [postBodyWriteStream release]; - [postBodyReadStream release]; - [PACurl release]; - [clientCertificates release]; - [responseStatusMessage release]; - [connectionInfo release]; - [requestID release]; - [dataDecompressor release]; - [userAgent release]; - - #if NS_BLOCKS_AVAILABLE - [self releaseBlocksOnMainThread]; - #endif - - [super dealloc]; -} - -#if NS_BLOCKS_AVAILABLE -- (void)releaseBlocksOnMainThread -{ - NSMutableArray *blocks = [NSMutableArray array]; - if (completionBlock) { - [blocks addObject:completionBlock]; - [completionBlock release]; - completionBlock = nil; - } - if (failureBlock) { - [blocks addObject:failureBlock]; - [failureBlock release]; - failureBlock = nil; - } - if (startedBlock) { - [blocks addObject:startedBlock]; - [startedBlock release]; - startedBlock = nil; - } - if (headersReceivedBlock) { - [blocks addObject:headersReceivedBlock]; - [headersReceivedBlock release]; - headersReceivedBlock = nil; - } - if (bytesReceivedBlock) { - [blocks addObject:bytesReceivedBlock]; - [bytesReceivedBlock release]; - bytesReceivedBlock = nil; - } - if (bytesSentBlock) { - [blocks addObject:bytesSentBlock]; - [bytesSentBlock release]; - bytesSentBlock = nil; - } - if (downloadSizeIncrementedBlock) { - [blocks addObject:downloadSizeIncrementedBlock]; - [downloadSizeIncrementedBlock release]; - downloadSizeIncrementedBlock = nil; - } - if (uploadSizeIncrementedBlock) { - [blocks addObject:uploadSizeIncrementedBlock]; - [uploadSizeIncrementedBlock release]; - uploadSizeIncrementedBlock = nil; - } - if (dataReceivedBlock) { - [blocks addObject:dataReceivedBlock]; - [dataReceivedBlock release]; - dataReceivedBlock = nil; - } - if (proxyAuthenticationNeededBlock) { - [blocks addObject:proxyAuthenticationNeededBlock]; - [proxyAuthenticationNeededBlock release]; - proxyAuthenticationNeededBlock = nil; - } - if (authenticationNeededBlock) { - [blocks addObject:authenticationNeededBlock]; - [authenticationNeededBlock release]; - authenticationNeededBlock = nil; - } - [[self class] performSelectorOnMainThread:@selector(releaseBlocks:) withObject:blocks waitUntilDone:[NSThread isMainThread]]; -} -// Always called on main thread -+ (void)releaseBlocks:(NSArray *)blocks -{ - // Blocks will be released when this method exits -} -#endif - - -#pragma mark setup request - -- (void)addRequestHeader:(NSString *)header value:(NSString *)value -{ - if (!requestHeaders) { - [self setRequestHeaders:[NSMutableDictionary dictionaryWithCapacity:1]]; - } - [requestHeaders setObject:value forKey:header]; -} - -// This function will be called either just before a request starts, or when postLength is needed, whichever comes first -// postLength must be set by the time this function is complete -- (void)buildPostBody -{ - - if ([self haveBuiltPostBody]) { - return; - } - - // Are we submitting the request body from a file on disk - if ([self postBodyFilePath]) { - - // If we were writing to the post body via appendPostData or appendPostDataFromFile, close the write stream - if ([self postBodyWriteStream]) { - [[self postBodyWriteStream] close]; - [self setPostBodyWriteStream:nil]; - } - - - NSString *path; - if ([self shouldCompressRequestBody]) { - if (![self compressedPostBodyFilePath]) { - [self setCompressedPostBodyFilePath:[NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]]; - - NSError *err = nil; - if (![ASIDataCompressor compressDataFromFile:[self postBodyFilePath] toFile:[self compressedPostBodyFilePath] error:&err]) { - [self failWithError:err]; - return; - } - } - path = [self compressedPostBodyFilePath]; - } else { - path = [self postBodyFilePath]; - } - NSError *err = nil; - [self setPostLength:[[[[[NSFileManager alloc] init] autorelease] attributesOfItemAtPath:path error:&err] fileSize]]; - if (err) { - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIFileManagementError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Failed to get attributes for file at path '%@'",path],NSLocalizedDescriptionKey,error,NSUnderlyingErrorKey,nil]]]; - return; - } - - // Otherwise, we have an in-memory request body - } else { - if ([self shouldCompressRequestBody]) { - NSError *err = nil; - NSData *compressedBody = [ASIDataCompressor compressData:[self postBody] error:&err]; - if (err) { - [self failWithError:err]; - return; - } - [self setCompressedPostBody:compressedBody]; - [self setPostLength:[[self compressedPostBody] length]]; - } else { - [self setPostLength:[[self postBody] length]]; - } - } - - if ([self postLength] > 0) { - if ([requestMethod isEqualToString:@"GET"] || [requestMethod isEqualToString:@"DELETE"] || [requestMethod isEqualToString:@"HEAD"]) { - [self setRequestMethod:@"POST"]; - } - [self addRequestHeader:@"Content-Length" value:[NSString stringWithFormat:@"%llu",[self postLength]]]; - } - [self setHaveBuiltPostBody:YES]; - -} - -// Sets up storage for the post body -- (void)setupPostBody -{ - if ([self shouldStreamPostDataFromDisk]) { - if (![self postBodyFilePath]) { - [self setPostBodyFilePath:[NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]]; - [self setDidCreateTemporaryPostDataFile:YES]; - } - if (![self postBodyWriteStream]) { - [self setPostBodyWriteStream:[[[NSOutputStream alloc] initToFileAtPath:[self postBodyFilePath] append:NO] autorelease]]; - [[self postBodyWriteStream] open]; - } - } else { - if (![self postBody]) { - [self setPostBody:[[[NSMutableData alloc] init] autorelease]]; - } - } -} - -- (void)appendPostData:(NSData *)data -{ - [self setupPostBody]; - if ([data length] == 0) { - return; - } - if ([self shouldStreamPostDataFromDisk]) { - [[self postBodyWriteStream] write:[data bytes] maxLength:[data length]]; - } else { - [[self postBody] appendData:data]; - } -} - -- (void)appendPostDataFromFile:(NSString *)file -{ - [self setupPostBody]; - NSInputStream *stream = [[[NSInputStream alloc] initWithFileAtPath:file] autorelease]; - [stream open]; - NSUInteger bytesRead; - while ([stream hasBytesAvailable]) { - - unsigned char buffer[1024*256]; - bytesRead = [stream read:buffer maxLength:sizeof(buffer)]; - if (bytesRead == 0) { - break; - } - if ([self shouldStreamPostDataFromDisk]) { - [[self postBodyWriteStream] write:buffer maxLength:bytesRead]; - } else { - [[self postBody] appendData:[NSData dataWithBytes:buffer length:bytesRead]]; - } - } - [stream close]; -} - -- (NSString *)requestMethod -{ - [[self cancelledLock] lock]; - NSString *m = requestMethod; - [[self cancelledLock] unlock]; - return m; -} - -- (void)setRequestMethod:(NSString *)newRequestMethod -{ - [[self cancelledLock] lock]; - if (requestMethod != newRequestMethod) { - [requestMethod release]; - requestMethod = [newRequestMethod retain]; - if ([requestMethod isEqualToString:@"POST"] || [requestMethod isEqualToString:@"PUT"] || [postBody length] || postBodyFilePath) { - [self setShouldAttemptPersistentConnection:NO]; - } - } - [[self cancelledLock] unlock]; -} - -- (NSURL *)url -{ - [[self cancelledLock] lock]; - NSURL *u = url; - [[self cancelledLock] unlock]; - return u; -} - - -- (void)setURL:(NSURL *)newURL -{ - [[self cancelledLock] lock]; - if ([newURL isEqual:[self url]]) { - [[self cancelledLock] unlock]; - return; - } - [url release]; - url = [newURL retain]; - if (requestAuthentication) { - CFRelease(requestAuthentication); - requestAuthentication = NULL; - } - if (proxyAuthentication) { - CFRelease(proxyAuthentication); - proxyAuthentication = NULL; - } - if (request) { - CFRelease(request); - request = NULL; - } - [self setRedirectURL:nil]; - [[self cancelledLock] unlock]; -} - -- (id)delegate -{ - [[self cancelledLock] lock]; - id d = delegate; - [[self cancelledLock] unlock]; - return d; -} - -- (void)setDelegate:(id)newDelegate -{ - [[self cancelledLock] lock]; - delegate = newDelegate; - [[self cancelledLock] unlock]; -} - -- (id)queue -{ - [[self cancelledLock] lock]; - id q = queue; - [[self cancelledLock] unlock]; - return q; -} - - -- (void)setQueue:(id)newQueue -{ - [[self cancelledLock] lock]; - if (newQueue != queue) { - [queue release]; - queue = [newQueue retain]; - } - [[self cancelledLock] unlock]; -} - -#pragma mark get information about this request - -// cancel the request - this must be run on the same thread as the request is running on -- (void)cancelOnRequestThread -{ - #if DEBUG_REQUEST_STATUS - ASI_DEBUG_LOG(@"[STATUS] Request cancelled: %@",self); - #endif - - [[self cancelledLock] lock]; - - if ([self isCancelled] || [self complete]) { - [[self cancelledLock] unlock]; - return; - } - [self failWithError:ASIRequestCancelledError]; - [self setComplete:YES]; - [self cancelLoad]; - - CFRetain(self); - [self willChangeValueForKey:@"isCancelled"]; - cancelled = YES; - [self didChangeValueForKey:@"isCancelled"]; - - [[self cancelledLock] unlock]; - CFRelease(self); -} - -- (void)cancel -{ - [self performSelector:@selector(cancelOnRequestThread) onThread:[[self class] threadForRequest:self] withObject:nil waitUntilDone:NO]; -} - -- (void)clearDelegatesAndCancel -{ - [[self cancelledLock] lock]; - - // Clear delegates - [self setDelegate:nil]; - [self setQueue:nil]; - [self setDownloadProgressDelegate:nil]; - [self setUploadProgressDelegate:nil]; - - #if NS_BLOCKS_AVAILABLE - // Clear blocks - [self releaseBlocksOnMainThread]; - #endif - - [[self cancelledLock] unlock]; - [self cancel]; -} - - -- (BOOL)isCancelled -{ - BOOL result; - - [[self cancelledLock] lock]; - result = cancelled; - [[self cancelledLock] unlock]; - - return result; -} - -// Call this method to get the received data as an NSString. Don't use for binary data! -- (NSString *)responseString -{ - NSData *data = [self responseData]; - if (!data) { - return nil; - } - - return [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:[self responseEncoding]] autorelease]; -} - -- (BOOL)isResponseCompressed -{ - NSString *encoding = [[self responseHeaders] objectForKey:@"Content-Encoding"]; - return encoding && [encoding rangeOfString:@"gzip"].location != NSNotFound; -} - -- (NSData *)responseData -{ - if ([self isResponseCompressed] && [self shouldWaitToInflateCompressedResponses]) { - return [ASIDataDecompressor uncompressData:[self rawResponseData] error:NULL]; - } else { - return [self rawResponseData]; - } - return nil; -} - -#pragma mark running a request - -- (void)startSynchronous -{ -#if DEBUG_REQUEST_STATUS || DEBUG_THROTTLING - ASI_DEBUG_LOG(@"[STATUS] Starting synchronous request %@",self); -#endif - [self setSynchronous:YES]; - [self setRunLoopMode:ASIHTTPRequestRunLoopMode]; - [self setInProgress:YES]; - - if (![self isCancelled] && ![self complete]) { - [self main]; - while (!complete) { - [[NSRunLoop currentRunLoop] runMode:[self runLoopMode] beforeDate:[NSDate distantFuture]]; - } - } - - [self setInProgress:NO]; -} - -- (void)start -{ - [self setInProgress:YES]; - [self performSelector:@selector(main) onThread:[[self class] threadForRequest:self] withObject:nil waitUntilDone:NO]; -} - -- (void)startAsynchronous -{ -#if DEBUG_REQUEST_STATUS || DEBUG_THROTTLING - ASI_DEBUG_LOG(@"[STATUS] Starting asynchronous request %@",self); -#endif - [sharedQueue addOperation:self]; -} - -#pragma mark concurrency - -- (BOOL)isConcurrent -{ - return YES; -} - -- (BOOL)isFinished -{ - return finished; -} - -- (BOOL)isExecuting { - return [self inProgress]; -} - -#pragma mark request logic - -// Create the request -- (void)main -{ - @try { - - [[self cancelledLock] lock]; - - #if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 - if ([ASIHTTPRequest isMultitaskingSupported] && [self shouldContinueWhenAppEntersBackground]) { - backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ - // Synchronize the cleanup call on the main thread in case - // the task actually finishes at around the same time. - dispatch_async(dispatch_get_main_queue(), ^{ - if (backgroundTask != UIBackgroundTaskInvalid) - { - [[UIApplication sharedApplication] endBackgroundTask:backgroundTask]; - backgroundTask = UIBackgroundTaskInvalid; - [self cancel]; - } - }); - }]; - } - #endif - - - // A HEAD request generated by an ASINetworkQueue may have set the error already. If so, we should not proceed. - if ([self error]) { - [self setComplete:YES]; - [self markAsFinished]; - return; - } - - [self setComplete:NO]; - [self setDidUseCachedResponse:NO]; - - if (![self url]) { - [self failWithError:ASIUnableToCreateRequestError]; - return; - } - - // Must call before we create the request so that the request method can be set if needs be - if (![self mainRequest]) { - [self buildPostBody]; - } - - if (![[self requestMethod] isEqualToString:@"GET"]) { - [self setDownloadCache:nil]; - } - - - // If we're redirecting, we'll already have a CFHTTPMessageRef - if (request) { - CFRelease(request); - } - - // Create a new HTTP request. - request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, (CFStringRef)[self requestMethod], (CFURLRef)[self url], [self useHTTPVersionOne] ? kCFHTTPVersion1_0 : kCFHTTPVersion1_1); - if (!request) { - [self failWithError:ASIUnableToCreateRequestError]; - return; - } - - //If this is a HEAD request generated by an ASINetworkQueue, we need to let the main request generate its headers first so we can use them - if ([self mainRequest]) { - [[self mainRequest] buildRequestHeaders]; - } - - // Even if this is a HEAD request with a mainRequest, we still need to call to give subclasses a chance to add their own to HEAD requests (ASIS3Request does this) - [self buildRequestHeaders]; - - if ([self downloadCache]) { - - // If this request should use the default policy, set its policy to the download cache's default policy - if (![self cachePolicy]) { - [self setCachePolicy:[[self downloadCache] defaultCachePolicy]]; - } - - // If have have cached data that is valid for this request, use that and stop - if ([[self downloadCache] canUseCachedDataForRequest:self]) { - [self useDataFromCache]; - return; - } - - // If cached data is stale, or we have been told to ask the server if it has been modified anyway, we need to add headers for a conditional GET - if ([self cachePolicy] & (ASIAskServerIfModifiedWhenStaleCachePolicy|ASIAskServerIfModifiedCachePolicy)) { - - NSDictionary *cachedHeaders = [[self downloadCache] cachedResponseHeadersForURL:[self url]]; - if (cachedHeaders) { - NSString *etag = [cachedHeaders objectForKey:@"Etag"]; - if (etag) { - [[self requestHeaders] setObject:etag forKey:@"If-None-Match"]; - } - NSString *lastModified = [cachedHeaders objectForKey:@"Last-Modified"]; - if (lastModified) { - [[self requestHeaders] setObject:lastModified forKey:@"If-Modified-Since"]; - } - } - } - } - - [self applyAuthorizationHeader]; - - - NSString *header; - for (header in [self requestHeaders]) { - CFHTTPMessageSetHeaderFieldValue(request, (CFStringRef)header, (CFStringRef)[[self requestHeaders] objectForKey:header]); - } - - // If we immediately have access to proxy settings, start the request - // Otherwise, we'll start downloading the proxy PAC file, and call startRequest once that process is complete - if ([self configureProxies]) { - [self startRequest]; - } - - } @catch (NSException *exception) { - NSError *underlyingError = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASIUnhandledExceptionError userInfo:[exception userInfo]]; - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIUnhandledExceptionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[exception name],NSLocalizedDescriptionKey,[exception reason],NSLocalizedFailureReasonErrorKey,underlyingError,NSUnderlyingErrorKey,nil]]]; - - } @finally { - [[self cancelledLock] unlock]; - } -} - -- (void)applyAuthorizationHeader -{ - // Do we want to send credentials before we are asked for them? - if (![self shouldPresentCredentialsBeforeChallenge]) { - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ will not send credentials to the server until it asks for them",self); - #endif - return; - } - - NSDictionary *credentials = nil; - - // Do we already have an auth header? - if (![[self requestHeaders] objectForKey:@"Authorization"]) { - - // If we have basic authentication explicitly set and a username and password set on the request, add a basic auth header - if ([self username] && [self password] && [[self authenticationScheme] isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeBasic]) { - [self addBasicAuthenticationHeaderWithUsername:[self username] andPassword:[self password]]; - - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ has a username and password set, and was manually configured to use BASIC. Will send credentials without waiting for an authentication challenge",self); - #endif - - } else { - - // See if we have any cached credentials we can use in the session store - if ([self useSessionPersistence]) { - credentials = [self findSessionAuthenticationCredentials]; - - if (credentials) { - - // When the Authentication key is set, the credentials were stored after an authentication challenge, so we can let CFNetwork apply them - // (credentials for Digest and NTLM will always be stored like this) - if ([credentials objectForKey:@"Authentication"]) { - - // If we've already talked to this server and have valid credentials, let's apply them to the request - if (CFHTTPMessageApplyCredentialDictionary(request, (CFHTTPAuthenticationRef)[credentials objectForKey:@"Authentication"], (CFDictionaryRef)[credentials objectForKey:@"Credentials"], NULL)) { - [self setAuthenticationScheme:[credentials objectForKey:@"AuthenticationScheme"]]; - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ found cached credentials (%@), will reuse without waiting for an authentication challenge",self,[credentials objectForKey:@"AuthenticationScheme"]); - #endif - } else { - [[self class] removeAuthenticationCredentialsFromSessionStore:[credentials objectForKey:@"Credentials"]]; - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Failed to apply cached credentials to request %@. These will be removed from the session store, and this request will wait for an authentication challenge",self); - #endif - } - - // If the Authentication key is not set, these credentials were stored after a username and password set on a previous request passed basic authentication - // When this happens, we'll need to create the Authorization header ourselves - } else { - NSDictionary *usernameAndPassword = [credentials objectForKey:@"Credentials"]; - [self addBasicAuthenticationHeaderWithUsername:[usernameAndPassword objectForKey:(NSString *)kCFHTTPAuthenticationUsername] andPassword:[usernameAndPassword objectForKey:(NSString *)kCFHTTPAuthenticationPassword]]; - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ found cached BASIC credentials from a previous request. Will send credentials without waiting for an authentication challenge",self); - #endif - } - } - } - } - } - - // Apply proxy authentication credentials - if ([self useSessionPersistence]) { - credentials = [self findSessionProxyAuthenticationCredentials]; - if (credentials) { - if (!CFHTTPMessageApplyCredentialDictionary(request, (CFHTTPAuthenticationRef)[credentials objectForKey:@"Authentication"], (CFDictionaryRef)[credentials objectForKey:@"Credentials"], NULL)) { - [[self class] removeProxyAuthenticationCredentialsFromSessionStore:[credentials objectForKey:@"Credentials"]]; - } - } - } -} - -- (void)applyCookieHeader -{ - // Add cookies from the persistent (mac os global) store - if ([self useCookiePersistence]) { - NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[[self url] absoluteURL]]; - if (cookies) { - [[self requestCookies] addObjectsFromArray:cookies]; - } - } - - // Apply request cookies - NSArray *cookies; - if ([self mainRequest]) { - cookies = [[self mainRequest] requestCookies]; - } else { - cookies = [self requestCookies]; - } - if ([cookies count] > 0) { - NSHTTPCookie *cookie; - NSString *cookieHeader = nil; - for (cookie in cookies) { - if (!cookieHeader) { - cookieHeader = [NSString stringWithFormat: @"%@=%@",[cookie name],[cookie value]]; - } else { - cookieHeader = [NSString stringWithFormat: @"%@; %@=%@",cookieHeader,[cookie name],[cookie value]]; - } - } - if (cookieHeader) { - [self addRequestHeader:@"Cookie" value:cookieHeader]; - } - } -} - -- (void)buildRequestHeaders -{ - if ([self haveBuiltRequestHeaders]) { - return; - } - [self setHaveBuiltRequestHeaders:YES]; - - if ([self mainRequest]) { - for (NSString *header in [[self mainRequest] requestHeaders]) { - [self addRequestHeader:header value:[[[self mainRequest] requestHeaders] valueForKey:header]]; - } - return; - } - - [self applyCookieHeader]; - - // Build and set the user agent string if the request does not already have a custom user agent specified - if (![[self requestHeaders] objectForKey:@"User-Agent"]) { - NSString *userAgentString = [self userAgent]; - if (!userAgentString) { - userAgentString = [ASIHTTPRequest defaultUserAgentString]; - } - if (userAgentString) { - [self addRequestHeader:@"User-Agent" value:userAgentString]; - } - } - - - // Accept a compressed response - if ([self allowCompressedResponse]) { - [self addRequestHeader:@"Accept-Encoding" value:@"gzip"]; - } - - // Configure a compressed request body - if ([self shouldCompressRequestBody]) { - [self addRequestHeader:@"Content-Encoding" value:@"gzip"]; - } - - // Should this request resume an existing download? - [self updatePartialDownloadSize]; - if ([self partialDownloadSize]) { - [self addRequestHeader:@"Range" value:[NSString stringWithFormat:@"bytes=%llu-",[self partialDownloadSize]]]; - } -} - -- (void)updatePartialDownloadSize -{ - NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease]; - - if ([self allowResumeForFileDownloads] && [self downloadDestinationPath] && [self temporaryFileDownloadPath] && [fileManager fileExistsAtPath:[self temporaryFileDownloadPath]]) { - NSError *err = nil; - [self setPartialDownloadSize:[[fileManager attributesOfItemAtPath:[self temporaryFileDownloadPath] error:&err] fileSize]]; - if (err) { - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIFileManagementError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Failed to get attributes for file at path '%@'",[self temporaryFileDownloadPath]],NSLocalizedDescriptionKey,error,NSUnderlyingErrorKey,nil]]]; - return; - } - } -} - -- (void)startRequest -{ - if ([self isCancelled]) { - return; - } - - [self performSelectorOnMainThread:@selector(requestStarted) withObject:nil waitUntilDone:[NSThread isMainThread]]; - - [self setDownloadComplete:NO]; - [self setComplete:NO]; - [self setTotalBytesRead:0]; - [self setLastBytesRead:0]; - - if ([self redirectCount] == 0) { - [self setOriginalURL:[self url]]; - } - - // If we're retrying a request, let's remove any progress we made - if ([self lastBytesSent] > 0) { - [self removeUploadProgressSoFar]; - } - - [self setLastBytesSent:0]; - [self setContentLength:0]; - [self setResponseHeaders:nil]; - if (![self downloadDestinationPath]) { - [self setRawResponseData:[[[NSMutableData alloc] init] autorelease]]; - } - - - // - // Create the stream for the request - // - - NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease]; - - [self setReadStreamIsScheduled:NO]; - - // Do we need to stream the request body from disk - if ([self shouldStreamPostDataFromDisk] && [self postBodyFilePath] && [fileManager fileExistsAtPath:[self postBodyFilePath]]) { - - // Are we gzipping the request body? - if ([self compressedPostBodyFilePath] && [fileManager fileExistsAtPath:[self compressedPostBodyFilePath]]) { - [self setPostBodyReadStream:[ASIInputStream inputStreamWithFileAtPath:[self compressedPostBodyFilePath] request:self]]; - } else { - [self setPostBodyReadStream:[ASIInputStream inputStreamWithFileAtPath:[self postBodyFilePath] request:self]]; - } - [self setReadStream:[NSMakeCollectable(CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request,(CFReadStreamRef)[self postBodyReadStream])) autorelease]]; - } else { - - // If we have a request body, we'll stream it from memory using our custom stream, so that we can measure bandwidth use and it can be bandwidth-throttled if necessary - if ([self postBody] && [[self postBody] length] > 0) { - if ([self shouldCompressRequestBody] && [self compressedPostBody]) { - [self setPostBodyReadStream:[ASIInputStream inputStreamWithData:[self compressedPostBody] request:self]]; - } else if ([self postBody]) { - [self setPostBodyReadStream:[ASIInputStream inputStreamWithData:[self postBody] request:self]]; - } - [self setReadStream:[NSMakeCollectable(CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request,(CFReadStreamRef)[self postBodyReadStream])) autorelease]]; - - } else { - [self setReadStream:[NSMakeCollectable(CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request)) autorelease]]; - } - } - - if (![self readStream]) { - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileBuildingRequestType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to create read stream",NSLocalizedDescriptionKey,nil]]]; - return; - } - - - - - // - // Handle SSL certificate settings - // - - if([[[[self url] scheme] lowercaseString] isEqualToString:@"https"]) { - - NSMutableDictionary *sslProperties = [NSMutableDictionary dictionaryWithCapacity:1]; - - // Tell CFNetwork not to validate SSL certificates - if (![self validatesSecureCertificate]) { - [sslProperties setObject:(NSString *)kCFBooleanFalse forKey:(NSString *)kCFStreamSSLValidatesCertificateChain]; - } - - // Tell CFNetwork to use a client certificate - if (clientCertificateIdentity) { - - NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:[clientCertificates count]+1]; - - // The first object in the array is our SecIdentityRef - [certificates addObject:(id)clientCertificateIdentity]; - - // If we've added any additional certificates, add them too - for (id cert in clientCertificates) { - [certificates addObject:cert]; - } - [sslProperties setObject:certificates forKey:(NSString *)kCFStreamSSLCertificates]; - } - - CFReadStreamSetProperty((CFReadStreamRef)[self readStream], kCFStreamPropertySSLSettings, sslProperties); - } - - // - // Handle proxy settings - // - - if ([self proxyHost] && [self proxyPort]) { - NSString *hostKey; - NSString *portKey; - - if (![self proxyType]) { - [self setProxyType:(NSString *)kCFProxyTypeHTTP]; - } - - if ([[self proxyType] isEqualToString:(NSString *)kCFProxyTypeSOCKS]) { - hostKey = (NSString *)kCFStreamPropertySOCKSProxyHost; - portKey = (NSString *)kCFStreamPropertySOCKSProxyPort; - } else { - hostKey = (NSString *)kCFStreamPropertyHTTPProxyHost; - portKey = (NSString *)kCFStreamPropertyHTTPProxyPort; - if ([[[[self url] scheme] lowercaseString] isEqualToString:@"https"]) { - hostKey = (NSString *)kCFStreamPropertyHTTPSProxyHost; - portKey = (NSString *)kCFStreamPropertyHTTPSProxyPort; - } - } - NSMutableDictionary *proxyToUse = [NSMutableDictionary dictionaryWithObjectsAndKeys:[self proxyHost],hostKey,[NSNumber numberWithInt:[self proxyPort]],portKey,nil]; - - if ([[self proxyType] isEqualToString:(NSString *)kCFProxyTypeSOCKS]) { - CFReadStreamSetProperty((CFReadStreamRef)[self readStream], kCFStreamPropertySOCKSProxy, proxyToUse); - } else { - CFReadStreamSetProperty((CFReadStreamRef)[self readStream], kCFStreamPropertyHTTPProxy, proxyToUse); - } - } - - - // - // Handle persistent connections - // - - [ASIHTTPRequest expirePersistentConnections]; - - [connectionsLock lock]; - - - if (![[self url] host] || ![[self url] scheme]) { - [self setConnectionInfo:nil]; - [self setShouldAttemptPersistentConnection:NO]; - } - - // Will store the old stream that was using this connection (if there was one) so we can clean it up once we've opened our own stream - NSInputStream *oldStream = nil; - - // Use a persistent connection if possible - if ([self shouldAttemptPersistentConnection]) { - - - // If we are redirecting, we will re-use the current connection only if we are connecting to the same server - if ([self connectionInfo]) { - - if (![[[self connectionInfo] objectForKey:@"host"] isEqualToString:[[self url] host]] || ![[[self connectionInfo] objectForKey:@"scheme"] isEqualToString:[[self url] scheme]] || [(NSNumber *)[[self connectionInfo] objectForKey:@"port"] intValue] != [[[self url] port] intValue]) { - [self setConnectionInfo:nil]; - - // Check if we should have expired this connection - } else if ([[[self connectionInfo] objectForKey:@"expires"] timeIntervalSinceNow] < 0) { - #if DEBUG_PERSISTENT_CONNECTIONS - ASI_DEBUG_LOG(@"[CONNECTION] Not re-using connection #%i because it has expired",[[[self connectionInfo] objectForKey:@"id"] intValue]); - #endif - [persistentConnectionsPool removeObject:[self connectionInfo]]; - [self setConnectionInfo:nil]; - - } else if ([[self connectionInfo] objectForKey:@"request"] != nil) { - //Some other request reused this connection already - we'll have to create a new one - #if DEBUG_PERSISTENT_CONNECTIONS - ASI_DEBUG_LOG(@"%@ - Not re-using connection #%i for request #%i because it is already used by request #%i",self,[[[self connectionInfo] objectForKey:@"id"] intValue],[[self requestID] intValue],[[[self connectionInfo] objectForKey:@"request"] intValue]); - #endif - [self setConnectionInfo:nil]; - } - } - - - - if (![self connectionInfo] && [[self url] host] && [[self url] scheme]) { // We must have a proper url with a host and scheme, or this will explode - - // Look for a connection to the same server in the pool - for (NSMutableDictionary *existingConnection in persistentConnectionsPool) { - if (![existingConnection objectForKey:@"request"] && [[existingConnection objectForKey:@"host"] isEqualToString:[[self url] host]] && [[existingConnection objectForKey:@"scheme"] isEqualToString:[[self url] scheme]] && [(NSNumber *)[existingConnection objectForKey:@"port"] intValue] == [[[self url] port] intValue]) { - [self setConnectionInfo:existingConnection]; - } - } - } - - if ([[self connectionInfo] objectForKey:@"stream"]) { - oldStream = [[[self connectionInfo] objectForKey:@"stream"] retain]; - - } - - // No free connection was found in the pool matching the server/scheme/port we're connecting to, we'll need to create a new one - if (![self connectionInfo]) { - [self setConnectionInfo:[NSMutableDictionary dictionary]]; - nextConnectionNumberToCreate++; - [[self connectionInfo] setObject:[NSNumber numberWithInt:nextConnectionNumberToCreate] forKey:@"id"]; - [[self connectionInfo] setObject:[[self url] host] forKey:@"host"]; - [[self connectionInfo] setObject:[NSNumber numberWithInt:[[[self url] port] intValue]] forKey:@"port"]; - [[self connectionInfo] setObject:[[self url] scheme] forKey:@"scheme"]; - [persistentConnectionsPool addObject:[self connectionInfo]]; - } - - // If we are retrying this request, it will already have a requestID - if (![self requestID]) { - nextRequestID++; - [self setRequestID:[NSNumber numberWithUnsignedInt:nextRequestID]]; - } - [[self connectionInfo] setObject:[self requestID] forKey:@"request"]; - [[self connectionInfo] setObject:[self readStream] forKey:@"stream"]; - CFReadStreamSetProperty((CFReadStreamRef)[self readStream], kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue); - - #if DEBUG_PERSISTENT_CONNECTIONS - ASI_DEBUG_LOG(@"[CONNECTION] Request #%@ will use connection #%i",[self requestID],[[[self connectionInfo] objectForKey:@"id"] intValue]); - #endif - - - // Tag the stream with an id that tells it which connection to use behind the scenes - // See http://lists.apple.com/archives/macnetworkprog/2008/Dec/msg00001.html for details on this approach - - CFReadStreamSetProperty((CFReadStreamRef)[self readStream], CFSTR("ASIStreamID"), [[self connectionInfo] objectForKey:@"id"]); - - } else { - #if DEBUG_PERSISTENT_CONNECTIONS - ASI_DEBUG_LOG(@"[CONNECTION] Request %@ will not use a persistent connection",self); - #endif - } - - [connectionsLock unlock]; - - // Schedule the stream - if (![self readStreamIsScheduled] && (!throttleWakeUpTime || [throttleWakeUpTime timeIntervalSinceDate:[NSDate date]] < 0)) { - [self scheduleReadStream]; - } - - BOOL streamSuccessfullyOpened = NO; - - - // Start the HTTP connection - CFStreamClientContext ctxt = {0, self, NULL, NULL, NULL}; - if (CFReadStreamSetClient((CFReadStreamRef)[self readStream], kNetworkEvents, ReadStreamClientCallBack, &ctxt)) { - if (CFReadStreamOpen((CFReadStreamRef)[self readStream])) { - streamSuccessfullyOpened = YES; - } - } - - // Here, we'll close the stream that was previously using this connection, if there was one - // We've kept it open until now (when we've just opened a new stream) so that the new stream can make use of the old connection - // http://lists.apple.com/archives/Macnetworkprog/2006/Mar/msg00119.html - if (oldStream) { - [oldStream close]; - [oldStream release]; - oldStream = nil; - } - - if (!streamSuccessfullyOpened) { - [self setConnectionCanBeReused:NO]; - [self destroyReadStream]; - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileBuildingRequestType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to start HTTP connection",NSLocalizedDescriptionKey,nil]]]; - return; - } - - if (![self mainRequest]) { - if ([self shouldResetUploadProgress]) { - if ([self showAccurateProgress]) { - [self incrementUploadSizeBy:[self postLength]]; - } else { - [self incrementUploadSizeBy:1]; - } - [ASIHTTPRequest updateProgressIndicator:&uploadProgressDelegate withProgress:0 ofTotal:1]; - } - if ([self shouldResetDownloadProgress] && ![self partialDownloadSize]) { - [ASIHTTPRequest updateProgressIndicator:&downloadProgressDelegate withProgress:0 ofTotal:1]; - } - } - - - // Record when the request started, so we can timeout if nothing happens - [self setLastActivityTime:[NSDate date]]; - [self setStatusTimer:[NSTimer timerWithTimeInterval:0.25 target:self selector:@selector(updateStatus:) userInfo:nil repeats:YES]]; - [[NSRunLoop currentRunLoop] addTimer:[self statusTimer] forMode:[self runLoopMode]]; -} - -- (void)setStatusTimer:(NSTimer *)timer -{ - CFRetain(self); - // We must invalidate the old timer here, not before we've created and scheduled a new timer - // This is because the timer may be the only thing retaining an asynchronous request - if (statusTimer && timer != statusTimer) { - [statusTimer invalidate]; - [statusTimer release]; - } - statusTimer = [timer retain]; - CFRelease(self); -} - -// This gets fired every 1/4 of a second to update the progress and work out if we need to timeout -- (void)updateStatus:(NSTimer*)timer -{ - [self checkRequestStatus]; - if (![self inProgress]) { - [self setStatusTimer:nil]; - } -} - -- (void)performRedirect -{ - [self setURL:[self redirectURL]]; - [self setComplete:YES]; - [self setNeedsRedirect:NO]; - [self setRedirectCount:[self redirectCount]+1]; - - if ([self redirectCount] > RedirectionLimit) { - // Some naughty / badly coded website is trying to force us into a redirection loop. This is not cool. - [self failWithError:ASITooMuchRedirectionError]; - [self setComplete:YES]; - } else { - // Go all the way back to the beginning and build the request again, so that we can apply any new cookies - [self main]; - } -} - -// Called by delegate to resume loading with a new url after the delegate received request:willRedirectToURL: -- (void)redirectToURL:(NSURL *)newURL -{ - [self setRedirectURL:newURL]; - [self performSelector:@selector(performRedirect) onThread:[[self class] threadForRequest:self] withObject:nil waitUntilDone:NO]; -} - -- (BOOL)shouldTimeOut -{ - NSTimeInterval secondsSinceLastActivity = [[NSDate date] timeIntervalSinceDate:lastActivityTime]; - // See if we need to timeout - if ([self readStream] && [self readStreamIsScheduled] && [self lastActivityTime] && [self timeOutSeconds] > 0 && secondsSinceLastActivity > [self timeOutSeconds]) { - - // We have no body, or we've sent more than the upload buffer size,so we can safely time out here - if ([self postLength] == 0 || ([self uploadBufferSize] > 0 && [self totalBytesSent] > [self uploadBufferSize])) { - return YES; - - // ***Black magic warning*** - // We have a body, but we've taken longer than timeOutSeconds to upload the first small chunk of data - // Since there's no reliable way to track upload progress for the first 32KB (iPhone) or 128KB (Mac) with CFNetwork, we'll be slightly more forgiving on the timeout, as there's a strong chance our connection is just very slow. - } else if (secondsSinceLastActivity > [self timeOutSeconds]*1.5) { - return YES; - } - } - return NO; -} - -- (void)checkRequestStatus -{ - // We won't let the request cancel while we're updating progress / checking for a timeout - [[self cancelledLock] lock]; - // See if our NSOperationQueue told us to cancel - if ([self isCancelled] || [self complete]) { - [[self cancelledLock] unlock]; - return; - } - - [self performThrottling]; - - if ([self shouldTimeOut]) { - // Do we need to auto-retry this request? - if ([self numberOfTimesToRetryOnTimeout] > [self retryCount]) { - - // If we are resuming a download, we may need to update the Range header to take account of data we've just downloaded - [self updatePartialDownloadSize]; - if ([self partialDownloadSize]) { - CFHTTPMessageSetHeaderFieldValue(request, (CFStringRef)@"Range", (CFStringRef)[NSString stringWithFormat:@"bytes=%llu-",[self partialDownloadSize]]); - } - [self setRetryCount:[self retryCount]+1]; - [self unscheduleReadStream]; - [[self cancelledLock] unlock]; - [self startRequest]; - return; - } - [self failWithError:ASIRequestTimedOutError]; - [self cancelLoad]; - [self setComplete:YES]; - [[self cancelledLock] unlock]; - return; - } - - // readStream will be null if we aren't currently running (perhaps we're waiting for a delegate to supply credentials) - if ([self readStream]) { - - // If we have a post body - if ([self postLength]) { - - [self setLastBytesSent:totalBytesSent]; - - // Find out how much data we've uploaded so far - [self setTotalBytesSent:[[NSMakeCollectable(CFReadStreamCopyProperty((CFReadStreamRef)[self readStream], kCFStreamPropertyHTTPRequestBytesWrittenCount)) autorelease] unsignedLongLongValue]]; - if (totalBytesSent > lastBytesSent) { - - // We've uploaded more data, reset the timeout - [self setLastActivityTime:[NSDate date]]; - [ASIHTTPRequest incrementBandwidthUsedInLastSecond:(unsigned long)(totalBytesSent-lastBytesSent)]; - - #if DEBUG_REQUEST_STATUS - if ([self totalBytesSent] == [self postLength]) { - ASI_DEBUG_LOG(@"[STATUS] Request %@ finished uploading data",self); - } - #endif - } - } - - [self updateProgressIndicators]; - - } - - [[self cancelledLock] unlock]; -} - - -// Cancel loading and clean up. DO NOT USE THIS TO CANCEL REQUESTS - use [request cancel] instead -- (void)cancelLoad -{ - // If we're in the middle of downloading a PAC file, let's stop that first - if (PACFileReadStream) { - [PACFileReadStream setDelegate:nil]; - [PACFileReadStream close]; - [self setPACFileReadStream:nil]; - [self setPACFileData:nil]; - } else if (PACFileRequest) { - [PACFileRequest setDelegate:nil]; - [PACFileRequest cancel]; - [self setPACFileRequest:nil]; - } - - [self destroyReadStream]; - - [[self postBodyReadStream] close]; - [self setPostBodyReadStream:nil]; - - if ([self rawResponseData]) { - if (![self complete]) { - [self setRawResponseData:nil]; - } - // If we were downloading to a file - } else if ([self temporaryFileDownloadPath]) { - [[self fileDownloadOutputStream] close]; - [self setFileDownloadOutputStream:nil]; - - [[self inflatedFileDownloadOutputStream] close]; - [self setInflatedFileDownloadOutputStream:nil]; - - // If we haven't said we might want to resume, let's remove the temporary file too - if (![self complete]) { - if (![self allowResumeForFileDownloads]) { - [self removeTemporaryDownloadFile]; - } - [self removeTemporaryUncompressedDownloadFile]; - } - } - - // Clean up any temporary file used to store request body for streaming - if (![self authenticationNeeded] && ![self willRetryRequest] && [self didCreateTemporaryPostDataFile]) { - [self removeTemporaryUploadFile]; - [self removeTemporaryCompressedUploadFile]; - [self setDidCreateTemporaryPostDataFile:NO]; - } -} - -#pragma mark HEAD request - -// Used by ASINetworkQueue to create a HEAD request appropriate for this request with the same headers (though you can use it yourself) -- (ASIHTTPRequest *)HEADRequest -{ - ASIHTTPRequest *headRequest = [[self class] requestWithURL:[self url]]; - - // Copy the properties that make sense for a HEAD request - [headRequest setRequestHeaders:[[[self requestHeaders] mutableCopy] autorelease]]; - [headRequest setRequestCookies:[[[self requestCookies] mutableCopy] autorelease]]; - [headRequest setUseCookiePersistence:[self useCookiePersistence]]; - [headRequest setUseKeychainPersistence:[self useKeychainPersistence]]; - [headRequest setUseSessionPersistence:[self useSessionPersistence]]; - [headRequest setAllowCompressedResponse:[self allowCompressedResponse]]; - [headRequest setUsername:[self username]]; - [headRequest setPassword:[self password]]; - [headRequest setDomain:[self domain]]; - [headRequest setProxyUsername:[self proxyUsername]]; - [headRequest setProxyPassword:[self proxyPassword]]; - [headRequest setProxyDomain:[self proxyDomain]]; - [headRequest setProxyHost:[self proxyHost]]; - [headRequest setProxyPort:[self proxyPort]]; - [headRequest setProxyType:[self proxyType]]; - [headRequest setShouldPresentAuthenticationDialog:[self shouldPresentAuthenticationDialog]]; - [headRequest setShouldPresentProxyAuthenticationDialog:[self shouldPresentProxyAuthenticationDialog]]; - [headRequest setTimeOutSeconds:[self timeOutSeconds]]; - [headRequest setUseHTTPVersionOne:[self useHTTPVersionOne]]; - [headRequest setValidatesSecureCertificate:[self validatesSecureCertificate]]; - [headRequest setClientCertificateIdentity:clientCertificateIdentity]; - [headRequest setClientCertificates:[[clientCertificates copy] autorelease]]; - [headRequest setPACurl:[self PACurl]]; - [headRequest setShouldPresentCredentialsBeforeChallenge:[self shouldPresentCredentialsBeforeChallenge]]; - [headRequest setNumberOfTimesToRetryOnTimeout:[self numberOfTimesToRetryOnTimeout]]; - [headRequest setShouldUseRFC2616RedirectBehaviour:[self shouldUseRFC2616RedirectBehaviour]]; - [headRequest setShouldAttemptPersistentConnection:[self shouldAttemptPersistentConnection]]; - [headRequest setPersistentConnectionTimeoutSeconds:[self persistentConnectionTimeoutSeconds]]; - - [headRequest setMainRequest:self]; - [headRequest setRequestMethod:@"HEAD"]; - return headRequest; -} - - -#pragma mark upload/download progress - - -- (void)updateProgressIndicators -{ - //Only update progress if this isn't a HEAD request used to preset the content-length - if (![self mainRequest]) { - if ([self showAccurateProgress] || ([self complete] && ![self updatedProgress])) { - [self updateUploadProgress]; - [self updateDownloadProgress]; - } - } -} - -- (id)uploadProgressDelegate -{ - [[self cancelledLock] lock]; - id d = [[uploadProgressDelegate retain] autorelease]; - [[self cancelledLock] unlock]; - return d; -} - -- (void)setUploadProgressDelegate:(id)newDelegate -{ - [[self cancelledLock] lock]; - uploadProgressDelegate = newDelegate; - - #if !TARGET_OS_IPHONE - // If the uploadProgressDelegate is an NSProgressIndicator, we set its MaxValue to 1.0 so we can update it as if it were a UIProgressView - double max = 1.0; - [ASIHTTPRequest performSelector:@selector(setMaxValue:) onTarget:&uploadProgressDelegate withObject:nil amount:&max callerToRetain:nil]; - #endif - [[self cancelledLock] unlock]; -} - -- (id)downloadProgressDelegate -{ - [[self cancelledLock] lock]; - id d = [[downloadProgressDelegate retain] autorelease]; - [[self cancelledLock] unlock]; - return d; -} - -- (void)setDownloadProgressDelegate:(id)newDelegate -{ - [[self cancelledLock] lock]; - downloadProgressDelegate = newDelegate; - - #if !TARGET_OS_IPHONE - // If the downloadProgressDelegate is an NSProgressIndicator, we set its MaxValue to 1.0 so we can update it as if it were a UIProgressView - double max = 1.0; - [ASIHTTPRequest performSelector:@selector(setMaxValue:) onTarget:&downloadProgressDelegate withObject:nil amount:&max callerToRetain:nil]; - #endif - [[self cancelledLock] unlock]; -} - - -- (void)updateDownloadProgress -{ - // We won't update download progress until we've examined the headers, since we might need to authenticate - if (![self responseHeaders] || [self needsRedirect] || !([self contentLength] || [self complete])) { - return; - } - - unsigned long long bytesReadSoFar = [self totalBytesRead]+[self partialDownloadSize]; - unsigned long long value = 0; - - if ([self showAccurateProgress] && [self contentLength]) { - value = bytesReadSoFar-[self lastBytesRead]; - if (value == 0) { - return; - } - } else { - value = 1; - [self setUpdatedProgress:YES]; - } - if (!value) { - return; - } - - [ASIHTTPRequest performSelector:@selector(request:didReceiveBytes:) onTarget:&queue withObject:self amount:&value callerToRetain:self]; - [ASIHTTPRequest performSelector:@selector(request:didReceiveBytes:) onTarget:&downloadProgressDelegate withObject:self amount:&value callerToRetain:self]; - - [ASIHTTPRequest updateProgressIndicator:&downloadProgressDelegate withProgress:[self totalBytesRead]+[self partialDownloadSize] ofTotal:[self contentLength]+[self partialDownloadSize]]; - - #if NS_BLOCKS_AVAILABLE - if (bytesReceivedBlock) { - unsigned long long totalSize = [self contentLength] + [self partialDownloadSize]; - [self performBlockOnMainThread:^{ if (bytesReceivedBlock) { bytesReceivedBlock(value, totalSize); }}]; - } - #endif - [self setLastBytesRead:bytesReadSoFar]; -} - -- (void)updateUploadProgress -{ - if ([self isCancelled] || [self totalBytesSent] == 0) { - return; - } - - // If this is the first time we've written to the buffer, totalBytesSent will be the size of the buffer (currently seems to be 128KB on both Leopard and iPhone 2.2.1, 32KB on iPhone 3.0) - // If request body is less than the buffer size, totalBytesSent will be the total size of the request body - // We will remove this from any progress display, as kCFStreamPropertyHTTPRequestBytesWrittenCount does not tell us how much data has actually be written - if ([self uploadBufferSize] == 0 && [self totalBytesSent] != [self postLength]) { - [self setUploadBufferSize:[self totalBytesSent]]; - [self incrementUploadSizeBy:-[self uploadBufferSize]]; - } - - unsigned long long value = 0; - - if ([self showAccurateProgress]) { - if ([self totalBytesSent] == [self postLength] || [self lastBytesSent] > 0) { - value = [self totalBytesSent]-[self lastBytesSent]; - } else { - return; - } - } else { - value = 1; - [self setUpdatedProgress:YES]; - } - - if (!value) { - return; - } - - [ASIHTTPRequest performSelector:@selector(request:didSendBytes:) onTarget:&queue withObject:self amount:&value callerToRetain:self]; - [ASIHTTPRequest performSelector:@selector(request:didSendBytes:) onTarget:&uploadProgressDelegate withObject:self amount:&value callerToRetain:self]; - [ASIHTTPRequest updateProgressIndicator:&uploadProgressDelegate withProgress:[self totalBytesSent]-[self uploadBufferSize] ofTotal:[self postLength]-[self uploadBufferSize]]; - - #if NS_BLOCKS_AVAILABLE - if(bytesSentBlock){ - unsigned long long totalSize = [self postLength]; - [self performBlockOnMainThread:^{ if (bytesSentBlock) { bytesSentBlock(value, totalSize); }}]; - } - #endif -} - - -- (void)incrementDownloadSizeBy:(long long)length -{ - [ASIHTTPRequest performSelector:@selector(request:incrementDownloadSizeBy:) onTarget:&queue withObject:self amount:&length callerToRetain:self]; - [ASIHTTPRequest performSelector:@selector(request:incrementDownloadSizeBy:) onTarget:&downloadProgressDelegate withObject:self amount:&length callerToRetain:self]; - - #if NS_BLOCKS_AVAILABLE - if(downloadSizeIncrementedBlock){ - [self performBlockOnMainThread:^{ if (downloadSizeIncrementedBlock) { downloadSizeIncrementedBlock(length); }}]; - } - #endif -} - -- (void)incrementUploadSizeBy:(long long)length -{ - [ASIHTTPRequest performSelector:@selector(request:incrementUploadSizeBy:) onTarget:&queue withObject:self amount:&length callerToRetain:self]; - [ASIHTTPRequest performSelector:@selector(request:incrementUploadSizeBy:) onTarget:&uploadProgressDelegate withObject:self amount:&length callerToRetain:self]; - - #if NS_BLOCKS_AVAILABLE - if(uploadSizeIncrementedBlock) { - [self performBlockOnMainThread:^{ if (uploadSizeIncrementedBlock) { uploadSizeIncrementedBlock(length); }}]; - } - #endif -} - - --(void)removeUploadProgressSoFar -{ - long long progressToRemove = -[self totalBytesSent]; - [ASIHTTPRequest performSelector:@selector(request:didSendBytes:) onTarget:&queue withObject:self amount:&progressToRemove callerToRetain:self]; - [ASIHTTPRequest performSelector:@selector(request:didSendBytes:) onTarget:&uploadProgressDelegate withObject:self amount:&progressToRemove callerToRetain:self]; - [ASIHTTPRequest updateProgressIndicator:&uploadProgressDelegate withProgress:0 ofTotal:[self postLength]]; - - #if NS_BLOCKS_AVAILABLE - if(bytesSentBlock){ - unsigned long long totalSize = [self postLength]; - [self performBlockOnMainThread:^{ if (bytesSentBlock) { bytesSentBlock(progressToRemove, totalSize); }}]; - } - #endif -} - -#if NS_BLOCKS_AVAILABLE -- (void)performBlockOnMainThread:(ASIBasicBlock)block -{ - [self performSelectorOnMainThread:@selector(callBlock:) withObject:[[block copy] autorelease] waitUntilDone:[NSThread isMainThread]]; -} - -- (void)callBlock:(ASIBasicBlock)block -{ - block(); -} -#endif - - -+ (void)performSelector:(SEL)selector onTarget:(id *)target withObject:(id)object amount:(void *)amount callerToRetain:(id)callerToRetain -{ - if ([*target respondsToSelector:selector]) { - NSMethodSignature *signature = nil; - signature = [*target methodSignatureForSelector:selector]; - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; - - [invocation setSelector:selector]; - - int argumentNumber = 2; - - // If we got an object parameter, we pass a pointer to the object pointer - if (object) { - [invocation setArgument:&object atIndex:argumentNumber]; - argumentNumber++; - } - - // For the amount we'll just pass the pointer directly so NSInvocation will call the method using the number itself rather than a pointer to it - if (amount) { - [invocation setArgument:amount atIndex:argumentNumber]; - } - - SEL callback = @selector(performInvocation:onTarget:releasingObject:); - NSMethodSignature *cbSignature = [ASIHTTPRequest methodSignatureForSelector:callback]; - NSInvocation *cbInvocation = [NSInvocation invocationWithMethodSignature:cbSignature]; - [cbInvocation setSelector:callback]; - [cbInvocation setTarget:self]; - [cbInvocation setArgument:&invocation atIndex:2]; - [cbInvocation setArgument:&target atIndex:3]; - if (callerToRetain) { - [cbInvocation setArgument:&callerToRetain atIndex:4]; - } - - CFRetain(invocation); - - // Used to pass in a request that we must retain until after the call - // We're using CFRetain rather than [callerToRetain retain] so things to avoid earthquakes when using garbage collection - if (callerToRetain) { - CFRetain(callerToRetain); - } - [cbInvocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:[NSThread isMainThread]]; - } -} - -+ (void)performInvocation:(NSInvocation *)invocation onTarget:(id *)target releasingObject:(id)objectToRelease -{ - if (*target && [*target respondsToSelector:[invocation selector]]) { - [invocation invokeWithTarget:*target]; - } - CFRelease(invocation); - if (objectToRelease) { - CFRelease(objectToRelease); - } -} - - -+ (void)updateProgressIndicator:(id *)indicator withProgress:(unsigned long long)progress ofTotal:(unsigned long long)total -{ - #if TARGET_OS_IPHONE - // Cocoa Touch: UIProgressView - SEL selector = @selector(setProgress:); - float progressAmount = (float)((progress*1.0)/(total*1.0)); - - #else - // Cocoa: NSProgressIndicator - double progressAmount = progressAmount = (progress*1.0)/(total*1.0); - SEL selector = @selector(setDoubleValue:); - #endif - - if (![*indicator respondsToSelector:selector]) { - return; - } - - [progressLock lock]; - [ASIHTTPRequest performSelector:selector onTarget:indicator withObject:nil amount:&progressAmount callerToRetain:nil]; - [progressLock unlock]; -} - - -#pragma mark talking to delegates / calling blocks - -/* ALWAYS CALLED ON MAIN THREAD! */ -- (void)requestStarted -{ - if ([self error] || [self mainRequest]) { - return; - } - if (delegate && [delegate respondsToSelector:didStartSelector]) { - [delegate performSelector:didStartSelector withObject:self]; - } - #if NS_BLOCKS_AVAILABLE - if(startedBlock){ - startedBlock(); - } - #endif - if (queue && [queue respondsToSelector:@selector(requestStarted:)]) { - [queue performSelector:@selector(requestStarted:) withObject:self]; - } -} - -/* ALWAYS CALLED ON MAIN THREAD! */ -- (void)requestRedirected -{ - if ([self error] || [self mainRequest]) { - return; - } - - if([[self delegate] respondsToSelector:@selector(requestRedirected:)]){ - [[self delegate] performSelector:@selector(requestRedirected:) withObject:self]; - } - - #if NS_BLOCKS_AVAILABLE - if(requestRedirectedBlock){ - requestRedirectedBlock(); - } - #endif -} - - -/* ALWAYS CALLED ON MAIN THREAD! */ -- (void)requestReceivedResponseHeaders:(NSMutableDictionary *)newResponseHeaders -{ - if ([self error] || [self mainRequest]) { - return; - } - - if (delegate && [delegate respondsToSelector:didReceiveResponseHeadersSelector]) { - [delegate performSelector:didReceiveResponseHeadersSelector withObject:self withObject:newResponseHeaders]; - } - - #if NS_BLOCKS_AVAILABLE - if(headersReceivedBlock){ - headersReceivedBlock(newResponseHeaders); - } - #endif - - if (queue && [queue respondsToSelector:@selector(request:didReceiveResponseHeaders:)]) { - [queue performSelector:@selector(request:didReceiveResponseHeaders:) withObject:self withObject:newResponseHeaders]; - } -} - -/* ALWAYS CALLED ON MAIN THREAD! */ -- (void)requestWillRedirectToURL:(NSURL *)newURL -{ - if ([self error] || [self mainRequest]) { - return; - } - if (delegate && [delegate respondsToSelector:willRedirectSelector]) { - [delegate performSelector:willRedirectSelector withObject:self withObject:newURL]; - } - if (queue && [queue respondsToSelector:@selector(request:willRedirectToURL:)]) { - [queue performSelector:@selector(request:willRedirectToURL:) withObject:self withObject:newURL]; - } -} - -// Subclasses might override this method to process the result in the same thread -// If you do this, don't forget to call [super requestFinished] to let the queue / delegate know we're done -- (void)requestFinished -{ -#if DEBUG_REQUEST_STATUS || DEBUG_THROTTLING - ASI_DEBUG_LOG(@"[STATUS] Request finished: %@",self); -#endif - if ([self error] || [self mainRequest]) { - return; - } - if ([self isPACFileRequest]) { - [self reportFinished]; - } else { - [self performSelectorOnMainThread:@selector(reportFinished) withObject:nil waitUntilDone:[NSThread isMainThread]]; - } -} - -/* ALWAYS CALLED ON MAIN THREAD! */ -- (void)reportFinished -{ - if (delegate && [delegate respondsToSelector:didFinishSelector]) { - [delegate performSelector:didFinishSelector withObject:self]; - } - - #if NS_BLOCKS_AVAILABLE - if(completionBlock){ - completionBlock(); - } - #endif - - if (queue && [queue respondsToSelector:@selector(requestFinished:)]) { - [queue performSelector:@selector(requestFinished:) withObject:self]; - } -} - -/* ALWAYS CALLED ON MAIN THREAD! */ -- (void)reportFailure -{ - if (delegate && [delegate respondsToSelector:didFailSelector]) { - [delegate performSelector:didFailSelector withObject:self]; - } - - #if NS_BLOCKS_AVAILABLE - if(failureBlock){ - failureBlock(); - } - #endif - - if (queue && [queue respondsToSelector:@selector(requestFailed:)]) { - [queue performSelector:@selector(requestFailed:) withObject:self]; - } -} - -/* ALWAYS CALLED ON MAIN THREAD! */ -- (void)passOnReceivedData:(NSData *)data -{ - if (delegate && [delegate respondsToSelector:didReceiveDataSelector]) { - [delegate performSelector:didReceiveDataSelector withObject:self withObject:data]; - } - - #if NS_BLOCKS_AVAILABLE - if (dataReceivedBlock) { - dataReceivedBlock(data); - } - #endif -} - -// Subclasses might override this method to perform error handling in the same thread -// If you do this, don't forget to call [super failWithError:] to let the queue / delegate know we're done -- (void)failWithError:(NSError *)theError -{ -#if DEBUG_REQUEST_STATUS || DEBUG_THROTTLING - ASI_DEBUG_LOG(@"[STATUS] Request %@: %@",self,(theError == ASIRequestCancelledError ? @"Cancelled" : @"Failed")); -#endif - [self setComplete:YES]; - - // Invalidate the current connection so subsequent requests don't attempt to reuse it - if (theError && [theError code] != ASIAuthenticationErrorType && [theError code] != ASITooMuchRedirectionErrorType) { - [connectionsLock lock]; - #if DEBUG_PERSISTENT_CONNECTIONS - ASI_DEBUG_LOG(@"[CONNECTION] Request #%@ failed and will invalidate connection #%@",[self requestID],[[self connectionInfo] objectForKey:@"id"]); - #endif - [[self connectionInfo] removeObjectForKey:@"request"]; - [persistentConnectionsPool removeObject:[self connectionInfo]]; - [connectionsLock unlock]; - [self destroyReadStream]; - } - if ([self connectionCanBeReused]) { - [[self connectionInfo] setObject:[NSDate dateWithTimeIntervalSinceNow:[self persistentConnectionTimeoutSeconds]] forKey:@"expires"]; - } - - if ([self isCancelled] || [self error]) { - return; - } - - // If we have cached data, use it and ignore the error when using ASIFallbackToCacheIfLoadFailsCachePolicy - if ([self downloadCache] && ([self cachePolicy] & ASIFallbackToCacheIfLoadFailsCachePolicy)) { - if ([[self downloadCache] canUseCachedDataForRequest:self]) { - [self useDataFromCache]; - return; - } - } - - - [self setError:theError]; - - ASIHTTPRequest *failedRequest = self; - - // If this is a HEAD request created by an ASINetworkQueue or compatible queue delegate, make the main request fail - if ([self mainRequest]) { - failedRequest = [self mainRequest]; - [failedRequest setError:theError]; - } - - if ([self isPACFileRequest]) { - [failedRequest reportFailure]; - } else { - [failedRequest performSelectorOnMainThread:@selector(reportFailure) withObject:nil waitUntilDone:[NSThread isMainThread]]; - } - - if (!inProgress) - { - // if we're not in progress, we can't notify the queue we've finished (doing so can cause a crash later on) - // "markAsFinished" will be at the start of main() when we are started - return; - } - [self markAsFinished]; -} - -#pragma mark parsing HTTP response headers - -- (void)readResponseHeaders -{ - [self setAuthenticationNeeded:ASINoAuthenticationNeededYet]; - - CFHTTPMessageRef message = (CFHTTPMessageRef)CFReadStreamCopyProperty((CFReadStreamRef)[self readStream], kCFStreamPropertyHTTPResponseHeader); - if (!message) { - return; - } - - // Make sure we've received all the headers - if (!CFHTTPMessageIsHeaderComplete(message)) { - CFRelease(message); - return; - } - - #if DEBUG_REQUEST_STATUS - if ([self totalBytesSent] == [self postLength]) { - ASI_DEBUG_LOG(@"[STATUS] Request %@ received response headers",self); - } - #endif - - [self setResponseHeaders:[NSMakeCollectable(CFHTTPMessageCopyAllHeaderFields(message)) autorelease]]; - [self setResponseStatusCode:(int)CFHTTPMessageGetResponseStatusCode(message)]; - [self setResponseStatusMessage:[NSMakeCollectable(CFHTTPMessageCopyResponseStatusLine(message)) autorelease]]; - - if ([self downloadCache] && ([[self downloadCache] canUseCachedDataForRequest:self])) { - - // Update the expiry date - [[self downloadCache] updateExpiryForRequest:self maxAge:[self secondsToCache]]; - - // Read the response from the cache - [self useDataFromCache]; - - CFRelease(message); - return; - } - - // Is the server response a challenge for credentials? - if ([self responseStatusCode] == 401) { - [self setAuthenticationNeeded:ASIHTTPAuthenticationNeeded]; - } else if ([self responseStatusCode] == 407) { - [self setAuthenticationNeeded:ASIProxyAuthenticationNeeded]; - } else { - #if DEBUG_HTTP_AUTHENTICATION - if ([self authenticationScheme]) { - ASI_DEBUG_LOG(@"[AUTH] Request %@ has passed %@ authentication",self,[self authenticationScheme]); - } - #endif - } - - // Authentication succeeded, or no authentication was required - if (![self authenticationNeeded]) { - - // Did we get here without an authentication challenge? (which can happen when shouldPresentCredentialsBeforeChallenge is YES and basic auth was successful) - if (!requestAuthentication && [[self authenticationScheme] isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeBasic] && [self username] && [self password] && [self useSessionPersistence]) { - - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ passed BASIC authentication, and will save credentials in the session store for future use",self); - #endif - - NSMutableDictionary *newCredentials = [NSMutableDictionary dictionaryWithCapacity:2]; - [newCredentials setObject:[self username] forKey:(NSString *)kCFHTTPAuthenticationUsername]; - [newCredentials setObject:[self password] forKey:(NSString *)kCFHTTPAuthenticationPassword]; - - // Store the credentials in the session - NSMutableDictionary *sessionCredentials = [NSMutableDictionary dictionary]; - [sessionCredentials setObject:newCredentials forKey:@"Credentials"]; - [sessionCredentials setObject:[self url] forKey:@"URL"]; - [sessionCredentials setObject:(NSString *)kCFHTTPAuthenticationSchemeBasic forKey:@"AuthenticationScheme"]; - [[self class] storeAuthenticationCredentialsInSessionStore:sessionCredentials]; - } - } - - // Read response textEncoding - [self parseStringEncodingFromHeaders]; - - // Handle cookies - NSArray *newCookies = [NSHTTPCookie cookiesWithResponseHeaderFields:[self responseHeaders] forURL:[self url]]; - [self setResponseCookies:newCookies]; - - if ([self useCookiePersistence]) { - - // Store cookies in global persistent store - [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:newCookies forURL:[self url] mainDocumentURL:nil]; - - // We also keep any cookies in the sessionCookies array, so that we have a reference to them if we need to remove them later - NSHTTPCookie *cookie; - for (cookie in newCookies) { - [ASIHTTPRequest addSessionCookie:cookie]; - } - } - - // Do we need to redirect? - if (![self willRedirect]) { - // See if we got a Content-length header - NSString *cLength = [responseHeaders valueForKey:@"Content-Length"]; - ASIHTTPRequest *theRequest = self; - if ([self mainRequest]) { - theRequest = [self mainRequest]; - } - - if (cLength) { - unsigned long long length = strtoull([cLength UTF8String], NULL, 0); - - // Workaround for Apache HEAD requests for dynamically generated content returning the wrong Content-Length when using gzip - if ([self mainRequest] && [self allowCompressedResponse] && length == 20 && [self showAccurateProgress] && [self shouldResetDownloadProgress]) { - [[self mainRequest] setShowAccurateProgress:NO]; - [[self mainRequest] incrementDownloadSizeBy:1]; - - } else { - [theRequest setContentLength:length]; - if ([self showAccurateProgress] && [self shouldResetDownloadProgress]) { - [theRequest incrementDownloadSizeBy:[theRequest contentLength]+[theRequest partialDownloadSize]]; - } - } - - } else if ([self showAccurateProgress] && [self shouldResetDownloadProgress]) { - [theRequest setShowAccurateProgress:NO]; - [theRequest incrementDownloadSizeBy:1]; - } - } - - // Handle connection persistence - if ([self shouldAttemptPersistentConnection]) { - - NSString *connectionHeader = [[[self responseHeaders] objectForKey:@"Connection"] lowercaseString]; - - NSString *httpVersion = [NSMakeCollectable(CFHTTPMessageCopyVersion(message)) autorelease]; - - // Don't re-use the connection if the server is HTTP 1.0 and didn't send Connection: Keep-Alive - if (![httpVersion isEqualToString:(NSString *)kCFHTTPVersion1_0] || [connectionHeader isEqualToString:@"keep-alive"]) { - - // See if server explicitly told us to close the connection - if (![connectionHeader isEqualToString:@"close"]) { - - NSString *keepAliveHeader = [[self responseHeaders] objectForKey:@"Keep-Alive"]; - - // If we got a keep alive header, we'll reuse the connection for as long as the server tells us - if (keepAliveHeader) { - int timeout = 0; - int max = 0; - NSScanner *scanner = [NSScanner scannerWithString:keepAliveHeader]; - [scanner scanString:@"timeout=" intoString:NULL]; - [scanner scanInt:&timeout]; - [scanner scanUpToString:@"max=" intoString:NULL]; - [scanner scanString:@"max=" intoString:NULL]; - [scanner scanInt:&max]; - if (max > 5) { - [self setConnectionCanBeReused:YES]; - [self setPersistentConnectionTimeoutSeconds:timeout]; - #if DEBUG_PERSISTENT_CONNECTIONS - ASI_DEBUG_LOG(@"[CONNECTION] Got a keep-alive header, will keep this connection open for %f seconds", [self persistentConnectionTimeoutSeconds]); - #endif - } - - // Otherwise, we'll assume we can keep this connection open - } else { - [self setConnectionCanBeReused:YES]; - #if DEBUG_PERSISTENT_CONNECTIONS - ASI_DEBUG_LOG(@"[CONNECTION] Got no keep-alive header, will keep this connection open for %f seconds", [self persistentConnectionTimeoutSeconds]); - #endif - } - } - } - } - - CFRelease(message); - [self performSelectorOnMainThread:@selector(requestReceivedResponseHeaders:) withObject:[[[self responseHeaders] copy] autorelease] waitUntilDone:[NSThread isMainThread]]; -} - -- (BOOL)willRedirect -{ - // Do we need to redirect? - if (![self shouldRedirect] || ![responseHeaders valueForKey:@"Location"]) { - return NO; - } - - // Note that ASIHTTPRequest does not currently support 305 Use Proxy - int responseCode = [self responseStatusCode]; - if (responseCode != 301 && responseCode != 302 && responseCode != 303 && responseCode != 307) { - return NO; - } - - [self performSelectorOnMainThread:@selector(requestRedirected) withObject:nil waitUntilDone:[NSThread isMainThread]]; - - // By default, we redirect 301 and 302 response codes as GET requests - // According to RFC 2616 this is wrong, but this is what most browsers do, so it's probably what you're expecting to happen - // See also: - // http://allseeing-i.lighthouseapp.com/projects/27881/tickets/27-302-redirection-issue - - if (responseCode != 307 && (![self shouldUseRFC2616RedirectBehaviour] || responseCode == 303)) { - [self setRequestMethod:@"GET"]; - [self setPostBody:nil]; - [self setPostLength:0]; - - // Perhaps there are other headers we should be preserving, but it's hard to know what we need to keep and what to throw away. - NSString *userAgentHeader = [[self requestHeaders] objectForKey:@"User-Agent"]; - NSString *acceptHeader = [[self requestHeaders] objectForKey:@"Accept"]; - [self setRequestHeaders:nil]; - if (userAgentHeader) { - [self addRequestHeader:@"User-Agent" value:userAgentHeader]; - } - if (acceptHeader) { - [self addRequestHeader:@"Accept" value:acceptHeader]; - } - [self setHaveBuiltRequestHeaders:NO]; - - } else { - // Force rebuild the cookie header incase we got some new cookies from this request - // All other request headers will remain as they are for 301 / 302 redirects - [self applyCookieHeader]; - } - - // Force the redirected request to rebuild the request headers (if not a 303, it will re-use old ones, and add any new ones) - [self setRedirectURL:[[NSURL URLWithString:[responseHeaders valueForKey:@"Location"] relativeToURL:[self url]] absoluteURL]]; - [self setNeedsRedirect:YES]; - - // Clear the request cookies - // This means manually added cookies will not be added to the redirect request - only those stored in the global persistent store - // But, this is probably the safest option - we might be redirecting to a different domain - [self setRequestCookies:[NSMutableArray array]]; - - #if DEBUG_REQUEST_STATUS - ASI_DEBUG_LOG(@"[STATUS] Request will redirect (code: %i): %@",responseCode,self); - #endif - - return YES; -} - -- (void)parseStringEncodingFromHeaders -{ - // Handle response text encoding - NSStringEncoding charset = 0; - NSString *mimeType = nil; - [[self class] parseMimeType:&mimeType andResponseEncoding:&charset fromContentType:[[self responseHeaders] valueForKey:@"Content-Type"]]; - if (charset != 0) { - [self setResponseEncoding:charset]; - } else { - [self setResponseEncoding:[self defaultResponseEncoding]]; - } -} - -#pragma mark http authentication - -- (void)saveProxyCredentialsToKeychain:(NSDictionary *)newCredentials -{ - NSURLCredential *authenticationCredentials = [NSURLCredential credentialWithUser:[newCredentials objectForKey:(NSString *)kCFHTTPAuthenticationUsername] password:[newCredentials objectForKey:(NSString *)kCFHTTPAuthenticationPassword] persistence:NSURLCredentialPersistencePermanent]; - if (authenticationCredentials) { - [ASIHTTPRequest saveCredentials:authenticationCredentials forProxy:[self proxyHost] port:[self proxyPort] realm:[self proxyAuthenticationRealm]]; - } -} - - -- (void)saveCredentialsToKeychain:(NSDictionary *)newCredentials -{ - NSURLCredential *authenticationCredentials = [NSURLCredential credentialWithUser:[newCredentials objectForKey:(NSString *)kCFHTTPAuthenticationUsername] password:[newCredentials objectForKey:(NSString *)kCFHTTPAuthenticationPassword] persistence:NSURLCredentialPersistencePermanent]; - - if (authenticationCredentials) { - [ASIHTTPRequest saveCredentials:authenticationCredentials forHost:[[self url] host] port:[[[self url] port] intValue] protocol:[[self url] scheme] realm:[self authenticationRealm]]; - } -} - -- (BOOL)applyProxyCredentials:(NSDictionary *)newCredentials -{ - [self setProxyAuthenticationRetryCount:[self proxyAuthenticationRetryCount]+1]; - - if (newCredentials && proxyAuthentication && request) { - - // Apply whatever credentials we've built up to the old request - if (CFHTTPMessageApplyCredentialDictionary(request, proxyAuthentication, (CFMutableDictionaryRef)newCredentials, NULL)) { - - //If we have credentials and they're ok, let's save them to the keychain - if (useKeychainPersistence) { - [self saveProxyCredentialsToKeychain:newCredentials]; - } - if (useSessionPersistence) { - NSMutableDictionary *sessionProxyCredentials = [NSMutableDictionary dictionary]; - [sessionProxyCredentials setObject:(id)proxyAuthentication forKey:@"Authentication"]; - [sessionProxyCredentials setObject:newCredentials forKey:@"Credentials"]; - [sessionProxyCredentials setObject:[self proxyHost] forKey:@"Host"]; - [sessionProxyCredentials setObject:[NSNumber numberWithInt:[self proxyPort]] forKey:@"Port"]; - [sessionProxyCredentials setObject:[self proxyAuthenticationScheme] forKey:@"AuthenticationScheme"]; - [[self class] storeProxyAuthenticationCredentialsInSessionStore:sessionProxyCredentials]; - } - [self setProxyCredentials:newCredentials]; - return YES; - } else { - [[self class] removeProxyAuthenticationCredentialsFromSessionStore:newCredentials]; - } - } - return NO; -} - -- (BOOL)applyCredentials:(NSDictionary *)newCredentials -{ - [self setAuthenticationRetryCount:[self authenticationRetryCount]+1]; - - if (newCredentials && requestAuthentication && request) { - // Apply whatever credentials we've built up to the old request - if (CFHTTPMessageApplyCredentialDictionary(request, requestAuthentication, (CFMutableDictionaryRef)newCredentials, NULL)) { - - //If we have credentials and they're ok, let's save them to the keychain - if (useKeychainPersistence) { - [self saveCredentialsToKeychain:newCredentials]; - } - if (useSessionPersistence) { - - NSMutableDictionary *sessionCredentials = [NSMutableDictionary dictionary]; - [sessionCredentials setObject:(id)requestAuthentication forKey:@"Authentication"]; - [sessionCredentials setObject:newCredentials forKey:@"Credentials"]; - [sessionCredentials setObject:[self url] forKey:@"URL"]; - [sessionCredentials setObject:[self authenticationScheme] forKey:@"AuthenticationScheme"]; - if ([self authenticationRealm]) { - [sessionCredentials setObject:[self authenticationRealm] forKey:@"AuthenticationRealm"]; - } - [[self class] storeAuthenticationCredentialsInSessionStore:sessionCredentials]; - - } - [self setRequestCredentials:newCredentials]; - return YES; - } else { - [[self class] removeAuthenticationCredentialsFromSessionStore:newCredentials]; - } - } - return NO; -} - -- (NSMutableDictionary *)findProxyCredentials -{ - NSMutableDictionary *newCredentials = [[[NSMutableDictionary alloc] init] autorelease]; - - NSString *user = nil; - NSString *pass = nil; - - ASIHTTPRequest *theRequest = [self mainRequest]; - // If this is a HEAD request generated by an ASINetworkQueue, we'll try to use the details from the main request - if ([theRequest proxyUsername] && [theRequest proxyPassword]) { - user = [theRequest proxyUsername]; - pass = [theRequest proxyPassword]; - - // Let's try to use the ones set in this object - } else if ([self proxyUsername] && [self proxyPassword]) { - user = [self proxyUsername]; - pass = [self proxyPassword]; - } - - // When we connect to a website using NTLM via a proxy, we will use the main credentials - if ((!user || !pass) && [self proxyAuthenticationScheme] == (NSString *)kCFHTTPAuthenticationSchemeNTLM) { - user = [self username]; - pass = [self password]; - } - - - - // Ok, that didn't work, let's try the keychain - // For authenticating proxies, we'll look in the keychain regardless of the value of useKeychainPersistence - if ((!user || !pass)) { - NSURLCredential *authenticationCredentials = [ASIHTTPRequest savedCredentialsForProxy:[self proxyHost] port:[self proxyPort] protocol:[[self url] scheme] realm:[self proxyAuthenticationRealm]]; - if (authenticationCredentials) { - user = [authenticationCredentials user]; - pass = [authenticationCredentials password]; - } - - } - - // Handle NTLM, which requires a domain to be set too - if (CFHTTPAuthenticationRequiresAccountDomain(proxyAuthentication)) { - - NSString *ntlmDomain = [self proxyDomain]; - - // If we have no domain yet - if (!ntlmDomain || [ntlmDomain length] == 0) { - - // Let's try to extract it from the username - NSArray* ntlmComponents = [user componentsSeparatedByString:@"\\"]; - if ([ntlmComponents count] == 2) { - ntlmDomain = [ntlmComponents objectAtIndex:0]; - user = [ntlmComponents objectAtIndex:1]; - - // If we are connecting to a website using NTLM, but we are connecting via a proxy, the string we need may be in the domain property - } else { - ntlmDomain = [self domain]; - } - if (!ntlmDomain) { - ntlmDomain = @""; - } - } - [newCredentials setObject:ntlmDomain forKey:(NSString *)kCFHTTPAuthenticationAccountDomain]; - } - - - // If we have a username and password, let's apply them to the request and continue - if (user && pass) { - [newCredentials setObject:user forKey:(NSString *)kCFHTTPAuthenticationUsername]; - [newCredentials setObject:pass forKey:(NSString *)kCFHTTPAuthenticationPassword]; - return newCredentials; - } - return nil; -} - - -- (NSMutableDictionary *)findCredentials -{ - NSMutableDictionary *newCredentials = [[[NSMutableDictionary alloc] init] autorelease]; - - // First, let's look at the url to see if the username and password were included - NSString *user = [[self url] user]; - NSString *pass = [[self url] password]; - - if (user && pass) { - - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ will use credentials set on its url",self); - #endif - - } else { - - // If this is a HEAD request generated by an ASINetworkQueue, we'll try to use the details from the main request - if ([self mainRequest] && [[self mainRequest] username] && [[self mainRequest] password]) { - user = [[self mainRequest] username]; - pass = [[self mainRequest] password]; - - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ will use credentials from its parent request",self); - #endif - - // Let's try to use the ones set in this object - } else if ([self username] && [self password]) { - user = [self username]; - pass = [self password]; - - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ will use username and password properties as credentials",self); - #endif - } - } - - // Ok, that didn't work, let's try the keychain - if ((!user || !pass) && useKeychainPersistence) { - NSURLCredential *authenticationCredentials = [ASIHTTPRequest savedCredentialsForHost:[[self url] host] port:[[[self url] port] intValue] protocol:[[self url] scheme] realm:[self authenticationRealm]]; - if (authenticationCredentials) { - user = [authenticationCredentials user]; - pass = [authenticationCredentials password]; - #if DEBUG_HTTP_AUTHENTICATION - if (user && pass) { - ASI_DEBUG_LOG(@"[AUTH] Request %@ will use credentials from the keychain",self); - } - #endif - } - } - - // Handle NTLM, which requires a domain to be set too - if (CFHTTPAuthenticationRequiresAccountDomain(requestAuthentication)) { - - NSString *ntlmDomain = [self domain]; - - // If we have no domain yet, let's try to extract it from the username - if (!ntlmDomain || [ntlmDomain length] == 0) { - ntlmDomain = @""; - NSArray* ntlmComponents = [user componentsSeparatedByString:@"\\"]; - if ([ntlmComponents count] == 2) { - ntlmDomain = [ntlmComponents objectAtIndex:0]; - user = [ntlmComponents objectAtIndex:1]; - } - } - [newCredentials setObject:ntlmDomain forKey:(NSString *)kCFHTTPAuthenticationAccountDomain]; - } - - // If we have a username and password, let's apply them to the request and continue - if (user && pass) { - [newCredentials setObject:user forKey:(NSString *)kCFHTTPAuthenticationUsername]; - [newCredentials setObject:pass forKey:(NSString *)kCFHTTPAuthenticationPassword]; - return newCredentials; - } - return nil; -} - -// Called by delegate or authentication dialog to resume loading once authentication info has been populated -- (void)retryUsingSuppliedCredentials -{ - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ received credentials from its delegate or an ASIAuthenticationDialog, will retry",self); - #endif - //If the url was changed by the delegate, our CFHTTPMessageRef will be NULL and we'll go back to the start - if (!request) { - [self performSelector:@selector(main) onThread:[[self class] threadForRequest:self] withObject:nil waitUntilDone:NO]; - return; - } - [self performSelector:@selector(attemptToApplyCredentialsAndResume) onThread:[[self class] threadForRequest:self] withObject:nil waitUntilDone:NO]; -} - -// Called by delegate or authentication dialog to cancel authentication -- (void)cancelAuthentication -{ - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ had authentication cancelled by its delegate or an ASIAuthenticationDialog",self); - #endif - [self performSelector:@selector(failAuthentication) onThread:[[self class] threadForRequest:self] withObject:nil waitUntilDone:NO]; -} - -- (void)failAuthentication -{ - [self failWithError:ASIAuthenticationError]; -} - -- (BOOL)showProxyAuthenticationDialog -{ - if ([self isSynchronous]) { - return NO; - } - - // Mac authentication dialog coming soon! - #if TARGET_OS_IPHONE - if ([self shouldPresentProxyAuthenticationDialog]) { - [ASIAuthenticationDialog performSelectorOnMainThread:@selector(presentAuthenticationDialogForRequest:) withObject:self waitUntilDone:[NSThread isMainThread]]; - return YES; - } - return NO; - #else - return NO; - #endif -} - - -- (BOOL)willAskDelegateForProxyCredentials -{ - if ([self isSynchronous]) { - return NO; - } - - // If we have a delegate, we'll see if it can handle proxyAuthenticationNeededForRequest:. - // Otherwise, we'll try the queue (if this request is part of one) and it will pass the message on to its own delegate - id authenticationDelegate = [self delegate]; - if (!authenticationDelegate) { - authenticationDelegate = [self queue]; - } - - BOOL delegateOrBlockWillHandleAuthentication = NO; - - if ([authenticationDelegate respondsToSelector:@selector(proxyAuthenticationNeededForRequest:)]) { - delegateOrBlockWillHandleAuthentication = YES; - } - - #if NS_BLOCKS_AVAILABLE - if(proxyAuthenticationNeededBlock){ - delegateOrBlockWillHandleAuthentication = YES; - } - #endif - - if (delegateOrBlockWillHandleAuthentication) { - [self performSelectorOnMainThread:@selector(askDelegateForProxyCredentials) withObject:nil waitUntilDone:NO]; - } - - return delegateOrBlockWillHandleAuthentication; -} - -/* ALWAYS CALLED ON MAIN THREAD! */ -- (void)askDelegateForProxyCredentials -{ - id authenticationDelegate = [self delegate]; - if (!authenticationDelegate) { - authenticationDelegate = [self queue]; - } - if ([authenticationDelegate respondsToSelector:@selector(proxyAuthenticationNeededForRequest:)]) { - [authenticationDelegate performSelector:@selector(proxyAuthenticationNeededForRequest:) withObject:self]; - return; - } - #if NS_BLOCKS_AVAILABLE - if(proxyAuthenticationNeededBlock){ - proxyAuthenticationNeededBlock(); - } - #endif -} - - -- (BOOL)willAskDelegateForCredentials -{ - if ([self isSynchronous]) { - return NO; - } - - // If we have a delegate, we'll see if it can handle proxyAuthenticationNeededForRequest:. - // Otherwise, we'll try the queue (if this request is part of one) and it will pass the message on to its own delegate - id authenticationDelegate = [self delegate]; - if (!authenticationDelegate) { - authenticationDelegate = [self queue]; - } - - BOOL delegateOrBlockWillHandleAuthentication = NO; - - if ([authenticationDelegate respondsToSelector:@selector(authenticationNeededForRequest:)]) { - delegateOrBlockWillHandleAuthentication = YES; - } - - #if NS_BLOCKS_AVAILABLE - if (authenticationNeededBlock) { - delegateOrBlockWillHandleAuthentication = YES; - } - #endif - - if (delegateOrBlockWillHandleAuthentication) { - [self performSelectorOnMainThread:@selector(askDelegateForCredentials) withObject:nil waitUntilDone:NO]; - } - return delegateOrBlockWillHandleAuthentication; -} - -/* ALWAYS CALLED ON MAIN THREAD! */ -- (void)askDelegateForCredentials -{ - id authenticationDelegate = [self delegate]; - if (!authenticationDelegate) { - authenticationDelegate = [self queue]; - } - - if ([authenticationDelegate respondsToSelector:@selector(authenticationNeededForRequest:)]) { - [authenticationDelegate performSelector:@selector(authenticationNeededForRequest:) withObject:self]; - return; - } - - #if NS_BLOCKS_AVAILABLE - if (authenticationNeededBlock) { - authenticationNeededBlock(); - } - #endif -} - -- (void)attemptToApplyProxyCredentialsAndResume -{ - - if ([self error] || [self isCancelled]) { - return; - } - - // Read authentication data - if (!proxyAuthentication) { - CFHTTPMessageRef responseHeader = (CFHTTPMessageRef) CFReadStreamCopyProperty((CFReadStreamRef)[self readStream],kCFStreamPropertyHTTPResponseHeader); - proxyAuthentication = CFHTTPAuthenticationCreateFromResponse(NULL, responseHeader); - CFRelease(responseHeader); - [self setProxyAuthenticationScheme:[NSMakeCollectable(CFHTTPAuthenticationCopyMethod(proxyAuthentication)) autorelease]]; - } - - // If we haven't got a CFHTTPAuthenticationRef by now, something is badly wrong, so we'll have to give up - if (!proxyAuthentication) { - [self cancelLoad]; - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to get authentication object from response headers",NSLocalizedDescriptionKey,nil]]]; - return; - } - - // Get the authentication realm - [self setProxyAuthenticationRealm:nil]; - if (!CFHTTPAuthenticationRequiresAccountDomain(proxyAuthentication)) { - [self setProxyAuthenticationRealm:[NSMakeCollectable(CFHTTPAuthenticationCopyRealm(proxyAuthentication)) autorelease]]; - } - - // See if authentication is valid - CFStreamError err; - if (!CFHTTPAuthenticationIsValid(proxyAuthentication, &err)) { - - CFRelease(proxyAuthentication); - proxyAuthentication = NULL; - - // check for bad credentials, so we can give the delegate a chance to replace them - if (err.domain == kCFStreamErrorDomainHTTP && (err.error == kCFStreamErrorHTTPAuthenticationBadUserName || err.error == kCFStreamErrorHTTPAuthenticationBadPassword)) { - - // Prevent more than one request from asking for credentials at once - [delegateAuthenticationLock lock]; - - // We know the credentials we just presented are bad, we should remove them from the session store too - [[self class] removeProxyAuthenticationCredentialsFromSessionStore:proxyCredentials]; - [self setProxyCredentials:nil]; - - - // If the user cancelled authentication via a dialog presented by another request, our queue may have cancelled us - if ([self error] || [self isCancelled]) { - [delegateAuthenticationLock unlock]; - return; - } - - - // Now we've acquired the lock, it may be that the session contains credentials we can re-use for this request - if ([self useSessionPersistence]) { - NSDictionary *credentials = [self findSessionProxyAuthenticationCredentials]; - if (credentials && [self applyProxyCredentials:[credentials objectForKey:@"Credentials"]]) { - [delegateAuthenticationLock unlock]; - [self startRequest]; - return; - } - } - - [self setLastActivityTime:nil]; - - if ([self willAskDelegateForProxyCredentials]) { - [self attemptToApplyProxyCredentialsAndResume]; - [delegateAuthenticationLock unlock]; - return; - } - if ([self showProxyAuthenticationDialog]) { - [self attemptToApplyProxyCredentialsAndResume]; - [delegateAuthenticationLock unlock]; - return; - } - [delegateAuthenticationLock unlock]; - } - [self cancelLoad]; - [self failWithError:ASIAuthenticationError]; - return; - } - - [self cancelLoad]; - - if (proxyCredentials) { - - // We use startRequest rather than starting all over again in load request because NTLM requires we reuse the request - if ((([self proxyAuthenticationScheme] != (NSString *)kCFHTTPAuthenticationSchemeNTLM) || [self proxyAuthenticationRetryCount] < 2) && [self applyProxyCredentials:proxyCredentials]) { - [self startRequest]; - - // We've failed NTLM authentication twice, we should assume our credentials are wrong - } else if ([self proxyAuthenticationScheme] == (NSString *)kCFHTTPAuthenticationSchemeNTLM && [self proxyAuthenticationRetryCount] == 2) { - [self failWithError:ASIAuthenticationError]; - - // Something went wrong, we'll have to give up - } else { - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to apply proxy credentials to request",NSLocalizedDescriptionKey,nil]]]; - } - - // Are a user name & password needed? - } else if (CFHTTPAuthenticationRequiresUserNameAndPassword(proxyAuthentication)) { - - // Prevent more than one request from asking for credentials at once - [delegateAuthenticationLock lock]; - - // If the user cancelled authentication via a dialog presented by another request, our queue may have cancelled us - if ([self error] || [self isCancelled]) { - [delegateAuthenticationLock unlock]; - return; - } - - // Now we've acquired the lock, it may be that the session contains credentials we can re-use for this request - if ([self useSessionPersistence]) { - NSDictionary *credentials = [self findSessionProxyAuthenticationCredentials]; - if (credentials && [self applyProxyCredentials:[credentials objectForKey:@"Credentials"]]) { - [delegateAuthenticationLock unlock]; - [self startRequest]; - return; - } - } - - NSMutableDictionary *newCredentials = [self findProxyCredentials]; - - //If we have some credentials to use let's apply them to the request and continue - if (newCredentials) { - - if ([self applyProxyCredentials:newCredentials]) { - [delegateAuthenticationLock unlock]; - [self startRequest]; - } else { - [delegateAuthenticationLock unlock]; - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to apply proxy credentials to request",NSLocalizedDescriptionKey,nil]]]; - } - - return; - } - - if ([self willAskDelegateForProxyCredentials]) { - [delegateAuthenticationLock unlock]; - return; - } - - if ([self showProxyAuthenticationDialog]) { - [delegateAuthenticationLock unlock]; - return; - } - [delegateAuthenticationLock unlock]; - - // The delegate isn't interested and we aren't showing the authentication dialog, we'll have to give up - [self failWithError:ASIAuthenticationError]; - return; - } - -} - -- (BOOL)showAuthenticationDialog -{ - if ([self isSynchronous]) { - return NO; - } - // Mac authentication dialog coming soon! - #if TARGET_OS_IPHONE - if ([self shouldPresentAuthenticationDialog]) { - [ASIAuthenticationDialog performSelectorOnMainThread:@selector(presentAuthenticationDialogForRequest:) withObject:self waitUntilDone:[NSThread isMainThread]]; - return YES; - } - return NO; - #else - return NO; - #endif -} - -- (void)attemptToApplyCredentialsAndResume -{ - if ([self error] || [self isCancelled]) { - return; - } - - // Do we actually need to authenticate with a proxy? - if ([self authenticationNeeded] == ASIProxyAuthenticationNeeded) { - [self attemptToApplyProxyCredentialsAndResume]; - return; - } - - // Read authentication data - if (!requestAuthentication) { - CFHTTPMessageRef responseHeader = (CFHTTPMessageRef) CFReadStreamCopyProperty((CFReadStreamRef)[self readStream],kCFStreamPropertyHTTPResponseHeader); - requestAuthentication = CFHTTPAuthenticationCreateFromResponse(NULL, responseHeader); - CFRelease(responseHeader); - [self setAuthenticationScheme:[NSMakeCollectable(CFHTTPAuthenticationCopyMethod(requestAuthentication)) autorelease]]; - } - - if (!requestAuthentication) { - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ failed to read authentication information from response headers",self); - #endif - - [self cancelLoad]; - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to get authentication object from response headers",NSLocalizedDescriptionKey,nil]]]; - return; - } - - // Get the authentication realm - [self setAuthenticationRealm:nil]; - if (!CFHTTPAuthenticationRequiresAccountDomain(requestAuthentication)) { - [self setAuthenticationRealm:[NSMakeCollectable(CFHTTPAuthenticationCopyRealm(requestAuthentication)) autorelease]]; - } - - #if DEBUG_HTTP_AUTHENTICATION - NSString *realm = [self authenticationRealm]; - if (realm) { - realm = [NSString stringWithFormat:@" (Realm: %@)",realm]; - } else { - realm = @""; - } - if ([self authenticationScheme] != (NSString *)kCFHTTPAuthenticationSchemeNTLM || [self authenticationRetryCount] == 0) { - ASI_DEBUG_LOG(@"[AUTH] Request %@ received 401 challenge and must authenticate using %@%@",self,[self authenticationScheme],realm); - } else { - ASI_DEBUG_LOG(@"[AUTH] Request %@ NTLM handshake step %i",self,[self authenticationRetryCount]+1); - } - #endif - - // See if authentication is valid - CFStreamError err; - if (!CFHTTPAuthenticationIsValid(requestAuthentication, &err)) { - - CFRelease(requestAuthentication); - requestAuthentication = NULL; - - // check for bad credentials, so we can give the delegate a chance to replace them - if (err.domain == kCFStreamErrorDomainHTTP && (err.error == kCFStreamErrorHTTPAuthenticationBadUserName || err.error == kCFStreamErrorHTTPAuthenticationBadPassword)) { - - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ had bad credentials, will remove them from the session store if they are cached",self); - #endif - - // Prevent more than one request from asking for credentials at once - [delegateAuthenticationLock lock]; - - // We know the credentials we just presented are bad, we should remove them from the session store too - [[self class] removeAuthenticationCredentialsFromSessionStore:requestCredentials]; - [self setRequestCredentials:nil]; - - // If the user cancelled authentication via a dialog presented by another request, our queue may have cancelled us - if ([self error] || [self isCancelled]) { - - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ failed or was cancelled while waiting to access credentials",self); - #endif - - [delegateAuthenticationLock unlock]; - return; - } - - // Now we've acquired the lock, it may be that the session contains credentials we can re-use for this request - if ([self useSessionPersistence]) { - NSDictionary *credentials = [self findSessionAuthenticationCredentials]; - if (credentials && [self applyCredentials:[credentials objectForKey:@"Credentials"]]) { - - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ will reuse cached credentials from the session (%@)",self,[credentials objectForKey:@"AuthenticationScheme"]); - #endif - - [delegateAuthenticationLock unlock]; - [self startRequest]; - return; - } - } - - [self setLastActivityTime:nil]; - - if ([self willAskDelegateForCredentials]) { - - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ will ask its delegate for credentials to use",self); - #endif - - [delegateAuthenticationLock unlock]; - return; - } - if ([self showAuthenticationDialog]) { - - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ will ask ASIAuthenticationDialog for credentials",self); - #endif - - [delegateAuthenticationLock unlock]; - return; - } - [delegateAuthenticationLock unlock]; - } - - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ has no credentials to present and must give up",self); - #endif - - [self cancelLoad]; - [self failWithError:ASIAuthenticationError]; - return; - } - - [self cancelLoad]; - - if (requestCredentials) { - - if ((([self authenticationScheme] != (NSString *)kCFHTTPAuthenticationSchemeNTLM) || [self authenticationRetryCount] < 2) && [self applyCredentials:requestCredentials]) { - [self startRequest]; - - // We've failed NTLM authentication twice, we should assume our credentials are wrong - } else if ([self authenticationScheme] == (NSString *)kCFHTTPAuthenticationSchemeNTLM && [self authenticationRetryCount ] == 2) { - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ has failed NTLM authentication",self); - #endif - - [self failWithError:ASIAuthenticationError]; - - } else { - - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ had credentials and they were not marked as bad, but we got a 401 all the same.",self); - #endif - - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to apply credentials to request",NSLocalizedDescriptionKey,nil]]]; - } - - // Are a user name & password needed? - } else if (CFHTTPAuthenticationRequiresUserNameAndPassword(requestAuthentication)) { - - // Prevent more than one request from asking for credentials at once - [delegateAuthenticationLock lock]; - - // If the user cancelled authentication via a dialog presented by another request, our queue may have cancelled us - if ([self error] || [self isCancelled]) { - - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ failed or was cancelled while waiting to access credentials",self); - #endif - - [delegateAuthenticationLock unlock]; - return; - } - - // Now we've acquired the lock, it may be that the session contains credentials we can re-use for this request - if ([self useSessionPersistence]) { - NSDictionary *credentials = [self findSessionAuthenticationCredentials]; - if (credentials && [self applyCredentials:[credentials objectForKey:@"Credentials"]]) { - - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ will reuse cached credentials from the session (%@)",self,[credentials objectForKey:@"AuthenticationScheme"]); - #endif - - [delegateAuthenticationLock unlock]; - [self startRequest]; - return; - } - } - - - NSMutableDictionary *newCredentials = [self findCredentials]; - - //If we have some credentials to use let's apply them to the request and continue - if (newCredentials) { - - if ([self applyCredentials:newCredentials]) { - [delegateAuthenticationLock unlock]; - [self startRequest]; - } else { - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ failed to apply credentials",self); - #endif - [delegateAuthenticationLock unlock]; - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to apply credentials to request",NSLocalizedDescriptionKey,nil]]]; - } - return; - } - if ([self willAskDelegateForCredentials]) { - - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ will ask its delegate for credentials to use",self); - #endif - - [delegateAuthenticationLock unlock]; - return; - } - if ([self showAuthenticationDialog]) { - - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ will ask ASIAuthenticationDialog for credentials",self); - #endif - - [delegateAuthenticationLock unlock]; - return; - } - - #if DEBUG_HTTP_AUTHENTICATION - ASI_DEBUG_LOG(@"[AUTH] Request %@ has no credentials to present and must give up",self); - #endif - [delegateAuthenticationLock unlock]; - [self failWithError:ASIAuthenticationError]; - return; - } - -} - -- (void)addBasicAuthenticationHeaderWithUsername:(NSString *)theUsername andPassword:(NSString *)thePassword -{ - [self addRequestHeader:@"Authorization" value:[NSString stringWithFormat:@"Basic %@",[ASIHTTPRequest base64forData:[[NSString stringWithFormat:@"%@:%@",theUsername,thePassword] dataUsingEncoding:NSUTF8StringEncoding]]]]; - [self setAuthenticationScheme:(NSString *)kCFHTTPAuthenticationSchemeBasic]; - -} - - -#pragma mark stream status handlers - -- (void)handleNetworkEvent:(CFStreamEventType)type -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - [[self cancelledLock] lock]; - - if ([self complete] || [self isCancelled]) { - [[self cancelledLock] unlock]; - [pool drain]; - return; - } - - CFRetain(self); - - // Dispatch the stream events. - switch (type) { - case kCFStreamEventHasBytesAvailable: - [self handleBytesAvailable]; - break; - - case kCFStreamEventEndEncountered: - [self handleStreamComplete]; - break; - - case kCFStreamEventErrorOccurred: - [self handleStreamError]; - break; - - default: - break; - } - - [self performThrottling]; - - [[self cancelledLock] unlock]; - - if ([self downloadComplete] && [self needsRedirect]) { - if (![self willAskDelegateToConfirmRedirect]) { - [self performRedirect]; - } - } else if ([self downloadComplete] && [self authenticationNeeded]) { - [self attemptToApplyCredentialsAndResume]; - } - - CFRelease(self); - [pool drain]; -} - -- (BOOL)willAskDelegateToConfirmRedirect -{ - // We must lock to ensure delegate / queue aren't changed while we check them - [[self cancelledLock] lock]; - - // Here we perform an initial check to see if either the delegate or the queue wants to be asked about the redirect, because if not we should redirect straight away - // We will check again on the main thread later - BOOL needToAskDelegateAboutRedirect = (([self delegate] && [[self delegate] respondsToSelector:[self willRedirectSelector]]) || ([self queue] && [[self queue] respondsToSelector:@selector(request:willRedirectToURL:)])); - - [[self cancelledLock] unlock]; - - // Either the delegate or the queue's delegate is interested in being told when we are about to redirect - if (needToAskDelegateAboutRedirect) { - NSURL *newURL = [[[self redirectURL] copy] autorelease]; - [self setRedirectURL:nil]; - [self performSelectorOnMainThread:@selector(requestWillRedirectToURL:) withObject:newURL waitUntilDone:[NSThread isMainThread]]; - return true; - } - return false; -} - -- (void)handleBytesAvailable -{ - if (![self responseHeaders]) { - [self readResponseHeaders]; - } - - // If we've cancelled the load part way through (for example, after deciding to use a cached version) - if ([self complete]) { - return; - } - - // In certain (presumably very rare) circumstances, handleBytesAvailable seems to be called when there isn't actually any data available - // We'll check that there is actually data available to prevent blocking on CFReadStreamRead() - // So far, I've only seen this in the stress tests, so it might never happen in real-world situations. - if (!CFReadStreamHasBytesAvailable((CFReadStreamRef)[self readStream])) { - return; - } - - long long bufferSize = 16384; - if (contentLength > 262144) { - bufferSize = 262144; - } else if (contentLength > 65536) { - bufferSize = 65536; - } - - // Reduce the buffer size if we're receiving data too quickly when bandwidth throttling is active - // This just augments the throttling done in measureBandwidthUsage to reduce the amount we go over the limit - - if ([[self class] isBandwidthThrottled]) { - [bandwidthThrottlingLock lock]; - if (maxBandwidthPerSecond > 0) { - long long maxiumumSize = (long long)maxBandwidthPerSecond-(long long)bandwidthUsedInLastSecond; - if (maxiumumSize < 0) { - // We aren't supposed to read any more data right now, but we'll read a single byte anyway so the CFNetwork's buffer isn't full - bufferSize = 1; - } else if (maxiumumSize/4 < bufferSize) { - // We were going to fetch more data that we should be allowed, so we'll reduce the size of our read - bufferSize = maxiumumSize/4; - } - } - if (bufferSize < 1) { - bufferSize = 1; - } - [bandwidthThrottlingLock unlock]; - } - - - UInt8 buffer[bufferSize]; - NSInteger bytesRead = [[self readStream] read:buffer maxLength:sizeof(buffer)]; - - // Less than zero is an error - if (bytesRead < 0) { - [self handleStreamError]; - - // If zero bytes were read, wait for the EOF to come. - } else if (bytesRead) { - - // If we are inflating the response on the fly - NSData *inflatedData = nil; - if ([self isResponseCompressed] && ![self shouldWaitToInflateCompressedResponses]) { - if (![self dataDecompressor]) { - [self setDataDecompressor:[ASIDataDecompressor decompressor]]; - } - NSError *err = nil; - inflatedData = [[self dataDecompressor] uncompressBytes:buffer length:bytesRead error:&err]; - if (err) { - [self failWithError:err]; - return; - } - } - - [self setTotalBytesRead:[self totalBytesRead]+bytesRead]; - [self setLastActivityTime:[NSDate date]]; - - // For bandwidth measurement / throttling - [ASIHTTPRequest incrementBandwidthUsedInLastSecond:bytesRead]; - - // If we need to redirect, and have automatic redirect on, and might be resuming a download, let's do nothing with the content - if ([self needsRedirect] && [self shouldRedirect] && [self allowResumeForFileDownloads]) { - return; - } - - BOOL dataWillBeHandledExternally = NO; - if ([[self delegate] respondsToSelector:[self didReceiveDataSelector]]) { - dataWillBeHandledExternally = YES; - } - #if NS_BLOCKS_AVAILABLE - if (dataReceivedBlock) { - dataWillBeHandledExternally = YES; - } - #endif - // Does the delegate want to handle the data manually? - if (dataWillBeHandledExternally) { - - NSData *data = nil; - if ([self isResponseCompressed] && ![self shouldWaitToInflateCompressedResponses]) { - data = inflatedData; - } else { - data = [NSData dataWithBytes:buffer length:bytesRead]; - } - [self performSelectorOnMainThread:@selector(passOnReceivedData:) withObject:data waitUntilDone:[NSThread isMainThread]]; - - // Are we downloading to a file? - } else if ([self downloadDestinationPath]) { - BOOL append = NO; - if (![self fileDownloadOutputStream]) { - if (![self temporaryFileDownloadPath]) { - [self setTemporaryFileDownloadPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]]; - } else if ([self allowResumeForFileDownloads] && [[self requestHeaders] objectForKey:@"Range"]) { - if ([[self responseHeaders] objectForKey:@"Content-Range"]) { - append = YES; - } else { - [self incrementDownloadSizeBy:-[self partialDownloadSize]]; - [self setPartialDownloadSize:0]; - } - } - - [self setFileDownloadOutputStream:[[[NSOutputStream alloc] initToFileAtPath:[self temporaryFileDownloadPath] append:append] autorelease]]; - [[self fileDownloadOutputStream] open]; - - } - [[self fileDownloadOutputStream] write:buffer maxLength:bytesRead]; - - if ([self isResponseCompressed] && ![self shouldWaitToInflateCompressedResponses]) { - - if (![self inflatedFileDownloadOutputStream]) { - if (![self temporaryUncompressedDataDownloadPath]) { - [self setTemporaryUncompressedDataDownloadPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]]; - } - - [self setInflatedFileDownloadOutputStream:[[[NSOutputStream alloc] initToFileAtPath:[self temporaryUncompressedDataDownloadPath] append:append] autorelease]]; - [[self inflatedFileDownloadOutputStream] open]; - } - - [[self inflatedFileDownloadOutputStream] write:[inflatedData bytes] maxLength:[inflatedData length]]; - } - - - //Otherwise, let's add the data to our in-memory store - } else { - if ([self isResponseCompressed] && ![self shouldWaitToInflateCompressedResponses]) { - [rawResponseData appendData:inflatedData]; - } else { - [rawResponseData appendBytes:buffer length:bytesRead]; - } - } - } -} - -- (void)handleStreamComplete -{ - -#if DEBUG_REQUEST_STATUS - ASI_DEBUG_LOG(@"[STATUS] Request %@ finished downloading data (%qu bytes)",self, [self totalBytesRead]); -#endif - [self setStatusTimer:nil]; - [self setDownloadComplete:YES]; - - if (![self responseHeaders]) { - [self readResponseHeaders]; - } - - [progressLock lock]; - // Find out how much data we've uploaded so far - [self setLastBytesSent:totalBytesSent]; - [self setTotalBytesSent:[[NSMakeCollectable(CFReadStreamCopyProperty((CFReadStreamRef)[self readStream], kCFStreamPropertyHTTPRequestBytesWrittenCount)) autorelease] unsignedLongLongValue]]; - [self setComplete:YES]; - if (![self contentLength]) { - [self setContentLength:[self totalBytesRead]]; - } - [self updateProgressIndicators]; - - - [[self postBodyReadStream] close]; - [self setPostBodyReadStream:nil]; - - [self setDataDecompressor:nil]; - - NSError *fileError = nil; - - // Delete up the request body temporary file, if it exists - if ([self didCreateTemporaryPostDataFile] && ![self authenticationNeeded]) { - [self removeTemporaryUploadFile]; - [self removeTemporaryCompressedUploadFile]; - } - - // Close the output stream as we're done writing to the file - if ([self temporaryFileDownloadPath]) { - - [[self fileDownloadOutputStream] close]; - [self setFileDownloadOutputStream:nil]; - - [[self inflatedFileDownloadOutputStream] close]; - [self setInflatedFileDownloadOutputStream:nil]; - - // If we are going to redirect and we are resuming, let's ignore this download - if ([self shouldRedirect] && [self needsRedirect] && [self allowResumeForFileDownloads]) { - - } else if ([self isResponseCompressed]) { - - // Decompress the file directly to the destination path - if ([self shouldWaitToInflateCompressedResponses]) { - [ASIDataDecompressor uncompressDataFromFile:[self temporaryFileDownloadPath] toFile:[self downloadDestinationPath] error:&fileError]; - - // Response should already have been inflated, move the temporary file to the destination path - } else { - NSError *moveError = nil; - [[[[NSFileManager alloc] init] autorelease] moveItemAtPath:[self temporaryUncompressedDataDownloadPath] toPath:[self downloadDestinationPath] error:&moveError]; - if (moveError) { - fileError = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASIFileManagementError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Failed to move file from '%@' to '%@'",[self temporaryFileDownloadPath],[self downloadDestinationPath]],NSLocalizedDescriptionKey,moveError,NSUnderlyingErrorKey,nil]]; - } - [self setTemporaryUncompressedDataDownloadPath:nil]; - - } - [self removeTemporaryDownloadFile]; - - } else { - - //Remove any file at the destination path - NSError *moveError = nil; - if (![[self class] removeFileAtPath:[self downloadDestinationPath] error:&moveError]) { - fileError = moveError; - - } - - //Move the temporary file to the destination path - if (!fileError) { - [[[[NSFileManager alloc] init] autorelease] moveItemAtPath:[self temporaryFileDownloadPath] toPath:[self downloadDestinationPath] error:&moveError]; - if (moveError) { - fileError = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASIFileManagementError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Failed to move file from '%@' to '%@'",[self temporaryFileDownloadPath],[self downloadDestinationPath]],NSLocalizedDescriptionKey,moveError,NSUnderlyingErrorKey,nil]]; - } - [self setTemporaryFileDownloadPath:nil]; - } - - } - } - - // Save to the cache - if ([self downloadCache] && ![self didUseCachedResponse]) { - [[self downloadCache] storeResponseForRequest:self maxAge:[self secondsToCache]]; - } - - [progressLock unlock]; - - - [connectionsLock lock]; - if (![self connectionCanBeReused]) { - [self unscheduleReadStream]; - } - #if DEBUG_PERSISTENT_CONNECTIONS - if ([self requestID]) { - ASI_DEBUG_LOG(@"[CONNECTION] Request #%@ finished using connection #%@",[self requestID], [[self connectionInfo] objectForKey:@"id"]); - } - #endif - [[self connectionInfo] removeObjectForKey:@"request"]; - [[self connectionInfo] setObject:[NSDate dateWithTimeIntervalSinceNow:[self persistentConnectionTimeoutSeconds]] forKey:@"expires"]; - [connectionsLock unlock]; - - if (![self authenticationNeeded]) { - [self destroyReadStream]; - } - - - if (![self needsRedirect] && ![self authenticationNeeded] && ![self didUseCachedResponse]) { - - if (fileError) { - [self failWithError:fileError]; - } else { - [self requestFinished]; - } - - [self markAsFinished]; - - // If request has asked delegate or ASIAuthenticationDialog for credentials - } else if ([self authenticationNeeded]) { - CFRunLoopStop(CFRunLoopGetCurrent()); - } - -} - -- (void)markAsFinished -{ - // Autoreleased requests may well be dealloced here otherwise - CFRetain(self); - - // dealloc won't be called when running with GC, so we'll clean these up now - if (request) { - CFRelease(request); - request = nil; - } - if (requestAuthentication) { - CFRelease(requestAuthentication); - requestAuthentication = nil; - } - if (proxyAuthentication) { - CFRelease(proxyAuthentication); - proxyAuthentication = nil; - } - - BOOL wasInProgress = inProgress; - BOOL wasFinished = finished; - - if (!wasFinished) - [self willChangeValueForKey:@"isFinished"]; - if (wasInProgress) - [self willChangeValueForKey:@"isExecuting"]; - - [self setInProgress:NO]; - finished = YES; - - if (wasInProgress) - [self didChangeValueForKey:@"isExecuting"]; - if (!wasFinished) - [self didChangeValueForKey:@"isFinished"]; - - CFRunLoopStop(CFRunLoopGetCurrent()); - - #if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 - if ([ASIHTTPRequest isMultitaskingSupported] && [self shouldContinueWhenAppEntersBackground]) { - dispatch_async(dispatch_get_main_queue(), ^{ - if (backgroundTask != UIBackgroundTaskInvalid) { - [[UIApplication sharedApplication] endBackgroundTask:backgroundTask]; - backgroundTask = UIBackgroundTaskInvalid; - } - }); - } - #endif - CFRelease(self); -} - -- (void)useDataFromCache -{ - NSDictionary *headers = [[self downloadCache] cachedResponseHeadersForURL:[self url]]; - NSString *dataPath = [[self downloadCache] pathToCachedResponseDataForURL:[self url]]; - - ASIHTTPRequest *theRequest = self; - if ([self mainRequest]) { - theRequest = [self mainRequest]; - } - - if (headers && dataPath) { - - [self setResponseStatusCode:[[headers objectForKey:@"X-ASIHTTPRequest-Response-Status-Code"] intValue]]; - [self setDidUseCachedResponse:YES]; - [theRequest setResponseHeaders:headers]; - - if ([theRequest downloadDestinationPath]) { - [theRequest setDownloadDestinationPath:dataPath]; - } else { - [theRequest setRawResponseData:[NSMutableData dataWithData:[[self downloadCache] cachedResponseDataForURL:[self url]]]]; - } - [theRequest setContentLength:[[[self responseHeaders] objectForKey:@"Content-Length"] longLongValue]]; - [theRequest setTotalBytesRead:[self contentLength]]; - - [theRequest parseStringEncodingFromHeaders]; - - [theRequest setResponseCookies:[NSHTTPCookie cookiesWithResponseHeaderFields:headers forURL:[self url]]]; - - // See if we need to redirect - if ([self willRedirect]) { - if (![self willAskDelegateToConfirmRedirect]) { - [self performRedirect]; - } - return; - } - } - - [theRequest setComplete:YES]; - [theRequest setDownloadComplete:YES]; - - // If we're pulling data from the cache without contacting the server at all, we won't have set originalURL yet - if ([self redirectCount] == 0) { - [theRequest setOriginalURL:[theRequest url]]; - } - - [theRequest updateProgressIndicators]; - [theRequest requestFinished]; - [theRequest markAsFinished]; - if ([self mainRequest]) { - [self markAsFinished]; - } -} - -- (BOOL)retryUsingNewConnection -{ - if ([self retryCount] == 0) { - - [self setWillRetryRequest:YES]; - [self cancelLoad]; - [self setWillRetryRequest:NO]; - - #if DEBUG_PERSISTENT_CONNECTIONS - ASI_DEBUG_LOG(@"[CONNECTION] Request attempted to use connection #%@, but it has been closed - will retry with a new connection", [[self connectionInfo] objectForKey:@"id"]); - #endif - [connectionsLock lock]; - [[self connectionInfo] removeObjectForKey:@"request"]; - [persistentConnectionsPool removeObject:[self connectionInfo]]; - [self setConnectionInfo:nil]; - [connectionsLock unlock]; - [self setRetryCount:[self retryCount]+1]; - [self startRequest]; - return YES; - } - #if DEBUG_PERSISTENT_CONNECTIONS - ASI_DEBUG_LOG(@"[CONNECTION] Request attempted to use connection #%@, but it has been closed - we have already retried with a new connection, so we must give up", [[self connectionInfo] objectForKey:@"id"]); - #endif - return NO; -} - -- (void)handleStreamError - -{ - NSError *underlyingError = [NSMakeCollectable(CFReadStreamCopyError((CFReadStreamRef)[self readStream])) autorelease]; - - if (![self error]) { // We may already have handled this error - - // First, check for a 'socket not connected', 'broken pipe' or 'connection lost' error - // This may occur when we've attempted to reuse a connection that should have been closed - // If we get this, we need to retry the request - // We'll only do this once - if it happens again on retry, we'll give up - // -1005 = kCFURLErrorNetworkConnectionLost - this doesn't seem to be declared on Mac OS 10.5 - if (([[underlyingError domain] isEqualToString:NSPOSIXErrorDomain] && ([underlyingError code] == ENOTCONN || [underlyingError code] == EPIPE)) - || ([[underlyingError domain] isEqualToString:(NSString *)kCFErrorDomainCFNetwork] && [underlyingError code] == -1005)) { - if ([self retryUsingNewConnection]) { - return; - } - } - - NSString *reason = @"A connection failure occurred"; - - // We'll use a custom error message for SSL errors, but you should always check underlying error if you want more details - // For some reason SecureTransport.h doesn't seem to be available on iphone, so error codes hard-coded - // Also, iPhone seems to handle errors differently from Mac OS X - a self-signed certificate returns a different error code on each platform, so we'll just provide a general error - if ([[underlyingError domain] isEqualToString:NSOSStatusErrorDomain]) { - if ([underlyingError code] <= -9800 && [underlyingError code] >= -9818) { - reason = [NSString stringWithFormat:@"%@: SSL problem (Possible causes may include a bad/expired/self-signed certificate, clock set to wrong date)",reason]; - } - } - [self cancelLoad]; - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIConnectionFailureErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:reason,NSLocalizedDescriptionKey,underlyingError,NSUnderlyingErrorKey,nil]]]; - } else { - [self cancelLoad]; - } - [self checkRequestStatus]; -} - -#pragma mark managing the read stream - -- (void)destroyReadStream -{ - if ([self readStream]) { - [self unscheduleReadStream]; - if (![self connectionCanBeReused]) { - [[self readStream] removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:[self runLoopMode]]; - [[self readStream] close]; - } - [self setReadStream:nil]; - } -} - -- (void)scheduleReadStream -{ - if ([self readStream] && ![self readStreamIsScheduled]) { - - [connectionsLock lock]; - runningRequestCount++; - if (shouldUpdateNetworkActivityIndicator) { - [[self class] showNetworkActivityIndicator]; - } - [connectionsLock unlock]; - - // Reset the timeout - [self setLastActivityTime:[NSDate date]]; - CFStreamClientContext ctxt = {0, self, NULL, NULL, NULL}; - CFReadStreamSetClient((CFReadStreamRef)[self readStream], kNetworkEvents, ReadStreamClientCallBack, &ctxt); - [[self readStream] scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:[self runLoopMode]]; - [self setReadStreamIsScheduled:YES]; - } -} - - -- (void)unscheduleReadStream -{ - if ([self readStream] && [self readStreamIsScheduled]) { - - [connectionsLock lock]; - runningRequestCount--; - if (shouldUpdateNetworkActivityIndicator && runningRequestCount == 0) { - // This call will wait half a second before turning off the indicator - // This can prevent flicker when you have a single request finish and then immediately start another request - // We run this on the main thread because we have no guarantee this thread will have a runloop in 0.5 seconds time - // We don't bother the cancel this call if we start a new request, because we'll check if requests are running before we hide it - [[self class] performSelectorOnMainThread:@selector(hideNetworkActivityIndicatorAfterDelay) withObject:nil waitUntilDone:[NSThread isMainThread]]; - } - [connectionsLock unlock]; - - CFReadStreamSetClient((CFReadStreamRef)[self readStream], kCFStreamEventNone, NULL, NULL); - [[self readStream] removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:[self runLoopMode]]; - [self setReadStreamIsScheduled:NO]; - } -} - -#pragma mark cleanup - -- (BOOL)removeTemporaryDownloadFile -{ - NSError *err = nil; - if ([self temporaryFileDownloadPath]) { - if (![[self class] removeFileAtPath:[self temporaryFileDownloadPath] error:&err]) { - [self failWithError:err]; - } - [self setTemporaryFileDownloadPath:nil]; - } - return (!err); -} - -- (BOOL)removeTemporaryUncompressedDownloadFile -{ - NSError *err = nil; - if ([self temporaryUncompressedDataDownloadPath]) { - if (![[self class] removeFileAtPath:[self temporaryUncompressedDataDownloadPath] error:&err]) { - [self failWithError:err]; - } - [self setTemporaryUncompressedDataDownloadPath:nil]; - } - return (!err); -} - -- (BOOL)removeTemporaryUploadFile -{ - NSError *err = nil; - if ([self postBodyFilePath]) { - if (![[self class] removeFileAtPath:[self postBodyFilePath] error:&err]) { - [self failWithError:err]; - } - [self setPostBodyFilePath:nil]; - } - return (!err); -} - -- (BOOL)removeTemporaryCompressedUploadFile -{ - NSError *err = nil; - if ([self compressedPostBodyFilePath]) { - if (![[self class] removeFileAtPath:[self compressedPostBodyFilePath] error:&err]) { - [self failWithError:err]; - } - [self setCompressedPostBodyFilePath:nil]; - } - return (!err); -} - -+ (BOOL)removeFileAtPath:(NSString *)path error:(NSError **)err -{ - NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease]; - - if ([fileManager fileExistsAtPath:path]) { - NSError *removeError = nil; - [fileManager removeItemAtPath:path error:&removeError]; - if (removeError) { - if (err) { - *err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASIFileManagementError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Failed to delete file at path '%@'",path],NSLocalizedDescriptionKey,removeError,NSUnderlyingErrorKey,nil]]; - } - return NO; - } - } - return YES; -} - -#pragma mark Proxies - -- (BOOL)configureProxies -{ - // Have details of the proxy been set on this request - if (![self isPACFileRequest] && (![self proxyHost] && ![self proxyPort])) { - - // If not, we need to figure out what they'll be - NSArray *proxies = nil; - - // Have we been given a proxy auto config file? - if ([self PACurl]) { - - // If yes, we'll need to fetch the PAC file asynchronously, so we stop this request to wait until we have the proxy details. - [self fetchPACFile]; - return NO; - - // Detect proxy settings and apply them - } else { - -#if TARGET_OS_IPHONE - NSDictionary *proxySettings = [NSMakeCollectable(CFNetworkCopySystemProxySettings()) autorelease]; -#else - NSDictionary *proxySettings = [NSMakeCollectable(SCDynamicStoreCopyProxies(NULL)) autorelease]; -#endif - - proxies = [NSMakeCollectable(CFNetworkCopyProxiesForURL((CFURLRef)[self url], (CFDictionaryRef)proxySettings)) autorelease]; - - // Now check to see if the proxy settings contained a PAC url, we need to run the script to get the real list of proxies if so - NSDictionary *settings = [proxies objectAtIndex:0]; - if ([settings objectForKey:(NSString *)kCFProxyAutoConfigurationURLKey]) { - [self setPACurl:[settings objectForKey:(NSString *)kCFProxyAutoConfigurationURLKey]]; - [self fetchPACFile]; - return NO; - } - } - - if (!proxies) { - [self setReadStream:nil]; - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileBuildingRequestType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to obtain information on proxy servers needed for request",NSLocalizedDescriptionKey,nil]]]; - return NO; - } - // I don't really understand why the dictionary returned by CFNetworkCopyProxiesForURL uses different key names from CFNetworkCopySystemProxySettings/SCDynamicStoreCopyProxies - // and why its key names are documented while those we actually need to use don't seem to be (passing the kCF* keys doesn't seem to work) - if ([proxies count] > 0) { - NSDictionary *settings = [proxies objectAtIndex:0]; - [self setProxyHost:[settings objectForKey:(NSString *)kCFProxyHostNameKey]]; - [self setProxyPort:[[settings objectForKey:(NSString *)kCFProxyPortNumberKey] intValue]]; - [self setProxyType:[settings objectForKey:(NSString *)kCFProxyTypeKey]]; - } - } - return YES; -} - - - -// Attempts to download a PAC (Proxy Auto-Configuration) file -// PAC files at file://, http:// and https:// addresses are supported -- (void)fetchPACFile -{ - // For file:// urls, we'll use an async NSInputStream (ASIHTTPRequest does not support file:// urls) - if ([[self PACurl] isFileURL]) { - NSInputStream *stream = [[[NSInputStream alloc] initWithFileAtPath:[[self PACurl] path]] autorelease]; - [self setPACFileReadStream:stream]; - [stream setDelegate:(id)self]; - [stream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:[self runLoopMode]]; - [stream open]; - // If it takes more than timeOutSeconds to read the PAC, we'll just give up and assume no proxies - // We won't bother to handle cases where the first part of the PAC is read within timeOutSeconds, but the whole thing takes longer - // Either our PAC file is in easy reach, or it's going to slow things down to the point that it's probably better requests fail - [self performSelector:@selector(timeOutPACRead) withObject:nil afterDelay:[self timeOutSeconds]]; - return; - } - - NSString *scheme = [[[self PACurl] scheme] lowercaseString]; - if (![scheme isEqualToString:@"http"] && ![scheme isEqualToString:@"https"]) { - // Don't know how to read data from this URL, we'll have to give up - // We'll simply assume no proxies, and start the request as normal - [self startRequest]; - return; - } - - // Create an ASIHTTPRequest to fetch the PAC file - ASIHTTPRequest *PACRequest = [ASIHTTPRequest requestWithURL:[self PACurl]]; - - // Will prevent this request attempting to configure proxy settings for itself - [PACRequest setIsPACFileRequest:YES]; - - [PACRequest setTimeOutSeconds:[self timeOutSeconds]]; - - // If we're a synchronous request, we'll download the PAC file synchronously - if ([self isSynchronous]) { - [PACRequest startSynchronous]; - if (![PACRequest error] && [PACRequest responseString]) { - [self runPACScript:[PACRequest responseString]]; - } - [self startRequest]; - return; - } - - [self setPACFileRequest:PACRequest]; - - // Force this request to run before others in the shared queue - [PACRequest setQueuePriority:NSOperationQueuePriorityHigh]; - - // We'll treat failure to download the PAC file the same as success - if we were unable to fetch a PAC file, we proceed as if we have no proxy server and let this request fail itself if necessary - [PACRequest setDelegate:self]; - [PACRequest setDidFinishSelector:@selector(finishedDownloadingPACFile:)]; - [PACRequest setDidFailSelector:@selector(finishedDownloadingPACFile:)]; - [PACRequest startAsynchronous]; - - // Temporarily increase the number of operations in the shared queue to give our request a chance to run - [connectionsLock lock]; - [sharedQueue setMaxConcurrentOperationCount:[sharedQueue maxConcurrentOperationCount]+1]; - [connectionsLock unlock]; -} - -// Called as we read the PAC file from a file:// url -- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode -{ - if (![self PACFileReadStream]) { - return; - } - if (eventCode == NSStreamEventHasBytesAvailable) { - - if (![self PACFileData]) { - [self setPACFileData:[NSMutableData data]]; - } - // If your PAC file is larger than 16KB, you're just being cruel. - uint8_t buf[16384]; - NSInteger len = [(NSInputStream *)stream read:buf maxLength:16384]; - if (len) { - [[self PACFileData] appendBytes:(const void *)buf length:len]; - } - - } else if (eventCode == NSStreamEventErrorOccurred || eventCode == NSStreamEventEndEncountered) { - - [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(timeOutPACRead) object:nil]; - - [stream close]; - [stream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:[self runLoopMode]]; - [self setPACFileReadStream:nil]; - - if (eventCode == NSStreamEventEndEncountered) { - // It sounds as though we have no idea what encoding a PAC file will use - static NSStringEncoding encodingsToTry[2] = {NSUTF8StringEncoding,NSISOLatin1StringEncoding}; - NSUInteger i; - for (i=0; i<2; i++) { - NSString *pacScript = [[[NSString alloc] initWithBytes:[[self PACFileData] bytes] length:[[self PACFileData] length] encoding:encodingsToTry[i]] autorelease]; - if (pacScript) { - [self runPACScript:pacScript]; - break; - } - } - } - [self setPACFileData:nil]; - [self startRequest]; - } -} - -// Called if it takes longer than timeOutSeconds to read the whole PAC file (when reading from a file:// url) -- (void)timeOutPACRead -{ - [self stream:[self PACFileReadStream] handleEvent:NSStreamEventErrorOccurred]; -} - -// Runs the downloaded PAC script -- (void)runPACScript:(NSString *)script -{ - if (script) { - // From: http://developer.apple.com/samplecode/CFProxySupportTool/listing1.html - // Work around . This dummy call to - // CFNetworkCopyProxiesForURL initialise some state within CFNetwork - // that is required by CFNetworkCopyProxiesForAutoConfigurationScript. - CFRelease(CFNetworkCopyProxiesForURL((CFURLRef)[self url], NULL)); - - // Obtain the list of proxies by running the autoconfiguration script - CFErrorRef err = NULL; - NSArray *proxies = [NSMakeCollectable(CFNetworkCopyProxiesForAutoConfigurationScript((CFStringRef)script,(CFURLRef)[self url], &err)) autorelease]; - if (!err && [proxies count] > 0) { - NSDictionary *settings = [proxies objectAtIndex:0]; - [self setProxyHost:[settings objectForKey:(NSString *)kCFProxyHostNameKey]]; - [self setProxyPort:[[settings objectForKey:(NSString *)kCFProxyPortNumberKey] intValue]]; - [self setProxyType:[settings objectForKey:(NSString *)kCFProxyTypeKey]]; - } - } -} - -// Called if we successfully downloaded a PAC file from a webserver -- (void)finishedDownloadingPACFile:(ASIHTTPRequest *)theRequest -{ - if (![theRequest error] && [theRequest responseString]) { - [self runPACScript:[theRequest responseString]]; - } - - // Set the shared queue's maxConcurrentOperationCount back to normal - [connectionsLock lock]; - [sharedQueue setMaxConcurrentOperationCount:[sharedQueue maxConcurrentOperationCount]-1]; - [connectionsLock unlock]; - - // We no longer need our PAC file request - [self setPACFileRequest:nil]; - - // Start the request - [self startRequest]; -} - - -#pragma mark persistent connections - -- (NSNumber *)connectionID -{ - return [[self connectionInfo] objectForKey:@"id"]; -} - -+ (void)expirePersistentConnections -{ - [connectionsLock lock]; - NSUInteger i; - for (i=0; i<[persistentConnectionsPool count]; i++) { - NSDictionary *existingConnection = [persistentConnectionsPool objectAtIndex:i]; - if (![existingConnection objectForKey:@"request"] && [[existingConnection objectForKey:@"expires"] timeIntervalSinceNow] <= 0) { -#if DEBUG_PERSISTENT_CONNECTIONS - ASI_DEBUG_LOG(@"[CONNECTION] Closing connection #%i because it has expired",[[existingConnection objectForKey:@"id"] intValue]); -#endif - NSInputStream *stream = [existingConnection objectForKey:@"stream"]; - if (stream) { - [stream close]; - } - [persistentConnectionsPool removeObject:existingConnection]; - i--; - } - } - [connectionsLock unlock]; -} - -#pragma mark NSCopying -- (id)copyWithZone:(NSZone *)zone -{ - // Don't forget - this will return a retained copy! - ASIHTTPRequest *newRequest = [[[self class] alloc] initWithURL:[self url]]; - [newRequest setDelegate:[self delegate]]; - [newRequest setRequestMethod:[self requestMethod]]; - [newRequest setPostBody:[self postBody]]; - [newRequest setShouldStreamPostDataFromDisk:[self shouldStreamPostDataFromDisk]]; - [newRequest setPostBodyFilePath:[self postBodyFilePath]]; - [newRequest setRequestHeaders:[[[self requestHeaders] mutableCopyWithZone:zone] autorelease]]; - [newRequest setRequestCookies:[[[self requestCookies] mutableCopyWithZone:zone] autorelease]]; - [newRequest setUseCookiePersistence:[self useCookiePersistence]]; - [newRequest setUseKeychainPersistence:[self useKeychainPersistence]]; - [newRequest setUseSessionPersistence:[self useSessionPersistence]]; - [newRequest setAllowCompressedResponse:[self allowCompressedResponse]]; - [newRequest setDownloadDestinationPath:[self downloadDestinationPath]]; - [newRequest setTemporaryFileDownloadPath:[self temporaryFileDownloadPath]]; - [newRequest setUsername:[self username]]; - [newRequest setPassword:[self password]]; - [newRequest setDomain:[self domain]]; - [newRequest setProxyUsername:[self proxyUsername]]; - [newRequest setProxyPassword:[self proxyPassword]]; - [newRequest setProxyDomain:[self proxyDomain]]; - [newRequest setProxyHost:[self proxyHost]]; - [newRequest setProxyPort:[self proxyPort]]; - [newRequest setProxyType:[self proxyType]]; - [newRequest setUploadProgressDelegate:[self uploadProgressDelegate]]; - [newRequest setDownloadProgressDelegate:[self downloadProgressDelegate]]; - [newRequest setShouldPresentAuthenticationDialog:[self shouldPresentAuthenticationDialog]]; - [newRequest setShouldPresentProxyAuthenticationDialog:[self shouldPresentProxyAuthenticationDialog]]; - [newRequest setPostLength:[self postLength]]; - [newRequest setHaveBuiltPostBody:[self haveBuiltPostBody]]; - [newRequest setDidStartSelector:[self didStartSelector]]; - [newRequest setDidFinishSelector:[self didFinishSelector]]; - [newRequest setDidFailSelector:[self didFailSelector]]; - [newRequest setTimeOutSeconds:[self timeOutSeconds]]; - [newRequest setShouldResetDownloadProgress:[self shouldResetDownloadProgress]]; - [newRequest setShouldResetUploadProgress:[self shouldResetUploadProgress]]; - [newRequest setShowAccurateProgress:[self showAccurateProgress]]; - [newRequest setDefaultResponseEncoding:[self defaultResponseEncoding]]; - [newRequest setAllowResumeForFileDownloads:[self allowResumeForFileDownloads]]; - [newRequest setUserInfo:[[[self userInfo] copyWithZone:zone] autorelease]]; - [newRequest setTag:[self tag]]; - [newRequest setUseHTTPVersionOne:[self useHTTPVersionOne]]; - [newRequest setShouldRedirect:[self shouldRedirect]]; - [newRequest setValidatesSecureCertificate:[self validatesSecureCertificate]]; - [newRequest setClientCertificateIdentity:clientCertificateIdentity]; - [newRequest setClientCertificates:[[clientCertificates copy] autorelease]]; - [newRequest setPACurl:[self PACurl]]; - [newRequest setShouldPresentCredentialsBeforeChallenge:[self shouldPresentCredentialsBeforeChallenge]]; - [newRequest setNumberOfTimesToRetryOnTimeout:[self numberOfTimesToRetryOnTimeout]]; - [newRequest setShouldUseRFC2616RedirectBehaviour:[self shouldUseRFC2616RedirectBehaviour]]; - [newRequest setShouldAttemptPersistentConnection:[self shouldAttemptPersistentConnection]]; - [newRequest setPersistentConnectionTimeoutSeconds:[self persistentConnectionTimeoutSeconds]]; - [newRequest setAuthenticationScheme:[self authenticationScheme]]; - return newRequest; -} - -#pragma mark default time out - -+ (NSTimeInterval)defaultTimeOutSeconds -{ - return defaultTimeOutSeconds; -} - -+ (void)setDefaultTimeOutSeconds:(NSTimeInterval)newTimeOutSeconds -{ - defaultTimeOutSeconds = newTimeOutSeconds; -} - - -#pragma mark client certificate - -- (void)setClientCertificateIdentity:(SecIdentityRef)anIdentity { - if(clientCertificateIdentity) { - CFRelease(clientCertificateIdentity); - } - - clientCertificateIdentity = anIdentity; - - if (clientCertificateIdentity) { - CFRetain(clientCertificateIdentity); - } -} - - -#pragma mark session credentials - -+ (NSMutableArray *)sessionProxyCredentialsStore -{ - [sessionCredentialsLock lock]; - if (!sessionProxyCredentialsStore) { - sessionProxyCredentialsStore = [[NSMutableArray alloc] init]; - } - [sessionCredentialsLock unlock]; - return sessionProxyCredentialsStore; -} - -+ (NSMutableArray *)sessionCredentialsStore -{ - [sessionCredentialsLock lock]; - if (!sessionCredentialsStore) { - sessionCredentialsStore = [[NSMutableArray alloc] init]; - } - [sessionCredentialsLock unlock]; - return sessionCredentialsStore; -} - -+ (void)storeProxyAuthenticationCredentialsInSessionStore:(NSDictionary *)credentials -{ - [sessionCredentialsLock lock]; - [self removeProxyAuthenticationCredentialsFromSessionStore:[credentials objectForKey:@"Credentials"]]; - [[[self class] sessionProxyCredentialsStore] addObject:credentials]; - [sessionCredentialsLock unlock]; -} - -+ (void)storeAuthenticationCredentialsInSessionStore:(NSDictionary *)credentials -{ - [sessionCredentialsLock lock]; - [self removeAuthenticationCredentialsFromSessionStore:[credentials objectForKey:@"Credentials"]]; - [[[self class] sessionCredentialsStore] addObject:credentials]; - [sessionCredentialsLock unlock]; -} - -+ (void)removeProxyAuthenticationCredentialsFromSessionStore:(NSDictionary *)credentials -{ - [sessionCredentialsLock lock]; - NSMutableArray *sessionCredentialsList = [[self class] sessionProxyCredentialsStore]; - NSUInteger i; - for (i=0; i<[sessionCredentialsList count]; i++) { - NSDictionary *theCredentials = [sessionCredentialsList objectAtIndex:i]; - if ([theCredentials objectForKey:@"Credentials"] == credentials) { - [sessionCredentialsList removeObjectAtIndex:i]; - [sessionCredentialsLock unlock]; - return; - } - } - [sessionCredentialsLock unlock]; -} - -+ (void)removeAuthenticationCredentialsFromSessionStore:(NSDictionary *)credentials -{ - [sessionCredentialsLock lock]; - NSMutableArray *sessionCredentialsList = [[self class] sessionCredentialsStore]; - NSUInteger i; - for (i=0; i<[sessionCredentialsList count]; i++) { - NSDictionary *theCredentials = [sessionCredentialsList objectAtIndex:i]; - if ([theCredentials objectForKey:@"Credentials"] == credentials) { - [sessionCredentialsList removeObjectAtIndex:i]; - [sessionCredentialsLock unlock]; - return; - } - } - [sessionCredentialsLock unlock]; -} - -- (NSDictionary *)findSessionProxyAuthenticationCredentials -{ - [sessionCredentialsLock lock]; - NSMutableArray *sessionCredentialsList = [[self class] sessionProxyCredentialsStore]; - for (NSDictionary *theCredentials in sessionCredentialsList) { - if ([[theCredentials objectForKey:@"Host"] isEqualToString:[self proxyHost]] && [[theCredentials objectForKey:@"Port"] intValue] == [self proxyPort]) { - [sessionCredentialsLock unlock]; - return theCredentials; - } - } - [sessionCredentialsLock unlock]; - return nil; -} - - -- (NSDictionary *)findSessionAuthenticationCredentials -{ - [sessionCredentialsLock lock]; - NSMutableArray *sessionCredentialsList = [[self class] sessionCredentialsStore]; - NSURL *requestURL = [self url]; - - BOOL haveFoundExactMatch; - NSDictionary *closeMatch = nil; - - // Loop through all the cached credentials we have, looking for the best match for this request - for (NSDictionary *theCredentials in sessionCredentialsList) { - - haveFoundExactMatch = NO; - NSURL *cachedCredentialsURL = [theCredentials objectForKey:@"URL"]; - - // Find an exact match (same url) - if ([cachedCredentialsURL isEqual:[self url]]) { - haveFoundExactMatch = YES; - - // This is not an exact match for the url, and we already have a close match we can use - } else if (closeMatch) { - continue; - - // Find a close match (same host, scheme and port) - } else if ([[cachedCredentialsURL host] isEqualToString:[requestURL host]] && ([cachedCredentialsURL port] == [requestURL port] || ([requestURL port] && [[cachedCredentialsURL port] isEqualToNumber:[requestURL port]])) && [[cachedCredentialsURL scheme] isEqualToString:[requestURL scheme]]) { - } else { - continue; - } - - // Just a sanity check to ensure we never choose credentials from a different realm. Can't really do more than that, as either this request or the stored credentials may not have a realm when the other does - if ([self authenticationRealm] && ([theCredentials objectForKey:@"AuthenticationRealm"] && ![[theCredentials objectForKey:@"AuthenticationRealm"] isEqualToString:[self authenticationRealm]])) { - continue; - } - - // If we have a username and password set on the request, check that they are the same as the cached ones - if ([self username] && [self password]) { - NSDictionary *usernameAndPassword = [theCredentials objectForKey:@"Credentials"]; - NSString *storedUsername = [usernameAndPassword objectForKey:(NSString *)kCFHTTPAuthenticationUsername]; - NSString *storedPassword = [usernameAndPassword objectForKey:(NSString *)kCFHTTPAuthenticationPassword]; - if (![storedUsername isEqualToString:[self username]] || ![storedPassword isEqualToString:[self password]]) { - continue; - } - } - - // If we have an exact match for the url, use those credentials - if (haveFoundExactMatch) { - [sessionCredentialsLock unlock]; - return theCredentials; - } - - // We have no exact match, let's remember that we have a good match for this server, and we'll use it at the end if we don't find an exact match - closeMatch = theCredentials; - } - [sessionCredentialsLock unlock]; - - // Return credentials that matched on host, port and scheme, or nil if we didn't find any - return closeMatch; -} - -#pragma mark keychain storage - -+ (void)saveCredentials:(NSURLCredential *)credentials forHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm -{ - NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithHost:host port:port protocol:protocol realm:realm authenticationMethod:NSURLAuthenticationMethodDefault] autorelease]; - [[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credentials forProtectionSpace:protectionSpace]; -} - -+ (void)saveCredentials:(NSURLCredential *)credentials forProxy:(NSString *)host port:(int)port realm:(NSString *)realm -{ - NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithProxyHost:host port:port type:NSURLProtectionSpaceHTTPProxy realm:realm authenticationMethod:NSURLAuthenticationMethodDefault] autorelease]; - [[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credentials forProtectionSpace:protectionSpace]; -} - -+ (NSURLCredential *)savedCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm -{ - NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithHost:host port:port protocol:protocol realm:realm authenticationMethod:NSURLAuthenticationMethodDefault] autorelease]; - return [[NSURLCredentialStorage sharedCredentialStorage] defaultCredentialForProtectionSpace:protectionSpace]; -} - -+ (NSURLCredential *)savedCredentialsForProxy:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm -{ - NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithProxyHost:host port:port type:NSURLProtectionSpaceHTTPProxy realm:realm authenticationMethod:NSURLAuthenticationMethodDefault] autorelease]; - return [[NSURLCredentialStorage sharedCredentialStorage] defaultCredentialForProtectionSpace:protectionSpace]; -} - -+ (void)removeCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm -{ - NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithHost:host port:port protocol:protocol realm:realm authenticationMethod:NSURLAuthenticationMethodDefault] autorelease]; - NSURLCredential *credential = [[NSURLCredentialStorage sharedCredentialStorage] defaultCredentialForProtectionSpace:protectionSpace]; - if (credential) { - [[NSURLCredentialStorage sharedCredentialStorage] removeCredential:credential forProtectionSpace:protectionSpace]; - } -} - -+ (void)removeCredentialsForProxy:(NSString *)host port:(int)port realm:(NSString *)realm -{ - NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithProxyHost:host port:port type:NSURLProtectionSpaceHTTPProxy realm:realm authenticationMethod:NSURLAuthenticationMethodDefault] autorelease]; - NSURLCredential *credential = [[NSURLCredentialStorage sharedCredentialStorage] defaultCredentialForProtectionSpace:protectionSpace]; - if (credential) { - [[NSURLCredentialStorage sharedCredentialStorage] removeCredential:credential forProtectionSpace:protectionSpace]; - } -} - -+ (NSMutableArray *)sessionCookies -{ - [sessionCookiesLock lock]; - if (!sessionCookies) { - [ASIHTTPRequest setSessionCookies:[NSMutableArray array]]; - } - NSMutableArray *cookies = [[sessionCookies retain] autorelease]; - [sessionCookiesLock unlock]; - return cookies; -} - -+ (void)setSessionCookies:(NSMutableArray *)newSessionCookies -{ - [sessionCookiesLock lock]; - // Remove existing cookies from the persistent store - for (NSHTTPCookie *cookie in sessionCookies) { - [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie]; - } - [sessionCookies release]; - sessionCookies = [newSessionCookies retain]; - [sessionCookiesLock unlock]; -} - -+ (void)addSessionCookie:(NSHTTPCookie *)newCookie -{ - [sessionCookiesLock lock]; - NSHTTPCookie *cookie; - NSUInteger i; - NSUInteger max = [[ASIHTTPRequest sessionCookies] count]; - for (i=0; i 0) { - if ([self readStreamIsScheduled]) { - [self unscheduleReadStream]; - #if DEBUG_THROTTLING - ASI_DEBUG_LOG(@"[THROTTLING] Sleeping request %@ until after %@",self,throttleWakeUpTime); - #endif - } - } else { - if (![self readStreamIsScheduled]) { - [self scheduleReadStream]; - #if DEBUG_THROTTLING - ASI_DEBUG_LOG(@"[THROTTLING] Waking up request %@",self); - #endif - } - } - } - [bandwidthThrottlingLock unlock]; - - // Bandwidth throttling must have been turned off since we last looked, let's re-schedule the stream - } else if (![self readStreamIsScheduled]) { - [self scheduleReadStream]; - } -} - -+ (BOOL)isBandwidthThrottled -{ -#if TARGET_OS_IPHONE - [bandwidthThrottlingLock lock]; - - BOOL throttle = isBandwidthThrottled || (!shouldThrottleBandwidthForWWANOnly && (maxBandwidthPerSecond > 0)); - [bandwidthThrottlingLock unlock]; - return throttle; -#else - [bandwidthThrottlingLock lock]; - BOOL throttle = (maxBandwidthPerSecond > 0); - [bandwidthThrottlingLock unlock]; - return throttle; -#endif -} - -+ (unsigned long)maxBandwidthPerSecond -{ - [bandwidthThrottlingLock lock]; - unsigned long amount = maxBandwidthPerSecond; - [bandwidthThrottlingLock unlock]; - return amount; -} - -+ (void)setMaxBandwidthPerSecond:(unsigned long)bytes -{ - [bandwidthThrottlingLock lock]; - maxBandwidthPerSecond = bytes; - [bandwidthThrottlingLock unlock]; -} - -+ (void)incrementBandwidthUsedInLastSecond:(unsigned long)bytes -{ - [bandwidthThrottlingLock lock]; - bandwidthUsedInLastSecond += bytes; - [bandwidthThrottlingLock unlock]; -} - -+ (void)recordBandwidthUsage -{ - if (bandwidthUsedInLastSecond == 0) { - [bandwidthUsageTracker removeAllObjects]; - } else { - NSTimeInterval interval = [bandwidthMeasurementDate timeIntervalSinceNow]; - while ((interval < 0 || [bandwidthUsageTracker count] > 5) && [bandwidthUsageTracker count] > 0) { - [bandwidthUsageTracker removeObjectAtIndex:0]; - interval++; - } - } - #if DEBUG_THROTTLING - ASI_DEBUG_LOG(@"[THROTTLING] ===Used: %u bytes of bandwidth in last measurement period===",bandwidthUsedInLastSecond); - #endif - [bandwidthUsageTracker addObject:[NSNumber numberWithUnsignedLong:bandwidthUsedInLastSecond]]; - [bandwidthMeasurementDate release]; - bandwidthMeasurementDate = [[NSDate dateWithTimeIntervalSinceNow:1] retain]; - bandwidthUsedInLastSecond = 0; - - NSUInteger measurements = [bandwidthUsageTracker count]; - unsigned long totalBytes = 0; - for (NSNumber *bytes in bandwidthUsageTracker) { - totalBytes += [bytes unsignedLongValue]; - } - averageBandwidthUsedPerSecond = totalBytes/measurements; -} - -+ (unsigned long)averageBandwidthUsedPerSecond -{ - [bandwidthThrottlingLock lock]; - unsigned long amount = averageBandwidthUsedPerSecond; - [bandwidthThrottlingLock unlock]; - return amount; -} - -+ (void)measureBandwidthUsage -{ - // Other requests may have to wait for this lock if we're sleeping, but this is fine, since in that case we already know they shouldn't be sending or receiving data - [bandwidthThrottlingLock lock]; - - if (!bandwidthMeasurementDate || [bandwidthMeasurementDate timeIntervalSinceNow] < -0) { - [ASIHTTPRequest recordBandwidthUsage]; - } - - // Are we performing bandwidth throttling? - if ( - #if TARGET_OS_IPHONE - isBandwidthThrottled || (!shouldThrottleBandwidthForWWANOnly && (maxBandwidthPerSecond)) - #else - maxBandwidthPerSecond - #endif - ) { - // How much data can we still send or receive this second? - long long bytesRemaining = (long long)maxBandwidthPerSecond - (long long)bandwidthUsedInLastSecond; - - // Have we used up our allowance? - if (bytesRemaining < 0) { - - // Yes, put this request to sleep until a second is up, with extra added punishment sleeping time for being very naughty (we have used more bandwidth than we were allowed) - double extraSleepyTime = (-bytesRemaining/(maxBandwidthPerSecond*1.0)); - [throttleWakeUpTime release]; - throttleWakeUpTime = [[NSDate alloc] initWithTimeInterval:extraSleepyTime sinceDate:bandwidthMeasurementDate]; - } - } - [bandwidthThrottlingLock unlock]; -} - -+ (unsigned long)maxUploadReadLength -{ - [bandwidthThrottlingLock lock]; - - // We'll split our bandwidth allowance into 4 (which is the default for an ASINetworkQueue's max concurrent operations count) to give all running requests a fighting chance of reading data this cycle - long long toRead = maxBandwidthPerSecond/4; - if (maxBandwidthPerSecond > 0 && (bandwidthUsedInLastSecond + toRead > maxBandwidthPerSecond)) { - toRead = (long long)maxBandwidthPerSecond-(long long)bandwidthUsedInLastSecond; - if (toRead < 0) { - toRead = 0; - } - } - - if (toRead == 0 || !bandwidthMeasurementDate || [bandwidthMeasurementDate timeIntervalSinceNow] < -0) { - [throttleWakeUpTime release]; - throttleWakeUpTime = [bandwidthMeasurementDate retain]; - } - [bandwidthThrottlingLock unlock]; - return (unsigned long)toRead; -} - - -#if TARGET_OS_IPHONE -+ (void)setShouldThrottleBandwidthForWWAN:(BOOL)throttle -{ - if (throttle) { - [ASIHTTPRequest throttleBandwidthForWWANUsingLimit:ASIWWANBandwidthThrottleAmount]; - } else { - [ASIHTTPRequest unsubscribeFromNetworkReachabilityNotifications]; - [ASIHTTPRequest setMaxBandwidthPerSecond:0]; - [bandwidthThrottlingLock lock]; - isBandwidthThrottled = NO; - shouldThrottleBandwidthForWWANOnly = NO; - [bandwidthThrottlingLock unlock]; - } -} - -+ (void)throttleBandwidthForWWANUsingLimit:(unsigned long)limit -{ - [bandwidthThrottlingLock lock]; - shouldThrottleBandwidthForWWANOnly = YES; - maxBandwidthPerSecond = limit; - [ASIHTTPRequest registerForNetworkReachabilityNotifications]; - [bandwidthThrottlingLock unlock]; - [ASIHTTPRequest reachabilityChanged:nil]; -} - -#pragma mark reachability - -+ (void)registerForNetworkReachabilityNotifications -{ - [[Reachability reachabilityForInternetConnection] startNotifier]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:) name:kReachabilityChangedNotification object:nil]; -} - - -+ (void)unsubscribeFromNetworkReachabilityNotifications -{ - [[NSNotificationCenter defaultCenter] removeObserver:self name:kReachabilityChangedNotification object:nil]; -} - -+ (BOOL)isNetworkReachableViaWWAN -{ - return ([[Reachability reachabilityForInternetConnection] currentReachabilityStatus] == ReachableViaWWAN); -} - -+ (void)reachabilityChanged:(NSNotification *)note -{ - [bandwidthThrottlingLock lock]; - isBandwidthThrottled = [ASIHTTPRequest isNetworkReachableViaWWAN]; - [bandwidthThrottlingLock unlock]; -} -#endif - -#pragma mark queue - -// Returns the shared queue -+ (NSOperationQueue *)sharedQueue -{ - return [[sharedQueue retain] autorelease]; -} - -#pragma mark cache - -+ (void)setDefaultCache:(id )cache -{ - @synchronized (self) { - [cache retain]; - [defaultCache release]; - defaultCache = cache; - } -} - -+ (id )defaultCache -{ - @synchronized(self) { - return [[defaultCache retain] autorelease]; - } - return nil; -} - - -#pragma mark network activity - -+ (BOOL)isNetworkInUse -{ - [connectionsLock lock]; - BOOL inUse = (runningRequestCount > 0); - [connectionsLock unlock]; - return inUse; -} - -+ (void)setShouldUpdateNetworkActivityIndicator:(BOOL)shouldUpdate -{ - [connectionsLock lock]; - shouldUpdateNetworkActivityIndicator = shouldUpdate; - [connectionsLock unlock]; -} - -+ (void)showNetworkActivityIndicator -{ -#if TARGET_OS_IPHONE - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; -#endif -} - -+ (void)hideNetworkActivityIndicator -{ -#if TARGET_OS_IPHONE - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; -#endif -} - - -/* Always called on main thread */ -+ (void)hideNetworkActivityIndicatorAfterDelay -{ - [self performSelector:@selector(hideNetworkActivityIndicatorIfNeeeded) withObject:nil afterDelay:0.5]; -} - -+ (void)hideNetworkActivityIndicatorIfNeeeded -{ - [connectionsLock lock]; - if (runningRequestCount == 0) { - [self hideNetworkActivityIndicator]; - } - [connectionsLock unlock]; -} - - -#pragma mark threading behaviour - -// In the default implementation, all requests run in a single background thread -// Advanced users only: Override this method in a subclass for a different threading behaviour -// Eg: return [NSThread mainThread] to run all requests in the main thread -// Alternatively, you can create a thread on demand, or manage a pool of threads -// Threads returned by this method will need to run the runloop in default mode (eg CFRunLoopRun()) -// Requests will stop the runloop when they complete -// If you have multiple requests sharing the thread or you want to re-use the thread, you'll need to restart the runloop -+ (NSThread *)threadForRequest:(ASIHTTPRequest *)request -{ - if (networkThread == nil) { - @synchronized(self) { - if (networkThread == nil) { - networkThread = [[NSThread alloc] initWithTarget:self selector:@selector(runRequests) object:nil]; - [networkThread start]; - } - } - } - return networkThread; -} - -+ (void)runRequests -{ - // Should keep the runloop from exiting - CFRunLoopSourceContext context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; - CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); - CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); - - BOOL runAlways = YES; // Introduced to cheat Static Analyzer - while (runAlways) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - CFRunLoopRun(); - [pool drain]; - } - - // Should never be called, but anyway - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); - CFRelease(source); -} - -#pragma mark miscellany - -#if TARGET_OS_IPHONE -+ (BOOL)isMultitaskingSupported -{ - BOOL multiTaskingSupported = NO; - if ([[UIDevice currentDevice] respondsToSelector:@selector(isMultitaskingSupported)]) { - multiTaskingSupported = [(id)[UIDevice currentDevice] isMultitaskingSupported]; - } - return multiTaskingSupported; -} -#endif - -// From: http://www.cocoadev.com/index.pl?BaseSixtyFour - -+ (NSString*)base64forData:(NSData*)theData { - - const uint8_t* input = (const uint8_t*)[theData bytes]; - NSInteger length = [theData length]; - - static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; - - NSMutableData* data = [NSMutableData dataWithLength:((length + 2) / 3) * 4]; - uint8_t* output = (uint8_t*)data.mutableBytes; - - NSInteger i,i2; - for (i=0; i < length; i += 3) { - NSInteger value = 0; - for (i2=0; i2<3; i2++) { - value <<= 8; - if (i+i2 < length) { - value |= (0xFF & input[i+i2]); - } - } - - NSInteger theIndex = (i / 3) * 4; - output[theIndex + 0] = table[(value >> 18) & 0x3F]; - output[theIndex + 1] = table[(value >> 12) & 0x3F]; - output[theIndex + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '='; - output[theIndex + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '='; - } - - return [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease]; -} - -+ (NSDate *)expiryDateForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge -{ - NSDictionary *responseHeaders = [request responseHeaders]; - - // If we weren't given a custom max-age, lets look for one in the response headers - if (!maxAge) { - NSString *cacheControl = [[responseHeaders objectForKey:@"Cache-Control"] lowercaseString]; - if (cacheControl) { - NSScanner *scanner = [NSScanner scannerWithString:cacheControl]; - [scanner scanUpToString:@"max-age" intoString:NULL]; - if ([scanner scanString:@"max-age" intoString:NULL]) { - [scanner scanString:@"=" intoString:NULL]; - [scanner scanDouble:&maxAge]; - } - } - } - - // RFC 2612 says max-age must override any Expires header - if (maxAge) { - return [[NSDate date] addTimeInterval:maxAge]; - } else { - NSString *expires = [responseHeaders objectForKey:@"Expires"]; - if (expires) { - return [ASIHTTPRequest dateFromRFC1123String:expires]; - } - } - return nil; -} - -// Based on hints from http://stackoverflow.com/questions/1850824/parsing-a-rfc-822-date-with-nsdateformatter -+ (NSDate *)dateFromRFC1123String:(NSString *)string -{ - NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease]; - [formatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"] autorelease]]; - // Does the string include a week day? - NSString *day = @""; - if ([string rangeOfString:@","].location != NSNotFound) { - day = @"EEE, "; - } - // Does the string include seconds? - NSString *seconds = @""; - if ([[string componentsSeparatedByString:@":"] count] == 3) { - seconds = @":ss"; - } - [formatter setDateFormat:[NSString stringWithFormat:@"%@dd MMM yyyy HH:mm%@ z",day,seconds]]; - return [formatter dateFromString:string]; -} - -+ (void)parseMimeType:(NSString **)mimeType andResponseEncoding:(NSStringEncoding *)stringEncoding fromContentType:(NSString *)contentType -{ - if (!contentType) { - return; - } - NSScanner *charsetScanner = [NSScanner scannerWithString: contentType]; - if (![charsetScanner scanUpToString:@";" intoString:mimeType] || [charsetScanner scanLocation] == [contentType length]) { - *mimeType = [contentType stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - return; - } - *mimeType = [*mimeType stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - NSString *charsetSeparator = @"charset="; - NSString *IANAEncoding = nil; - - if ([charsetScanner scanUpToString: charsetSeparator intoString: NULL] && [charsetScanner scanLocation] < [contentType length]) { - [charsetScanner setScanLocation: [charsetScanner scanLocation] + [charsetSeparator length]]; - [charsetScanner scanUpToString: @";" intoString: &IANAEncoding]; - } - - if (IANAEncoding) { - CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)IANAEncoding); - if (cfEncoding != kCFStringEncodingInvalidId) { - *stringEncoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding); - } - } -} - -#pragma mark - -#pragma mark blocks -#if NS_BLOCKS_AVAILABLE -- (void)setStartedBlock:(ASIBasicBlock)aStartedBlock -{ - [startedBlock release]; - startedBlock = [aStartedBlock copy]; -} - -- (void)setHeadersReceivedBlock:(ASIHeadersBlock)aReceivedBlock -{ - [headersReceivedBlock release]; - headersReceivedBlock = [aReceivedBlock copy]; -} - -- (void)setCompletionBlock:(ASIBasicBlock)aCompletionBlock -{ - [completionBlock release]; - completionBlock = [aCompletionBlock copy]; -} - -- (void)setFailedBlock:(ASIBasicBlock)aFailedBlock -{ - [failureBlock release]; - failureBlock = [aFailedBlock copy]; -} - -- (void)setBytesReceivedBlock:(ASIProgressBlock)aBytesReceivedBlock -{ - [bytesReceivedBlock release]; - bytesReceivedBlock = [aBytesReceivedBlock copy]; -} - -- (void)setBytesSentBlock:(ASIProgressBlock)aBytesSentBlock -{ - [bytesSentBlock release]; - bytesSentBlock = [aBytesSentBlock copy]; -} - -- (void)setDownloadSizeIncrementedBlock:(ASISizeBlock)aDownloadSizeIncrementedBlock{ - [downloadSizeIncrementedBlock release]; - downloadSizeIncrementedBlock = [aDownloadSizeIncrementedBlock copy]; -} - -- (void)setUploadSizeIncrementedBlock:(ASISizeBlock)anUploadSizeIncrementedBlock -{ - [uploadSizeIncrementedBlock release]; - uploadSizeIncrementedBlock = [anUploadSizeIncrementedBlock copy]; -} - -- (void)setDataReceivedBlock:(ASIDataBlock)aReceivedBlock -{ - [dataReceivedBlock release]; - dataReceivedBlock = [aReceivedBlock copy]; -} - -- (void)setAuthenticationNeededBlock:(ASIBasicBlock)anAuthenticationBlock -{ - [authenticationNeededBlock release]; - authenticationNeededBlock = [anAuthenticationBlock copy]; -} -- (void)setProxyAuthenticationNeededBlock:(ASIBasicBlock)aProxyAuthenticationBlock -{ - [proxyAuthenticationNeededBlock release]; - proxyAuthenticationNeededBlock = [aProxyAuthenticationBlock copy]; -} -- (void)setRequestRedirectedBlock:(ASIBasicBlock)aRedirectBlock -{ - [requestRedirectedBlock release]; - requestRedirectedBlock = [aRedirectBlock copy]; -} -#endif - -#pragma mark === - -@synthesize username; -@synthesize password; -@synthesize userAgent; -@synthesize domain; -@synthesize proxyUsername; -@synthesize proxyPassword; -@synthesize proxyDomain; -@synthesize url; -@synthesize originalURL; -@synthesize delegate; -@synthesize queue; -@synthesize uploadProgressDelegate; -@synthesize downloadProgressDelegate; -@synthesize useKeychainPersistence; -@synthesize useSessionPersistence; -@synthesize useCookiePersistence; -@synthesize downloadDestinationPath; -@synthesize temporaryFileDownloadPath; -@synthesize temporaryUncompressedDataDownloadPath; -@synthesize didStartSelector; -@synthesize didReceiveResponseHeadersSelector; -@synthesize willRedirectSelector; -@synthesize didFinishSelector; -@synthesize didFailSelector; -@synthesize didReceiveDataSelector; -@synthesize authenticationRealm; -@synthesize proxyAuthenticationRealm; -@synthesize error; -@synthesize complete; -@synthesize requestHeaders; -@synthesize responseHeaders; -@synthesize responseCookies; -@synthesize requestCookies; -@synthesize requestCredentials; -@synthesize responseStatusCode; -@synthesize rawResponseData; -@synthesize lastActivityTime; -@synthesize timeOutSeconds; -@synthesize requestMethod; -@synthesize postBody; -@synthesize compressedPostBody; -@synthesize contentLength; -@synthesize partialDownloadSize; -@synthesize postLength; -@synthesize shouldResetDownloadProgress; -@synthesize shouldResetUploadProgress; -@synthesize mainRequest; -@synthesize totalBytesRead; -@synthesize totalBytesSent; -@synthesize showAccurateProgress; -@synthesize uploadBufferSize; -@synthesize defaultResponseEncoding; -@synthesize responseEncoding; -@synthesize allowCompressedResponse; -@synthesize allowResumeForFileDownloads; -@synthesize userInfo; -@synthesize tag; -@synthesize postBodyFilePath; -@synthesize compressedPostBodyFilePath; -@synthesize postBodyWriteStream; -@synthesize postBodyReadStream; -@synthesize shouldStreamPostDataFromDisk; -@synthesize didCreateTemporaryPostDataFile; -@synthesize useHTTPVersionOne; -@synthesize lastBytesRead; -@synthesize lastBytesSent; -@synthesize cancelledLock; -@synthesize haveBuiltPostBody; -@synthesize fileDownloadOutputStream; -@synthesize inflatedFileDownloadOutputStream; -@synthesize authenticationRetryCount; -@synthesize proxyAuthenticationRetryCount; -@synthesize updatedProgress; -@synthesize shouldRedirect; -@synthesize validatesSecureCertificate; -@synthesize needsRedirect; -@synthesize redirectCount; -@synthesize shouldCompressRequestBody; -@synthesize proxyCredentials; -@synthesize proxyHost; -@synthesize proxyPort; -@synthesize proxyType; -@synthesize PACurl; -@synthesize authenticationScheme; -@synthesize proxyAuthenticationScheme; -@synthesize shouldPresentAuthenticationDialog; -@synthesize shouldPresentProxyAuthenticationDialog; -@synthesize authenticationNeeded; -@synthesize responseStatusMessage; -@synthesize shouldPresentCredentialsBeforeChallenge; -@synthesize haveBuiltRequestHeaders; -@synthesize inProgress; -@synthesize numberOfTimesToRetryOnTimeout; -@synthesize retryCount; -@synthesize willRetryRequest; -@synthesize shouldAttemptPersistentConnection; -@synthesize persistentConnectionTimeoutSeconds; -@synthesize connectionCanBeReused; -@synthesize connectionInfo; -@synthesize readStream; -@synthesize readStreamIsScheduled; -@synthesize shouldUseRFC2616RedirectBehaviour; -@synthesize downloadComplete; -@synthesize requestID; -@synthesize runLoopMode; -@synthesize statusTimer; -@synthesize downloadCache; -@synthesize cachePolicy; -@synthesize cacheStoragePolicy; -@synthesize didUseCachedResponse; -@synthesize secondsToCache; -@synthesize clientCertificates; -@synthesize redirectURL; -#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 -@synthesize shouldContinueWhenAppEntersBackground; -#endif -@synthesize dataDecompressor; -@synthesize shouldWaitToInflateCompressedResponses; - -@synthesize isPACFileRequest; -@synthesize PACFileRequest; -@synthesize PACFileReadStream; -@synthesize PACFileData; - -@synthesize isSynchronous; -@end diff --git a/ASIHTTP/ASIHTTPRequestConfig.h b/ASIHTTP/ASIHTTPRequestConfig.h deleted file mode 100644 index 3f6c587..0000000 --- a/ASIHTTP/ASIHTTPRequestConfig.h +++ /dev/null @@ -1,43 +0,0 @@ -// -// ASIHTTPRequestConfig.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 14/12/2009. -// Copyright 2009 All-Seeing Interactive. All rights reserved. -// - - -// ====== -// Debug output configuration options -// ====== - -// If defined will use the specified function for debug logging -// Otherwise use NSLog -#ifndef ASI_DEBUG_LOG - #define ASI_DEBUG_LOG NSLog -#endif - -// When set to 1 ASIHTTPRequests will print information about what a request is doing -#ifndef DEBUG_REQUEST_STATUS - #define DEBUG_REQUEST_STATUS 0 -#endif - -// When set to 1, ASIFormDataRequests will print information about the request body to the console -#ifndef DEBUG_FORM_DATA_REQUEST - #define DEBUG_FORM_DATA_REQUEST 0 -#endif - -// When set to 1, ASIHTTPRequests will print information about bandwidth throttling to the console -#ifndef DEBUG_THROTTLING - #define DEBUG_THROTTLING 0 -#endif - -// When set to 1, ASIHTTPRequests will print information about persistent connections to the console -#ifndef DEBUG_PERSISTENT_CONNECTIONS - #define DEBUG_PERSISTENT_CONNECTIONS 0 -#endif - -// When set to 1, ASIHTTPRequests will print information about HTTP authentication (Basic, Digest or NTLM) to the console -#ifndef DEBUG_HTTP_AUTHENTICATION - #define DEBUG_HTTP_AUTHENTICATION 0 -#endif diff --git a/ASIHTTP/ASIHTTPRequestDelegate.h b/ASIHTTP/ASIHTTPRequestDelegate.h deleted file mode 100644 index c495a27..0000000 --- a/ASIHTTP/ASIHTTPRequestDelegate.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// ASIHTTPRequestDelegate.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 13/04/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// - -@class ASIHTTPRequest; - -@protocol ASIHTTPRequestDelegate - -@optional - -// These are the default delegate methods for request status -// You can use different ones by setting didStartSelector / didFinishSelector / didFailSelector -- (void)requestStarted:(ASIHTTPRequest *)request; -- (void)request:(ASIHTTPRequest *)request didReceiveResponseHeaders:(NSDictionary *)responseHeaders; -- (void)request:(ASIHTTPRequest *)request willRedirectToURL:(NSURL *)newURL; -- (void)requestFinished:(ASIHTTPRequest *)request; -- (void)requestFailed:(ASIHTTPRequest *)request; -- (void)requestRedirected:(ASIHTTPRequest *)request; - -// When a delegate implements this method, it is expected to process all incoming data itself -// This means that responseData / responseString / downloadDestinationPath etc are ignored -// You can have the request call a different method by setting didReceiveDataSelector -- (void)request:(ASIHTTPRequest *)request didReceiveData:(NSData *)data; - -// If a delegate implements one of these, it will be asked to supply credentials when none are available -// The delegate can then either restart the request ([request retryUsingSuppliedCredentials]) once credentials have been set -// or cancel it ([request cancelAuthentication]) -- (void)authenticationNeededForRequest:(ASIHTTPRequest *)request; -- (void)proxyAuthenticationNeededForRequest:(ASIHTTPRequest *)request; - -@end diff --git a/ASIHTTP/ASIInputStream.h b/ASIHTTP/ASIInputStream.h deleted file mode 100644 index 7b9f93e..0000000 --- a/ASIHTTP/ASIInputStream.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// ASIInputStream.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 10/08/2009. -// Copyright 2009 All-Seeing Interactive. All rights reserved. -// - -#import - -@class ASIHTTPRequest; - -// This is a wrapper for NSInputStream that pretends to be an NSInputStream itself -// Subclassing NSInputStream seems to be tricky, and may involve overriding undocumented methods, so we'll cheat instead. -// It is used by ASIHTTPRequest whenever we have a request body, and handles measuring and throttling the bandwidth used for uploading - -@interface ASIInputStream : NSObject { - NSInputStream *stream; - ASIHTTPRequest *request; -} -+ (id)inputStreamWithFileAtPath:(NSString *)path request:(ASIHTTPRequest *)request; -+ (id)inputStreamWithData:(NSData *)data request:(ASIHTTPRequest *)request; - -@property (retain, nonatomic) NSInputStream *stream; -@property (assign, nonatomic) ASIHTTPRequest *request; -@end diff --git a/ASIHTTP/ASIInputStream.m b/ASIHTTP/ASIInputStream.m deleted file mode 100644 index d2b8428..0000000 --- a/ASIHTTP/ASIInputStream.m +++ /dev/null @@ -1,138 +0,0 @@ -// -// ASIInputStream.m -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 10/08/2009. -// Copyright 2009 All-Seeing Interactive. All rights reserved. -// - -#import "ASIInputStream.h" -#import "ASIHTTPRequest.h" - -// Used to ensure only one request can read data at once -static NSLock *readLock = nil; - -@implementation ASIInputStream - -+ (void)initialize -{ - if (self == [ASIInputStream class]) { - readLock = [[NSLock alloc] init]; - } -} - -+ (id)inputStreamWithFileAtPath:(NSString *)path request:(ASIHTTPRequest *)theRequest -{ - ASIInputStream *theStream = [[[self alloc] init] autorelease]; - [theStream setRequest:theRequest]; - [theStream setStream:[NSInputStream inputStreamWithFileAtPath:path]]; - return theStream; -} - -+ (id)inputStreamWithData:(NSData *)data request:(ASIHTTPRequest *)theRequest -{ - ASIInputStream *theStream = [[[self alloc] init] autorelease]; - [theStream setRequest:theRequest]; - [theStream setStream:[NSInputStream inputStreamWithData:data]]; - return theStream; -} - -- (void)dealloc -{ - [stream release]; - [super dealloc]; -} - -// Called when CFNetwork wants to read more of our request body -// When throttling is on, we ask ASIHTTPRequest for the maximum amount of data we can read -- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len -{ - [readLock lock]; - unsigned long toRead = len; - if ([ASIHTTPRequest isBandwidthThrottled]) { - toRead = [ASIHTTPRequest maxUploadReadLength]; - if (toRead > len) { - toRead = len; - } else if (toRead == 0) { - toRead = 1; - } - [request performThrottling]; - } - [readLock unlock]; - NSInteger rv = [stream read:buffer maxLength:toRead]; - if (rv > 0) - [ASIHTTPRequest incrementBandwidthUsedInLastSecond:rv]; - return rv; -} - -/* - * Implement NSInputStream mandatory methods to make sure they are implemented - * (necessary for MacRuby for example) and avoid the overhead of method - * forwarding for these common methods. - */ -- (void)open -{ - [stream open]; -} - -- (void)close -{ - [stream close]; -} - -- (id)delegate -{ - return [stream delegate]; -} - -- (void)setDelegate:(id)delegate -{ - [stream setDelegate:delegate]; -} - -- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode -{ - [stream scheduleInRunLoop:aRunLoop forMode:mode]; -} - -- (void)removeFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode -{ - [stream removeFromRunLoop:aRunLoop forMode:mode]; -} - -- (id)propertyForKey:(NSString *)key -{ - return [stream propertyForKey:key]; -} - -- (BOOL)setProperty:(id)property forKey:(NSString *)key -{ - return [stream setProperty:property forKey:key]; -} - -- (NSStreamStatus)streamStatus -{ - return [stream streamStatus]; -} - -- (NSError *)streamError -{ - return [stream streamError]; -} - -// If we get asked to perform a method we don't have (probably internal ones), -// we'll just forward the message to our stream - -- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector -{ - return [stream methodSignatureForSelector:aSelector]; -} - -- (void)forwardInvocation:(NSInvocation *)anInvocation -{ - [anInvocation invokeWithTarget:stream]; -} - -@synthesize stream; -@synthesize request; -@end diff --git a/ASIHTTP/ASINetworkQueue.h b/ASIHTTP/ASINetworkQueue.h deleted file mode 100644 index 787f391..0000000 --- a/ASIHTTP/ASINetworkQueue.h +++ /dev/null @@ -1,108 +0,0 @@ -// -// ASINetworkQueue.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 07/11/2008. -// Copyright 2008-2009 All-Seeing Interactive. All rights reserved. -// - -#import -#import "ASIHTTPRequestDelegate.h" -#import "ASIProgressDelegate.h" - -@interface ASINetworkQueue : NSOperationQueue { - - // Delegate will get didFail + didFinish messages (if set) - id delegate; - - // Will be called when a request starts with the request as the argument - SEL requestDidStartSelector; - - // Will be called when a request receives response headers - // Should take the form request:didRecieveResponseHeaders:, where the first argument is the request, and the second the headers dictionary - SEL requestDidReceiveResponseHeadersSelector; - - // Will be called when a request is about to redirect - // Should take the form request:willRedirectToURL:, where the first argument is the request, and the second the new url - SEL requestWillRedirectSelector; - - // Will be called when a request completes with the request as the argument - SEL requestDidFinishSelector; - - // Will be called when a request fails with the request as the argument - SEL requestDidFailSelector; - - // Will be called when the queue finishes with the queue as the argument - SEL queueDidFinishSelector; - - // Upload progress indicator, probably an NSProgressIndicator or UIProgressView - id uploadProgressDelegate; - - // Total amount uploaded so far for all requests in this queue - unsigned long long bytesUploadedSoFar; - - // Total amount to be uploaded for all requests in this queue - requests add to this figure as they work out how much data they have to transmit - unsigned long long totalBytesToUpload; - - // Download progress indicator, probably an NSProgressIndicator or UIProgressView - id downloadProgressDelegate; - - // Total amount downloaded so far for all requests in this queue - unsigned long long bytesDownloadedSoFar; - - // Total amount to be downloaded for all requests in this queue - requests add to this figure as they receive Content-Length headers - unsigned long long totalBytesToDownload; - - // When YES, the queue will cancel all requests when a request fails. Default is YES - BOOL shouldCancelAllRequestsOnFailure; - - //Number of real requests (excludes HEAD requests created to manage showAccurateProgress) - int requestsCount; - - // When NO, this request will only update the progress indicator when it completes - // When YES, this request will update the progress indicator according to how much data it has received so far - // When YES, the queue will first perform HEAD requests for all GET requests in the queue, so it can calculate the total download size before it starts - // NO means better performance, because it skips this step for GET requests, and it won't waste time updating the progress indicator until a request completes - // Set to YES if the size of a requests in the queue varies greatly for much more accurate results - // Default for requests in the queue is NO - BOOL showAccurateProgress; - - // Storage container for additional queue information. - NSDictionary *userInfo; - -} - -// Convenience constructor -+ (id)queue; - -// Call this to reset a queue - it will cancel all operations, clear delegates, and suspend operation -- (void)reset; - -// Used internally to manage HEAD requests when showAccurateProgress is YES, do not use! -- (void)addHEADOperation:(NSOperation *)operation; - -// All ASINetworkQueues are paused when created so that total size can be calculated before the queue starts -// This method will start the queue -- (void)go; - -@property (assign, nonatomic, setter=setUploadProgressDelegate:) id uploadProgressDelegate; -@property (assign, nonatomic, setter=setDownloadProgressDelegate:) id downloadProgressDelegate; - -@property (assign) SEL requestDidStartSelector; -@property (assign) SEL requestDidReceiveResponseHeadersSelector; -@property (assign) SEL requestWillRedirectSelector; -@property (assign) SEL requestDidFinishSelector; -@property (assign) SEL requestDidFailSelector; -@property (assign) SEL queueDidFinishSelector; -@property (assign) BOOL shouldCancelAllRequestsOnFailure; -@property (assign) id delegate; -@property (assign) BOOL showAccurateProgress; -@property (assign, readonly) int requestsCount; -@property (retain) NSDictionary *userInfo; - -@property (assign) unsigned long long bytesUploadedSoFar; -@property (assign) unsigned long long totalBytesToUpload; -@property (assign) unsigned long long bytesDownloadedSoFar; -@property (assign) unsigned long long totalBytesToDownload; - -@end diff --git a/ASIHTTP/ASINetworkQueue.m b/ASIHTTP/ASINetworkQueue.m deleted file mode 100644 index b24076d..0000000 --- a/ASIHTTP/ASINetworkQueue.m +++ /dev/null @@ -1,343 +0,0 @@ -// -// ASINetworkQueue.m -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 07/11/2008. -// Copyright 2008-2009 All-Seeing Interactive. All rights reserved. -// - -#import "ASINetworkQueue.h" -#import "ASIHTTPRequest.h" - -// Private stuff -@interface ASINetworkQueue () - - (void)resetProgressDelegate:(id *)progressDelegate; - @property (assign) int requestsCount; -@end - -@implementation ASINetworkQueue - -- (id)init -{ - self = [super init]; - [self setShouldCancelAllRequestsOnFailure:YES]; - [self setMaxConcurrentOperationCount:4]; - [self setSuspended:YES]; - - return self; -} - -+ (id)queue -{ - return [[[self alloc] init] autorelease]; -} - -- (void)dealloc -{ - //We need to clear the queue on any requests that haven't got around to cleaning up yet, as otherwise they'll try to let us know if something goes wrong, and we'll be long gone by then - for (ASIHTTPRequest *request in [self operations]) { - [request setQueue:nil]; - } - [userInfo release]; - [super dealloc]; -} - -- (void)setSuspended:(BOOL)suspend -{ - [super setSuspended:suspend]; -} - -- (void)reset -{ - [self cancelAllOperations]; - [self setDelegate:nil]; - [self setDownloadProgressDelegate:nil]; - [self setUploadProgressDelegate:nil]; - [self setRequestDidStartSelector:NULL]; - [self setRequestDidReceiveResponseHeadersSelector:NULL]; - [self setRequestDidFailSelector:NULL]; - [self setRequestDidFinishSelector:NULL]; - [self setQueueDidFinishSelector:NULL]; - [self setSuspended:YES]; -} - - -- (void)go -{ - [self setSuspended:NO]; -} - -- (void)cancelAllOperations -{ - [self setBytesUploadedSoFar:0]; - [self setTotalBytesToUpload:0]; - [self setBytesDownloadedSoFar:0]; - [self setTotalBytesToDownload:0]; - [super cancelAllOperations]; -} - -- (void)setUploadProgressDelegate:(id)newDelegate -{ - uploadProgressDelegate = newDelegate; - [self resetProgressDelegate:&uploadProgressDelegate]; - -} - -- (void)setDownloadProgressDelegate:(id)newDelegate -{ - downloadProgressDelegate = newDelegate; - [self resetProgressDelegate:&downloadProgressDelegate]; -} - -- (void)resetProgressDelegate:(id *)progressDelegate -{ -#if !TARGET_OS_IPHONE - // If the uploadProgressDelegate is an NSProgressIndicator, we set its MaxValue to 1.0 so we can treat it similarly to UIProgressViews - SEL selector = @selector(setMaxValue:); - if ([*progressDelegate respondsToSelector:selector]) { - double max = 1.0; - [ASIHTTPRequest performSelector:selector onTarget:progressDelegate withObject:nil amount:&max callerToRetain:nil]; - } - selector = @selector(setDoubleValue:); - if ([*progressDelegate respondsToSelector:selector]) { - double value = 0.0; - [ASIHTTPRequest performSelector:selector onTarget:progressDelegate withObject:nil amount:&value callerToRetain:nil]; - } -#else - SEL selector = @selector(setProgress:); - if ([*progressDelegate respondsToSelector:selector]) { - float value = 0.0f; - [ASIHTTPRequest performSelector:selector onTarget:progressDelegate withObject:nil amount:&value callerToRetain:nil]; - } -#endif -} - -- (void)addHEADOperation:(NSOperation *)operation -{ - if ([operation isKindOfClass:[ASIHTTPRequest class]]) { - - ASIHTTPRequest *request = (ASIHTTPRequest *)operation; - [request setRequestMethod:@"HEAD"]; - [request setQueuePriority:10]; - [request setShowAccurateProgress:YES]; - [request setQueue:self]; - - // Important - we are calling NSOperation's add method - we don't want to add this as a normal request! - [super addOperation:request]; - } -} - -// Only add ASIHTTPRequests to this queue!! -- (void)addOperation:(NSOperation *)operation -{ - if (![operation isKindOfClass:[ASIHTTPRequest class]]) { - [NSException raise:@"AttemptToAddInvalidRequest" format:@"Attempted to add an object that was not an ASIHTTPRequest to an ASINetworkQueue"]; - } - - [self setRequestsCount:[self requestsCount]+1]; - - ASIHTTPRequest *request = (ASIHTTPRequest *)operation; - - if ([self showAccurateProgress]) { - - // Force the request to build its body (this may change requestMethod) - [request buildPostBody]; - - // If this is a GET request and we want accurate progress, perform a HEAD request first to get the content-length - // We'll only do this before the queue is started - // If requests are added after the queue is started they will probably move the overall progress backwards anyway, so there's no value performing the HEAD requests first - // Instead, they'll update the total progress if and when they receive a content-length header - if ([[request requestMethod] isEqualToString:@"GET"]) { - if ([self isSuspended]) { - ASIHTTPRequest *HEADRequest = [request HEADRequest]; - [self addHEADOperation:HEADRequest]; - [request addDependency:HEADRequest]; - if ([request shouldResetDownloadProgress]) { - [self resetProgressDelegate:&downloadProgressDelegate]; - [request setShouldResetDownloadProgress:NO]; - } - } - } - [request buildPostBody]; - [self request:nil incrementUploadSizeBy:[request postLength]]; - - - } else { - [self request:nil incrementDownloadSizeBy:1]; - [self request:nil incrementUploadSizeBy:1]; - } - // Tell the request not to increment the upload size when it starts, as we've already added its length - if ([request shouldResetUploadProgress]) { - [self resetProgressDelegate:&uploadProgressDelegate]; - [request setShouldResetUploadProgress:NO]; - } - - [request setShowAccurateProgress:[self showAccurateProgress]]; - - [request setQueue:self]; - [super addOperation:request]; - -} - -- (void)requestStarted:(ASIHTTPRequest *)request -{ - if ([self requestDidStartSelector]) { - [[self delegate] performSelector:[self requestDidStartSelector] withObject:request]; - } -} - -- (void)request:(ASIHTTPRequest *)request didReceiveResponseHeaders:(NSDictionary *)responseHeaders -{ - if ([self requestDidReceiveResponseHeadersSelector]) { - [[self delegate] performSelector:[self requestDidReceiveResponseHeadersSelector] withObject:request withObject:responseHeaders]; - } -} - -- (void)request:(ASIHTTPRequest *)request willRedirectToURL:(NSURL *)newURL -{ - if ([self requestWillRedirectSelector]) { - [[self delegate] performSelector:[self requestWillRedirectSelector] withObject:request withObject:newURL]; - } -} - -- (void)requestFinished:(ASIHTTPRequest *)request -{ - [self setRequestsCount:[self requestsCount]-1]; - if ([self requestDidFinishSelector]) { - [[self delegate] performSelector:[self requestDidFinishSelector] withObject:request]; - } - if ([self requestsCount] == 0) { - if ([self queueDidFinishSelector]) { - [[self delegate] performSelector:[self queueDidFinishSelector] withObject:self]; - } - } -} - -- (void)requestFailed:(ASIHTTPRequest *)request -{ - [self setRequestsCount:[self requestsCount]-1]; - if ([self requestDidFailSelector]) { - [[self delegate] performSelector:[self requestDidFailSelector] withObject:request]; - } - if ([self requestsCount] == 0) { - if ([self queueDidFinishSelector]) { - [[self delegate] performSelector:[self queueDidFinishSelector] withObject:self]; - } - } - if ([self shouldCancelAllRequestsOnFailure] && [self requestsCount] > 0) { - [self cancelAllOperations]; - } - -} - - -- (void)request:(ASIHTTPRequest *)request didReceiveBytes:(long long)bytes -{ - [self setBytesDownloadedSoFar:[self bytesDownloadedSoFar]+bytes]; - if ([self downloadProgressDelegate]) { - [ASIHTTPRequest updateProgressIndicator:&downloadProgressDelegate withProgress:[self bytesDownloadedSoFar] ofTotal:[self totalBytesToDownload]]; - } -} - -- (void)request:(ASIHTTPRequest *)request didSendBytes:(long long)bytes -{ - [self setBytesUploadedSoFar:[self bytesUploadedSoFar]+bytes]; - if ([self uploadProgressDelegate]) { - [ASIHTTPRequest updateProgressIndicator:&uploadProgressDelegate withProgress:[self bytesUploadedSoFar] ofTotal:[self totalBytesToUpload]]; - } -} - -- (void)request:(ASIHTTPRequest *)request incrementDownloadSizeBy:(long long)newLength -{ - [self setTotalBytesToDownload:[self totalBytesToDownload]+newLength]; -} - -- (void)request:(ASIHTTPRequest *)request incrementUploadSizeBy:(long long)newLength -{ - [self setTotalBytesToUpload:[self totalBytesToUpload]+newLength]; -} - - -// Since this queue takes over as the delegate for all requests it contains, it should forward authorisation requests to its own delegate -- (void)authenticationNeededForRequest:(ASIHTTPRequest *)request -{ - if ([[self delegate] respondsToSelector:@selector(authenticationNeededForRequest:)]) { - [[self delegate] performSelector:@selector(authenticationNeededForRequest:) withObject:request]; - } -} - -- (void)proxyAuthenticationNeededForRequest:(ASIHTTPRequest *)request -{ - if ([[self delegate] respondsToSelector:@selector(proxyAuthenticationNeededForRequest:)]) { - [[self delegate] performSelector:@selector(proxyAuthenticationNeededForRequest:) withObject:request]; - } -} - - -- (BOOL)respondsToSelector:(SEL)selector -{ - // We handle certain methods differently because whether our delegate implements them or not can affect how the request should behave - - // If the delegate implements this, the request will stop to wait for credentials - if (selector == @selector(authenticationNeededForRequest:)) { - if ([[self delegate] respondsToSelector:@selector(authenticationNeededForRequest:)]) { - return YES; - } - return NO; - - // If the delegate implements this, the request will to wait for credentials - } else if (selector == @selector(proxyAuthenticationNeededForRequest:)) { - if ([[self delegate] respondsToSelector:@selector(proxyAuthenticationNeededForRequest:)]) { - return YES; - } - return NO; - - // If the delegate implements requestWillRedirectSelector, the request will stop to allow the delegate to change the url - } else if (selector == @selector(request:willRedirectToURL:)) { - if ([self requestWillRedirectSelector] && [[self delegate] respondsToSelector:[self requestWillRedirectSelector]]) { - return YES; - } - return NO; - } - return [super respondsToSelector:selector]; -} - -#pragma mark NSCopying - -- (id)copyWithZone:(NSZone *)zone -{ - ASINetworkQueue *newQueue = [[[self class] alloc] init]; - [newQueue setDelegate:[self delegate]]; - [newQueue setRequestDidStartSelector:[self requestDidStartSelector]]; - [newQueue setRequestWillRedirectSelector:[self requestWillRedirectSelector]]; - [newQueue setRequestDidReceiveResponseHeadersSelector:[self requestDidReceiveResponseHeadersSelector]]; - [newQueue setRequestDidFinishSelector:[self requestDidFinishSelector]]; - [newQueue setRequestDidFailSelector:[self requestDidFailSelector]]; - [newQueue setQueueDidFinishSelector:[self queueDidFinishSelector]]; - [newQueue setUploadProgressDelegate:[self uploadProgressDelegate]]; - [newQueue setDownloadProgressDelegate:[self downloadProgressDelegate]]; - [newQueue setShouldCancelAllRequestsOnFailure:[self shouldCancelAllRequestsOnFailure]]; - [newQueue setShowAccurateProgress:[self showAccurateProgress]]; - [newQueue setUserInfo:[[[self userInfo] copyWithZone:zone] autorelease]]; - return newQueue; -} - - -@synthesize requestsCount; -@synthesize bytesUploadedSoFar; -@synthesize totalBytesToUpload; -@synthesize bytesDownloadedSoFar; -@synthesize totalBytesToDownload; -@synthesize shouldCancelAllRequestsOnFailure; -@synthesize uploadProgressDelegate; -@synthesize downloadProgressDelegate; -@synthesize requestDidStartSelector; -@synthesize requestDidReceiveResponseHeadersSelector; -@synthesize requestWillRedirectSelector; -@synthesize requestDidFinishSelector; -@synthesize requestDidFailSelector; -@synthesize queueDidFinishSelector; -@synthesize delegate; -@synthesize showAccurateProgress; -@synthesize userInfo; -@end diff --git a/ASIHTTP/ASIProgressDelegate.h b/ASIHTTP/ASIProgressDelegate.h deleted file mode 100644 index e2bb0cf..0000000 --- a/ASIHTTP/ASIProgressDelegate.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// ASIProgressDelegate.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 13/04/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// - -@class ASIHTTPRequest; - -@protocol ASIProgressDelegate - -@optional - -// These methods are used to update UIProgressViews (iPhone OS) or NSProgressIndicators (Mac OS X) -// If you are using a custom progress delegate, you may find it easier to implement didReceiveBytes / didSendBytes instead -#if TARGET_OS_IPHONE -- (void)setProgress:(float)newProgress; -#else -- (void)setDoubleValue:(double)newProgress; -- (void)setMaxValue:(double)newMax; -#endif - -// Called when the request receives some data - bytes is the length of that data -- (void)request:(ASIHTTPRequest *)request didReceiveBytes:(long long)bytes; - -// Called when the request sends some data -// The first 32KB (128KB on older platforms) of data sent is not included in this amount because of limitations with the CFNetwork API -// bytes may be less than zero if a request needs to remove upload progress (probably because the request needs to run again) -- (void)request:(ASIHTTPRequest *)request didSendBytes:(long long)bytes; - -// Called when a request needs to change the length of the content to download -- (void)request:(ASIHTTPRequest *)request incrementDownloadSizeBy:(long long)newLength; - -// Called when a request needs to change the length of the content to upload -// newLength may be less than zero when a request needs to remove the size of the internal buffer from progress tracking -- (void)request:(ASIHTTPRequest *)request incrementUploadSizeBy:(long long)newLength; -@end diff --git a/ASIHTTP/ASIWebPageRequest.h b/ASIHTTP/ASIWebPageRequest.h deleted file mode 100644 index b82f440..0000000 --- a/ASIHTTP/ASIWebPageRequest.h +++ /dev/null @@ -1,80 +0,0 @@ -// -// ASIWebPageRequest.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 29/06/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// -// This is an EXPERIMENTAL class - use at your own risk! -// It is strongly recommend to set a downloadDestinationPath when using ASIWebPageRequest -// Also, performance will be better if your ASIWebPageRequest has a downloadCache setup -// Known issue: You cannot use startSychronous with an ASIWebPageRequest - -#import "ASIHTTPRequest.h" - -@class ASINetworkQueue; - -// Used internally for storing what type of data we got from the server -typedef enum _ASIWebContentType { - ASINotParsedWebContentType = 0, - ASIHTMLWebContentType = 1, - ASICSSWebContentType = 2 -} ASIWebContentType; - -// These correspond with the urlReplacementMode property of ASIWebPageRequest -typedef enum _ASIURLReplacementMode { - - // Don't modify html or css content at all - ASIDontModifyURLs = 0, - - // Replace external resources urls (images, stylesheets etc) with data uris, so their content is embdedded directly in the html/css - ASIReplaceExternalResourcesWithData = 1, - - // Replace external resource urls with the url of locally cached content - // You must set the baseURL of a WebView / UIWebView to a file url pointing at the downloadDestinationPath of the main ASIWebPageRequest if you want to display your content - // See the Mac or iPhone example projects for a demonstration of how to do this - // The hrefs of all hyperlinks are changed to use absolute urls when using this mode - ASIReplaceExternalResourcesWithLocalURLs = 2 -} ASIURLReplacementMode; - - - -@interface ASIWebPageRequest : ASIHTTPRequest { - - // Each ASIWebPageRequest for an HTML or CSS file creates its own internal queue to download external resources - ASINetworkQueue *externalResourceQueue; - - // This dictionary stores a list of external resources to download, along with their content-type data or a path to the data - NSMutableDictionary *resourceList; - - // Used internally for parsing HTML (with libxml) - struct _xmlDoc *doc; - - // If the response is an HTML or CSS file, this will be set so the content can be correctly parsed when it has finished fetching external resources - ASIWebContentType webContentType; - - // Stores a reference to the ASIWebPageRequest that created this request - // Note that a parentRequest can also have a parent request because ASIWebPageRequests parse their contents to look for external resources recursively - // For example, a request for an image can be created by a request for a stylesheet which was created by a request for a web page - ASIWebPageRequest *parentRequest; - - // Controls what ASIWebPageRequest does with external resources. See the notes above for more. - ASIURLReplacementMode urlReplacementMode; - - // When set to NO, loading will stop when an external resource fails to load. Defaults to YES - BOOL shouldIgnoreExternalResourceErrors; -} - -// Will return a data URI that contains a base64 version of the content at this url -// This is used when replacing urls in the html and css with actual data -// If you subclass ASIWebPageRequest, you can override this function to return different content or a url pointing at another location -- (NSString *)contentForExternalURL:(NSString *)theURL; - -// Returns the location that a downloaded external resource's content will be stored in -- (NSString *)cachePathForRequest:(ASIWebPageRequest *)theRequest; - - -@property (retain, nonatomic) ASIWebPageRequest *parentRequest; -@property (assign, nonatomic) ASIURLReplacementMode urlReplacementMode; -@property (assign, nonatomic) BOOL shouldIgnoreExternalResourceErrors; -@end diff --git a/ASIHTTP/ASIWebPageRequest.m b/ASIHTTP/ASIWebPageRequest.m deleted file mode 100644 index eccac6a..0000000 --- a/ASIHTTP/ASIWebPageRequest.m +++ /dev/null @@ -1,722 +0,0 @@ -// -// ASIWebPageRequest.m -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 29/06/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// -// This is an EXPERIMENTAL class - use at your own risk! - -#import "ASIWebPageRequest.h" -#import "ASINetworkQueue.h" -#import -#import -#import -#import -#import - -// An xPath query that controls the external resources ASIWebPageRequest will fetch -// By default, it will fetch stylesheets, javascript files, images, frames, iframes, and html 5 video / audio -static xmlChar *xpathExpr = (xmlChar *)"//link/@href|//a/@href|//script/@src|//img/@src|//frame/@src|//iframe/@src|//style|//*/@style|//source/@src|//video/@poster|//audio/@src"; - -static NSLock *xmlParsingLock = nil; -static NSMutableArray *requestsUsingXMLParser = nil; - -@interface ASIWebPageRequest () -- (void)readResourceURLs; -- (void)updateResourceURLs; -- (void)parseAsHTML; -- (void)parseAsCSS; -- (void)addURLToFetch:(NSString *)newURL; -+ (NSArray *)CSSURLsFromString:(NSString *)string; -- (NSString *)relativePathTo:(NSString *)destinationPath fromPath:(NSString *)sourcePath; - -- (void)finishedFetchingExternalResources:(ASINetworkQueue *)queue; -- (void)externalResourceFetchSucceeded:(ASIHTTPRequest *)externalResourceRequest; -- (void)externalResourceFetchFailed:(ASIHTTPRequest *)externalResourceRequest; - -@property (retain, nonatomic) ASINetworkQueue *externalResourceQueue; -@property (retain, nonatomic) NSMutableDictionary *resourceList; -@end - -@implementation ASIWebPageRequest - -+ (void)initialize -{ - if (self == [ASIWebPageRequest class]) { - xmlParsingLock = [[NSLock alloc] init]; - requestsUsingXMLParser = [[NSMutableArray alloc] init]; - } -} - -- (id)initWithURL:(NSURL *)newURL -{ - self = [super initWithURL:newURL]; - [self setShouldIgnoreExternalResourceErrors:YES]; - return self; -} - -- (void)dealloc -{ - [externalResourceQueue cancelAllOperations]; - [externalResourceQueue release]; - [resourceList release]; - [parentRequest release]; - [super dealloc]; -} - -// This is a bit of a hack -// The role of this method in normal ASIHTTPRequests is to tell the queue we are done with the request, and perform some cleanup -// We override it to stop that happening, and instead do that work in the bottom of finishedFetchingExternalResources: -- (void)markAsFinished -{ - if ([self error]) { - [super markAsFinished]; - } -} - -// This method is normally responsible for telling delegates we are done, but it happens to be the most convenient place to parse the responses -// Again, we call the super implementation in finishedFetchingExternalResources:, or here if this download was not an HTML or CSS file -- (void)requestFinished -{ - complete = NO; - if ([self mainRequest] || [self didUseCachedResponse]) { - [super requestFinished]; - [super markAsFinished]; - return; - } - webContentType = ASINotParsedWebContentType; - NSString *contentType = [[[self responseHeaders] objectForKey:@"Content-Type"] lowercaseString]; - contentType = [[contentType componentsSeparatedByString:@";"] objectAtIndex:0]; - if ([contentType isEqualToString:@"text/html"] || [contentType isEqualToString:@"text/xhtml"] || [contentType isEqualToString:@"text/xhtml+xml"] || [contentType isEqualToString:@"application/xhtml+xml"]) { - [self parseAsHTML]; - return; - } else if ([contentType isEqualToString:@"text/css"]) { - [self parseAsCSS]; - return; - } - [super requestFinished]; - [super markAsFinished]; -} - -- (void)parseAsCSS -{ - webContentType = ASICSSWebContentType; - - NSString *responseCSS = nil; - NSError *err = nil; - if ([self downloadDestinationPath]) { - responseCSS = [NSString stringWithContentsOfFile:[self downloadDestinationPath] encoding:[self responseEncoding] error:&err]; - } else { - responseCSS = [self responseString]; - } - if (err) { - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:100 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to read HTML string from response",NSLocalizedDescriptionKey,err,NSUnderlyingErrorKey,nil]]]; - return; - } else if (!responseCSS) { - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:100 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to read HTML string from response",NSLocalizedDescriptionKey,nil]]]; - return; - } - NSArray *urls = [[self class] CSSURLsFromString:responseCSS]; - - [self setResourceList:[NSMutableDictionary dictionary]]; - - for (NSString *theURL in urls) { - [self addURLToFetch:theURL]; - } - if (![[self resourceList] count]) { - [super requestFinished]; - [super markAsFinished]; - return; - } - - // Create a new request for every item in the queue - [[self externalResourceQueue] cancelAllOperations]; - [self setExternalResourceQueue:[ASINetworkQueue queue]]; - [[self externalResourceQueue] setDelegate:self]; - [[self externalResourceQueue] setShouldCancelAllRequestsOnFailure:[self shouldIgnoreExternalResourceErrors]]; - [[self externalResourceQueue] setShowAccurateProgress:[self showAccurateProgress]]; - [[self externalResourceQueue] setQueueDidFinishSelector:@selector(finishedFetchingExternalResources:)]; - [[self externalResourceQueue] setRequestDidFinishSelector:@selector(externalResourceFetchSucceeded:)]; - [[self externalResourceQueue] setRequestDidFailSelector:@selector(externalResourceFetchFailed:)]; - for (NSString *theURL in [[self resourceList] keyEnumerator]) { - ASIWebPageRequest *externalResourceRequest = [ASIWebPageRequest requestWithURL:[NSURL URLWithString:theURL relativeToURL:[self url]]]; - [externalResourceRequest setRequestHeaders:[self requestHeaders]]; - [externalResourceRequest setDownloadCache:[self downloadCache]]; - [externalResourceRequest setCachePolicy:[self cachePolicy]]; - [externalResourceRequest setCacheStoragePolicy:[self cacheStoragePolicy]]; - [externalResourceRequest setUserInfo:[NSDictionary dictionaryWithObject:theURL forKey:@"Path"]]; - [externalResourceRequest setParentRequest:self]; - [externalResourceRequest setUrlReplacementMode:[self urlReplacementMode]]; - [externalResourceRequest setShouldResetDownloadProgress:NO]; - [externalResourceRequest setDelegate:self]; - [externalResourceRequest setUploadProgressDelegate:self]; - [externalResourceRequest setDownloadProgressDelegate:self]; - if ([self downloadDestinationPath]) { - [externalResourceRequest setDownloadDestinationPath:[self cachePathForRequest:externalResourceRequest]]; - } - [[self externalResourceQueue] addOperation:externalResourceRequest]; - } - [[self externalResourceQueue] go]; -} - -- (const char *)encodingName -{ - xmlCharEncoding encoding = XML_CHAR_ENCODING_NONE; - switch ([self responseEncoding]) - { - case NSASCIIStringEncoding: - encoding = XML_CHAR_ENCODING_ASCII; - break; - case NSJapaneseEUCStringEncoding: - encoding = XML_CHAR_ENCODING_EUC_JP; - break; - case NSUTF8StringEncoding: - encoding = XML_CHAR_ENCODING_UTF8; - break; - case NSISOLatin1StringEncoding: - encoding = XML_CHAR_ENCODING_8859_1; - break; - case NSShiftJISStringEncoding: - encoding = XML_CHAR_ENCODING_SHIFT_JIS; - break; - case NSISOLatin2StringEncoding: - encoding = XML_CHAR_ENCODING_8859_2; - break; - case NSISO2022JPStringEncoding: - encoding = XML_CHAR_ENCODING_2022_JP; - break; - case NSUTF16BigEndianStringEncoding: - encoding = XML_CHAR_ENCODING_UTF16BE; - break; - case NSUTF16LittleEndianStringEncoding: - encoding = XML_CHAR_ENCODING_UTF16LE; - break; - case NSUTF32BigEndianStringEncoding: - encoding = XML_CHAR_ENCODING_UCS4BE; - break; - case NSUTF32LittleEndianStringEncoding: - encoding = XML_CHAR_ENCODING_UCS4LE; - break; - case NSNEXTSTEPStringEncoding: - case NSSymbolStringEncoding: - case NSNonLossyASCIIStringEncoding: - case NSUnicodeStringEncoding: - case NSMacOSRomanStringEncoding: - case NSUTF32StringEncoding: - default: - encoding = XML_CHAR_ENCODING_ERROR; - break; - } - return xmlGetCharEncodingName(encoding); -} - -- (void)parseAsHTML -{ - webContentType = ASIHTMLWebContentType; - - // Only allow parsing of a single document at a time - [xmlParsingLock lock]; - - if (![requestsUsingXMLParser count]) { - xmlInitParser(); - } - [requestsUsingXMLParser addObject:self]; - - - /* Load XML document */ - if ([self downloadDestinationPath]) { - doc = htmlReadFile([[self downloadDestinationPath] cStringUsingEncoding:NSUTF8StringEncoding], [self encodingName], HTML_PARSE_NONET | HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR); - } else { - NSData *data = [self responseData]; - doc = htmlReadMemory([data bytes], (int)[data length], "", [self encodingName], HTML_PARSE_NONET | HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR); - } - if (doc == NULL) { - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:101 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Error: unable to parse reponse XML",NSLocalizedDescriptionKey,nil]]]; - return; - } - - [self setResourceList:[NSMutableDictionary dictionary]]; - - // Populate the list of URLS to download - [self readResourceURLs]; - - if ([self error] || ![[self resourceList] count]) { - [requestsUsingXMLParser removeObject:self]; - xmlFreeDoc(doc); - doc = NULL; - } - - [xmlParsingLock unlock]; - - if ([self error]) { - return; - } else if (![[self resourceList] count]) { - [super requestFinished]; - [super markAsFinished]; - return; - } - - // Create a new request for every item in the queue - [[self externalResourceQueue] cancelAllOperations]; - [self setExternalResourceQueue:[ASINetworkQueue queue]]; - [[self externalResourceQueue] setDelegate:self]; - [[self externalResourceQueue] setShouldCancelAllRequestsOnFailure:[self shouldIgnoreExternalResourceErrors]]; - [[self externalResourceQueue] setShowAccurateProgress:[self showAccurateProgress]]; - [[self externalResourceQueue] setQueueDidFinishSelector:@selector(finishedFetchingExternalResources:)]; - [[self externalResourceQueue] setRequestDidFinishSelector:@selector(externalResourceFetchSucceeded:)]; - [[self externalResourceQueue] setRequestDidFailSelector:@selector(externalResourceFetchFailed:)]; - for (NSString *theURL in [[self resourceList] keyEnumerator]) { - ASIWebPageRequest *externalResourceRequest = [ASIWebPageRequest requestWithURL:[NSURL URLWithString:theURL relativeToURL:[self url]]]; - [externalResourceRequest setRequestHeaders:[self requestHeaders]]; - [externalResourceRequest setDownloadCache:[self downloadCache]]; - [externalResourceRequest setCachePolicy:[self cachePolicy]]; - [externalResourceRequest setCacheStoragePolicy:[self cacheStoragePolicy]]; - [externalResourceRequest setUserInfo:[NSDictionary dictionaryWithObject:theURL forKey:@"Path"]]; - [externalResourceRequest setParentRequest:self]; - [externalResourceRequest setUrlReplacementMode:[self urlReplacementMode]]; - [externalResourceRequest setShouldResetDownloadProgress:NO]; - [externalResourceRequest setDelegate:self]; - [externalResourceRequest setUploadProgressDelegate:self]; - [externalResourceRequest setDownloadProgressDelegate:self]; - if ([self downloadDestinationPath]) { - [externalResourceRequest setDownloadDestinationPath:[self cachePathForRequest:externalResourceRequest]]; - } - [[self externalResourceQueue] addOperation:externalResourceRequest]; - } - [[self externalResourceQueue] go]; -} - -- (void)externalResourceFetchSucceeded:(ASIHTTPRequest *)externalResourceRequest -{ - NSString *originalPath = [[externalResourceRequest userInfo] objectForKey:@"Path"]; - NSMutableDictionary *requestResponse = [[self resourceList] objectForKey:originalPath]; - NSString *contentType = [[externalResourceRequest responseHeaders] objectForKey:@"Content-Type"]; - if (!contentType) { - contentType = @"application/octet-stream"; - } - [requestResponse setObject:contentType forKey:@"ContentType"]; - if ([self downloadDestinationPath]) { - [requestResponse setObject:[externalResourceRequest downloadDestinationPath] forKey:@"DataPath"]; - } else { - NSData *data = [externalResourceRequest responseData]; - if (data) { - [requestResponse setObject:data forKey:@"Data"]; - } - } -} - -- (void)externalResourceFetchFailed:(ASIHTTPRequest *)externalResourceRequest -{ - if ([[self externalResourceQueue] shouldCancelAllRequestsOnFailure]) { - [self failWithError:[externalResourceRequest error]]; - } -} - -- (void)finishedFetchingExternalResources:(ASINetworkQueue *)queue -{ - if ([self urlReplacementMode] != ASIDontModifyURLs) { - if (webContentType == ASICSSWebContentType) { - NSMutableString *parsedResponse; - NSError *err = nil; - if ([self downloadDestinationPath]) { - parsedResponse = [NSMutableString stringWithContentsOfFile:[self downloadDestinationPath] encoding:[self responseEncoding] error:&err]; - } else { - parsedResponse = [[[self responseString] mutableCopy] autorelease]; - } - if (err) { - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:101 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Error: unable to read response CSS from disk",NSLocalizedDescriptionKey,nil]]]; - return; - } - if (![self error]) { - for (NSString *resource in [[self resourceList] keyEnumerator]) { - if ([parsedResponse rangeOfString:resource].location != NSNotFound) { - NSString *newURL = [self contentForExternalURL:resource]; - if (newURL) { - [parsedResponse replaceOccurrencesOfString:resource withString:newURL options:0 range:NSMakeRange(0, [parsedResponse length])]; - } - } - } - } - if ([self downloadDestinationPath]) { - [parsedResponse writeToFile:[self downloadDestinationPath] atomically:NO encoding:[self responseEncoding] error:&err]; - if (err) { - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:101 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Error: unable to write response CSS to disk",NSLocalizedDescriptionKey,nil]]]; - return; - } - } else { - [self setRawResponseData:(id)[parsedResponse dataUsingEncoding:[self responseEncoding]]]; - } - } else { - [xmlParsingLock lock]; - - [self updateResourceURLs]; - - if (![self error]) { - - // We'll use the xmlsave API so we can strip the xml declaration - xmlSaveCtxtPtr saveContext; - - if ([self downloadDestinationPath]) { - - // Truncate the file first - [[[[NSFileManager alloc] init] autorelease] createFileAtPath:[self downloadDestinationPath] contents:nil attributes:nil]; - - saveContext = xmlSaveToFd([[NSFileHandle fileHandleForWritingAtPath:[self downloadDestinationPath]] fileDescriptor],NULL,2); // 2 == XML_SAVE_NO_DECL, this isn't declared on Mac OS 10.5 - xmlSaveDoc(saveContext, doc); - xmlSaveClose(saveContext); - - } else { - #if TARGET_OS_MAC && MAC_OS_X_VERSION_MAX_ALLOWED <= __MAC_10_5 - // xmlSaveToBuffer() is not implemented in the 10.5 version of libxml - NSString *tempPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; - [[[[NSFileManager alloc] init] autorelease] createFileAtPath:tempPath contents:nil attributes:nil]; - saveContext = xmlSaveToFd([[NSFileHandle fileHandleForWritingAtPath:tempPath] fileDescriptor],NULL,2); // 2 == XML_SAVE_NO_DECL, this isn't declared on Mac OS 10.5 - xmlSaveDoc(saveContext, doc); - xmlSaveClose(saveContext); - [self setRawResponseData:[NSMutableData dataWithContentsOfFile:tempPath]]; - #else - xmlBufferPtr buffer = xmlBufferCreate(); - saveContext = xmlSaveToBuffer(buffer,NULL,2); // 2 == XML_SAVE_NO_DECL, this isn't declared on Mac OS 10.5 - xmlSaveDoc(saveContext, doc); - xmlSaveClose(saveContext); - [self setRawResponseData:[[[NSMutableData alloc] initWithBytes:buffer->content length:buffer->use] autorelease]]; - xmlBufferFree(buffer); - #endif - } - - // Strip the content encoding if the original response was gzipped - if ([self isResponseCompressed]) { - NSMutableDictionary *headers = [[[self responseHeaders] mutableCopy] autorelease]; - [headers removeObjectForKey:@"Content-Encoding"]; - [self setResponseHeaders:headers]; - } - } - - xmlFreeDoc(doc); - doc = nil; - - [requestsUsingXMLParser removeObject:self]; - if (![requestsUsingXMLParser count]) { - xmlCleanupParser(); - } - [xmlParsingLock unlock]; - } - } - if (![self parentRequest]) { - [[self class] updateProgressIndicator:&downloadProgressDelegate withProgress:contentLength ofTotal:contentLength]; - } - - NSMutableDictionary *newHeaders = [[[self responseHeaders] mutableCopy] autorelease]; - [newHeaders removeObjectForKey:@"Content-Encoding"]; - [self setResponseHeaders:newHeaders]; - - // Write the parsed content back to the cache - if ([self urlReplacementMode] != ASIDontModifyURLs) { - [[self downloadCache] storeResponseForRequest:self maxAge:[self secondsToCache]]; - } - - [super requestFinished]; - [super markAsFinished]; -} - -- (void)readResourceURLs -{ - // Create xpath evaluation context - xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc); - if(xpathCtx == NULL) { - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:101 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Error: unable to create new XPath context",NSLocalizedDescriptionKey,nil]]]; - return; - } - - // Evaluate xpath expression - xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx); - if(xpathObj == NULL) { - xmlXPathFreeContext(xpathCtx); - [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:101 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Error: unable to evaluate XPath expression!",NSLocalizedDescriptionKey,nil]]]; - return; - } - - // Now loop through our matches - xmlNodeSetPtr nodes = xpathObj->nodesetval; - - int size = (nodes) ? nodes->nodeNr : 0; - int i; - for(i = size - 1; i >= 0; i--) { - assert(nodes->nodeTab[i]); - NSString *parentName = [NSString stringWithCString:(char *)nodes->nodeTab[i]->parent->name encoding:[self responseEncoding]]; - NSString *nodeName = [NSString stringWithCString:(char *)nodes->nodeTab[i]->name encoding:[self responseEncoding]]; - - xmlChar *nodeValue = xmlNodeGetContent(nodes->nodeTab[i]); - NSString *value = [NSString stringWithCString:(char *)nodeValue encoding:[self responseEncoding]]; - xmlFree(nodeValue); - - // Our xpath query matched all elements, but we're only interested in stylesheets - // We do the work here rather than in the xPath query because the query is case-sensitive, and we want to match on 'stylesheet', 'StyleSHEEt' etc - if ([[parentName lowercaseString] isEqualToString:@"link"]) { - xmlChar *relAttribute = xmlGetNoNsProp(nodes->nodeTab[i]->parent,(xmlChar *)"rel"); - if (relAttribute) { - NSString *rel = [NSString stringWithCString:(char *)relAttribute encoding:[self responseEncoding]]; - xmlFree(relAttribute); - if ([[rel lowercaseString] isEqualToString:@"stylesheet"]) { - [self addURLToFetch:value]; - } - } - - // Parse the content of - - -

AsyncSocket Documentation

- -

AsyncSocket is a TCP/IP socket networking library, designed to efficiently handle packet data. The library is in two files and one public Cocoa class.

-

The library is public domain, originally written by Dustin Voss, and now maintained by Deusty and the Cocoa community.

-

For support, visit the CocoaAsyncSocket Google code page at http://code.google.com/p/cocoaasyncsocket/

- -

Contents

-
    -
  1. Introduction - -
  2. -
  3. Socket Basics - -
  4. -
  5. Using AsyncSocket - -
  6. -
  7. AsyncSocket Reference - -
  8. -
  9. API Changes
  10. -
  11. API Index
  12. -
- - -

About AsyncSocket

-

- The Cocoa API provides a handful of networking classes, suitable for downloading and uploading images and files. - These classes support HTML, FTP, and other protocols, but cannot be used with application-specific protocols. - Without low-level socket classes supporting application-specific protocols, - developers must custom-code networking solutions using BSD or Carbon functions in conjunction with - NSFileHandle, - NSInputStream, and - NSOutputStream. - These functions and classes are not optimized for TCP/IP networking in a real Cocoa application, and can be difficult to integrate. -

-

- AsyncSocket provides easy-to-integrate “fire and forget” networking that makes it easy for your application to support networking. - Features include:

-
    -
  • - Queued non-blocking reads and writes, with optional timeouts. - You tell AsyncSocket what to read or write and get out of the way. - It will call you when it's done. -
  • -
  • - Automatic socket acceptance. If you tell AsyncSocket to accept connections, it will call you with new instances of itself for each connection. - You can, of course, disconnect them immediately. -
  • -
  • - Delegate support. Errors, connections, accepts, read completions, write completions, and disconnections all result in a message to your delegate. -
  • -
  • - Run-loop based, not thread based. Although you can use AsyncSocket on main or worker threads, you are not forced to do so. - AsyncSocket will sent messages to the delegate asynchronously via the run-loop. - The messages include a socket argument, allowing you to distinguish between many instances of AsyncSocket. -
  • -
  • - Self-contained in one class. You do not need to muck around with a collection of stream or socket instances. The class handles all of that. -
  • -
  • - Support for TCP streams. AsyncSocket does not support UDP or multicast sockets. -
  • -
  • - Based on Apple’s own CFSocket and CFStream Carbon APIs. -
  • -
- - -

About This Document

-

This document assumes the reader already has the general public’s understanding of networking, and a developer’s understanding of Cocoa and Objective-C programming.

- - -

Socket Basics

-

In networking parlance, a computer is a host for a number of sockets. A socket is one end of a communication channel called a network connection; the other end is another socket. From its own point of view, any socket is the local socket, and the socket at the other end of the connection is the remote socket.

-

To establish the connection, one of the two sockets, the connect socket, must contact the other socket, the listen socket, and the listen socket must accept the connection. To contact the listen socket, the connect socket must know its socket address. Every socket has a socket address. The address consists of two parts: the host address and the port number. The host address is the IP address of the computer, and the port number uniquely identifies each socket hosted on the computer.

-

A computer can have multiple host addresses. It will have a pair of addresses for each possible connection method (e.g., an Ethernet card, a modem, an AirPort card, a VPN connection) and a pair for connecting to itself (called “localhost”). One address of each pair is an IPv4 address such as “192.168.3.1,” and the other is an IPv6 address such as “fe80::230:65ff:fe29:aa9d.”

-

An address such as “www.google.com” corresponds to a host address, but it is not a host address itself. It is a DNS address or DNS name, which is converted to a host address by a DNS look-up operation. A URL such as “http://www.google.com:80” is likewise not a host address. URLs can include a DNS name, host address, port number, and other information.

-

Applications running on different hosts, or even on the same host, can use sockets to communicate with each other. Looking at it another way, each socket provides a communication service to its client application. The applications send and receive data to and from each other, which they can interpret and act upon. The data is composed of bytes, arranged into groups called packets and sent and received in accordance with a protocol followed by both applications.

-

A protocol establishes the structure of each packet, the circumstances under which any particular packet should be sent, and rules to handle exceptional circumstances. It also establishes roles for each client application. In a client-server architecture, some applications (the servers) provide a service used by other applications (the clients). In a peer-to-peer architecture, some applications (the peers) act as clients and servers at the same time.

- - -

Socket Limitations

-

In some ways, a socket is like a file. Both contain data with a beginning and an end. Both can be written to or read from. But in other ways they differ, and these differences drive the design of a protocol.

-

First, while a file is typically for either reading or writing, a socket is interactive. Applications must be able to interrupt and alert each other, changing each others’ behavior. For this reason, data is divided up into packets, and this division leaves an opening for interruptions.

-

Second, while a file has a known size, a socket does not have a size. An application cannot know how much data is left in the current packet, unless the protocol itself specifies this. Thus, all packets include implicit or explicit size information or markers to indicate when the packet is finished.

-

Third, while a file is reliable, a socket is not reliable. When you read from a socket, the data arrives in chunks, with possibly large delays between each chunk, and there is no way to know whether a particular delay is because of high traffic or because of an unexpected disconnection. So applications are forced to treat a long delay as if it was a disconnection, and protocols define time-outs and retry messages to regulate this.

-

The AsyncSocket library was designed to make these protocol considerations easy to deal with.

- - -

Packet Structure

-

Network communication protocols employ certain basic elements common to all protocols. These are:

-
    -
  • -

    A field. This is the fundamental component of a packet. A field is a sequence of bytes, usually short, that are interpreted as a unit. A field may be represent a number, a character or sequence of characters, binary data, an enumeration, or a series of bit flags.

    -

    If field is numeric and more than one byte long, the protocol must specify whether the bytes of the number should be arranged in “little-endian,” “big-endian,” or “network” byte order. Carbon and Cocoa both provide byte-order functions that can handle different byte orders.

    -

    A field may be fixed-length or variable-length. The length of a fixed-length field is specified by the protocol. The length of a variable-length field is specified either explicitly or implicitly. In the former case, the variable-length field (the data field) will be preceded by a fixed-length numeric field (the length field) that specifies the length of the data field. In the latter case, a byte sequence (the delimiter) will mark the end of the field.

    -
  • -
  • -

    A line of text. This is character data, typically encoded as ASCII. A line-ending marks the end of the line. There are three commonly-used line endings: CR, LF, and CRLF. The protocol must specify which is to be used. Many text-based protocols sub-divide a line of text into variable-length fields delimited by spaces and other characters.

    -
  • -
  • -

    A packet, consisting of a packet header and a payload. The packet header contains fixed-length fields describing the type (and possibly length) of the payload. The payload contains a series of variable-length and fixed-length fields, which vary according to the payload type.

    -
  • -
  • -

    A data stream, a continuous sequence of bytes. The end of the data stream is marked by the closing of the connection. A data stream can be considered one enormous unstructured packet. When an application is transmitting a data stream, protocols typically do not allow the receiving application to interrupt the transmitting application.

    -

    A data stream may be preceded by a data stream header, a series of variable-length and fixed-length fields that describe the data stream.

    -
  • -
-

Parsing Packets” describes how to use AsyncSocket methods to read these different elements.

- - -

Using AsyncSocket

-

The AsyncSocket library is composed of one class, also called AsyncSocket. An instance of AsyncSocket represents one socket, which may be a listen socket or a connect socket. An instance may also be disconnected, in which case it does not represent a functioning socket. Throughout this document, the terms “socket” and “instance of AsyncSocket” are used almost interchangeably.

-

To use the AsyncSocket class in your project, add AsyncSocket.m and /System/Library/Frameworks/CoreServices.framework to the project, and import AsyncSocket.h into each file that needs it.

-

This version of AsyncSocket requires Mac OS X 10.4 or later. If you must support Mac OS X 10.3, you may use AsyncSocket version 3.13.

- - -

Socket Ownership

-

In a client-server architecture, an application acting as a client usually employs one connect socket to communicate with a server, and an application acting as a server usually employs one listen socket to accept connections from clients and several connect sockets to communicate with the connected clients. In a peer-to-peer architecture, an application usually employs one listen socket and several connect sockets as if it were a server.

-

Each socket should be managed by a connection controller class. This class should be responsible for:

-
    -
  • Owning the local socket of the network connection.
  • -
  • Constructing and writing outgoing packets.
  • -
  • Reading and parsing incoming packets.
  • -
  • Detecting and handling error conditions.
  • -
-

A collection of connected sockets should be managed by a connection array controller. This class should be responsible for creating and destroying individual connection controllers as needed. Each managed connection controller should keep the connection array controller apprised of its status.

-

If an application has a listen socket, it should be owned and managed by the connection array controller. When the listen socket accepts a connection, the connection array controller should construct a new connection controller responsible for managing the new connection.

- - -

Delegate Methods

-

An instance of AsyncSocket sends messages to the its delegate object upon completing certain operations or encountering certain errors. All instances of AsyncSocket should have a delegate that responds to these messages appropriately. The delegate object should be the socket’s connection controller or connection array controller.

-

The delegate object should implement the following delegate methods according to the socket’s purpose (see “AsyncSocket Reference” below for detailed descriptions):

- -

You will seldom need to change a socket’s delegate, but should the need arise, be careful. If a socket completes any read or write operations initiated under the old delegate, the new delegate will be notified, not the old delegate. You can check for pending read or write operations by sending -canSafelySetDelegate: to the socket. See “Reading and Writing” below for a discussion of pending read or write operations.

-

Several instances of AsyncSocket can safely share one delegate object. Each instance passes itself as the first argument of any delegate message it sends. This allows the delegate object to distinguish between AsyncSocket instances.

- - -

Accepting, Connecting, and Disconnecting

-

To initiate a connection to a remote socket at a given socket address, send -connectToHost:onPort:error: to the socket, passing the host address and port as arguments. The host address can be an IP address or DNS name, including “localhost.” A DNS name does not include a port number.

-

To set up a listen socket for a given port, send -acceptOnPort:error: to the socket. The socket will listen on all available host addresses. To set up a socket that listens on only one address, send -acceptOnAddress:port:error:. To optionally direct an incoming connection to a particular thread, implement -onSocket:wantsRunLoopForNewSocket: in the socket’s delegate.

-

To alter the properties of the socket’s underlying CFReadStream or CFWriteStream objects (for example, to support SSL connections) implement -onSocketWillConnect: in the socket’s delegate.

-

To disconnect the socket cleanly, send -disconnectAfterWriting to the socket. This method will complete all write operations before disconnecting. To disconnect immediately, send -disconnect. In either case, the socket will send -onSocketDidDisconnect: to the delegate after it finishes disconnecting.

-

If the remote socket cleanly disconnects from the local socket, the local socket will send -onSocket:willDisconnectWithError: to the delegate. The message’s error argument will be nil (see “Error Handling” below). The local socket will then send -onSocketDidDisconnect: to the delegate.

- - -

Reading and Writing

-

AsyncSocket handles reading and writing operations itself. You specify the operation you desire, and AsyncSocket carries it out as soon as possible and sends a message to the delegate when the operation is complete.

-

AsyncSocket supports three reading operations (“read-to-length,” “read-to-data,” and “read-any”) and one write operation (“write-data”).

-
    -
  • -

    The “read-to-length” operation reads a certain number of bytes from the remote socket. To perform this operation, send -readDataToLength:withTimeout:tag: to the socket.

    -
  • -
  • -

    The “read-to-data” operation reads all bytes up to (and including) a delimiter sequence. To perform this operation, send -readDataToData:withTimeout:tag: to the socket.

    -
  • -
  • -

    The “read-any” operation reads the first available bytes. To perform this operation, send -readDataWithTimeout:tag: to the socket.

    -
  • -
  • -

    The “write-data” operation writes a data object to the remote socket. To perform this operation, send -writeData:withTimeout:tag: to the socket.

    -
  • -
-

Invoking a read or write method will perform the requested operation immediately, if possible. But if the operation cannot be completed immediately, it will be placed in a read or write queue. AsyncSocket will continue to execute the queued operations in sequential order in the background.

-

To check the progress of the currently-executing “read-to-length” or “write-data” operation, you may send -progressOfReadReturningTag:bytesDone:total: or -progressOfWriteReturningTag:bytesDone:total: to the socket. These methods return a decimal number between 0.0 and 1.0, and you may also get the number of bytes read or written of the total. -

The read and write operations support optional time-outs. The time-out value is a decimal number of seconds, for example, 1.5. The time-out value specifies how long the operation can take once begun. A queued operation does not start running out of time until it begins execution. AsyncSocket will use the system-defined time-out value if you send a negative time-out argument.

-

If an operation runs out of time before it is completed, the socket will disconnect. If your protocol supports a mechanism to recover from a long-delayed transmission, you can supply your own “soft” time-out by using NSTimer objects instead of or in addition to AsyncSocket’s own time-out system.

-

When an operation has completed within the allotted time, the socket will send a message to its delegate (either -onSocket:didReadData:withTag: or -onSocket:didWriteDataWithTag:). The delegate object should respond appropriately, sending another read or write message to the socket as necessary.

-

To help the delegate object respond appropriately, you can supply a tag value as an argument of a read or write message. A tag value can be an integer, pointer, or object id. Tag values are not transmitted to the remote socket, and the remote socket does not transmit tag values to the local socket. But the message sent by the local socket to its delegate upon completing the operation includes the tag value you initially supplied. The tag value can be used to “remember” the circumstances under which the operation was initiated. Tag values can mark a type of operation, or specify a step of a multi-operation process.

- - -

Parsing Packets

-

To parse a packet, or to read a data stream, line of text, or data stream header, consider how to break it down into a series of simple read operations.

-

Here are some techniques you can use to read and parse the elements of a protocol:

-
    -
  • -

    Any individual fixed-length field can be read by a “read-to-length” operation. But you can also read a series of fixed-length fields all at once. The fields will be collected in a single data object. You can easily recover individual fields by using a C struct, as in this sample code:

    -
    -UInt32 field1, field2; -struct fields { UInt32 field1; UInt32 field2; }; - -struct fields *fieldSeries = (struct fields *)[receivedData bytes]; -field1 = fieldSeries->field1; -field2 = fieldSeries->field2;
    -

    If only fixed-length fields comprise a packet, you can use this technique to read an entire packet at once.

    -
  • -
  • -

    Any delimited variable-length field can be read in its entirety by a single “read-to-data” operation.

    -
  • -
  • -

    A variable-length data field preceded by a fixed-length length field can be read in two parts. Use one “read-to-length” operation to read the length field. This will tell you how long the data field is. Use another “read-to-length” operation to read the data field.

    -
  • -
  • -

    A packet can also be read in two (or more) parts. Use one “read-to-length” operation to read the entire fixed-length packet header. Extract the length of the payload from the header, and use the above techniques to read the fields of the payload.

    -
  • -
  • -

    A line of text can be treated as a variable-length field delimited by a line ending. AsyncSocket provides delimiter messages that return the CR, LF, and CRLF line endings. Your client application will receive the line of text as a data object. You can then convert the data object to a string object, and use the NSString and NSScanner classes to break the line into individual fields.

    -
  • -
  • -

    Data stream headers vary in format, but can be treated as fixed- or variable-length fields and parsed using the above techniques.

    -
  • -
  • -

    A data stream can be read with one of two techniques. The first technique is appropriate when the data in the stream must be processed as it arrives, or if you do not know the size of the data:

    -
      -
    1. -

      Send -readDataWithTimeout:tag: to the socket.

      -
    2. -
    3. -

      When data arrives, the socket will send send -onSocket:didReadData:withTag: to the delegate. Process or store the data.

      -
    4. -
    5. -

      Send -readDataWithTimeout:tag: to the socket before returning from the delegate method.

      -
    6. -
    7. -

      Repeat until the connection closes. In the delegate object’s -onSocket:willDisconnectWithError: method, send -readDataWithTimeout:tag: to the socket one last time.

      -
    8. -
    -

    The second technique can only be used when you know the size of the data stream and want the entire stream returned in a single data object:

    -
      -
    1. Send -readDataToLength:withTimeout:tag: to the socket.

    2. -
    3. Wait for the delegate object’s -onSocket:didReadData:withTag: method to be called.

    4. -
    -
  • -
- - -

Error Handling

-

- If a socket encounters an input/output error, or if a read or write operation times out, AsyncSocket assumes the connection has been broken and must be re-established. - The socket will proceed to disconnect itself. The remote socket will typically be disconnected by its own client application. -

-

- Before disconnecting the local socket, an AsyncSocket instance will - send -onSocket:willDisconnectWithError: to its delegate object. - The second argument of this message is an NSError object. - This object may contain a POSIX, Mac OS, or CFStream error code, - as indicated by the object’s -domain method. - It may also contain an AsyncSocket-specific error code. - See “Errors” below for details. -

-

- During the execution of its -onSocket:willDisconnectWithError: method, - the delegate object may retrieve all unreceived data (including data from any partially-completed read operations) by - sending -unreadData to the socket. -

-

- After the delegate object’s -onSocket:willDisconnectWithError: method returns, - the socket will be disconnected. - It will then send -onSocketDidDisconnect: to the delegate. -

- - -

Threading and Run-Loops

-

An AsyncSocket instance must be used in conjunction with a run-loop. Its run-loop activity occurs in the NSDefaultRunLoopMode mode.

-

An AsyncSocket instance may be used from a worker thread or the main thread. However, each instance should only be used from one thread, so that the instance does not invoke delegate methods from within the wrong thread context.

-

To create a connect or listen socket in a particular thread, simply create the socket in the context of that thread.

-

A listen socket creates a new socket when it accepts a connection. To ensure this new socket is created in a particular thread, return the thread’s run-loop from the listen socket’s -onSocket:wantsRunLoopForNewSocket: delegate method.

- - -

Customizing AsyncSocket

-

AsyncSocket is not intended to be sub-classed. However, since AsyncSocket is in the public domain, you may alter it as much as you like for your own projects. The source code was written to be understandable.

-

AsyncSocket uses CFSocket, CFReadStream, and CFWriteStream internally. You may access these directly and set whatever properties or behaviors you may need. The -onSocketWillConnect: delegate method is designed to facilitate this.

- - -

AsyncSocket Reference

-

See “API Index” below for an alphabetical list of messages, methods, types, and constants.

- - -

Initialization and Deallocation Messages

-

AsyncSocket does not provide an auto-released convenience initializer.

-

-init -initWithDelegate: -initWithDelegate:userData: -dealloc

- - -

-init

-

This message initializes the receiver, without setting a delegate.

- - - - - - - - - - - - - - - - - -

Syntax

-(id)init

Arguments

None.

Return Value

An instance of AsyncSocket.

Errors

None.

- - -

-initWithDelegate:

-

This message initializes the receiver, setting the delegate at the same time.

- - - - - - - - - - - - - - - - - -

Syntax

-(id)initWithDelegate:(id)delegate

Arguments

delegate

-

An object that will act as the delegate for the receiver. The delegate should implement the necessary AsyncSocketDelegate methods.

-

May be nil.

-

Return Value

-
-

An instance of AsyncSocket.

-

Errors

None.

- - -

-initWithDelegate:userData:

-

This message initializes the receiver, setting the delegate and user data at the same time. This method is the designated initializer of an AsyncSocket instance.

- - - - - - - - - - - - - - - - - - - - - -

Syntax

-(id)initWithDelegate:(id)delegate userData:(long)userData

Arguments

delegate

-

An object that will act as the delegate for the receiver. The delegate should implement the necessary AsyncSocketDelegate methods.

-

May be nil.

-

userData

-

A value that will be associated with the receiver. It may be retrieved later through a -userData: message.

-

Return Value

-
-

An instance of AsyncSocket.

-

Errors

None.

- - -

-dealloc

-

This message will deallocate the receiver, disconnecting if necessary.

- - - - - - - - - - - - - - - - - -

Syntax

-(void)dealloc

Arguments

None.

Return Value

None.

Errors

None.

- - -

User Data Messages

-

-userData -setUserData:

- - -

-userData

-

This message returns the receiver’s current user data, an arbitary value associated with the receiver.

- - - - - - - - - - - - - - - - - -

Syntax

-(long)userData

Arguments

None.

Return Value

The current user data.

Errors

None.

- - -

-setUserData:

-

This message sets the receiver’s user data, an arbitary value associated with the receiver.

- - - - - - - - - - - - - - - - - - -

Syntax

-(void)setUserData:(long)userData

Arguments

userData

-

A value that will be associated with the receiver.

-

Return Value

None.

Errors

None.

- - -

Delegation Messages

-

-delegate -setDelegate: -canSafelySetDelegate

- - -

-delegate

-

This message returns the receiver’s current delegate object.

- - - - - - - - - - - - - - - - - -

Syntax

-(id)delegate

Arguments

None.

Return Value

The current delegate object, or nil.

Errors

None.

- - -

-setDelegate:

-

This message sets the receiver’s delegate object. The delegate object is not retained.

- - - - - - - - - - - - - - - - - - -

Syntax

-(void)setDelegate:(id)delegate

Arguments

delegate

-

An instance of a class that will act as the delegate for the receiver. Should implement the necessary AsyncSocketDelegate methods.

-

May be nil.

-

Return Value

None.

Errors

None.

-

If the delegate object is changed, the old delegate object will no longer receive any messages that it may be expecting as a result of pending read or write operations that it initiated. To ensure there are no pending read or write operations, the delegate object can invoke -canSafelySetDelegate:.

- - -

-canSafelySetDelegate

-

This message can be sent to determine whether a new delegate object needs to be made aware of pending read or write operations.

- - - - - - - - - - - - - - - - - -

Syntax

-(BOOL)canSafelySetDelegate

Arguments

None.

Return Value

-

YES, if the receiver has any pending read or write operations.

-

NO, if the receiver has no pending read or write operations.

-

Errors

None.

- - -

Connection Messages

-

-connectToHost:onPort:error: -acceptOnPort:error: -acceptOnAddress:port:error: -isConnected -disconnect -disconnectAfterWriting -connectedHost -connectedPort -localHost -localPort

- -

-connectToHost:onPort:error:

-

This message establishes an outgoing connection from the receiver.

- - - - - - - - - - - - - - - - - - - - - - - - - -

Syntax

-(BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)err

Arguments

hostname

-

A DNS name or IP address to which the receiver should connect. Both IPv4 and IPv6 addresses are supported.

-

port

-
-

A port number to which the receiver should connect.

-

err

-

The address of an NSError object pointer. In the event of an error, the pointer will be set to the NSError object describing the error.

-

The sender may pass nil if it does not wish to retrieve any NSError object.

-

Return Value

-

Returns YES if the connection is successful.

-

This does not indicate that the socket is ready for use. The socket is only ready when the -onSocket:didConnectToHost:port: delegate method is called.

-

Errors

-

Returns NO and an NSError object under the following conditions:

-
    -
  • hostname is not a valid address, or there is no such address.
  • -
  • The socket cannot be created, or cannot connect to the address.
  • -
-

Returns YES and calls -onSocket:willDisconnectWithError: under the following conditions:

-
    -
  • The read and write streams could not be attached or opened.
  • -
-

Raises an AsyncSocketException if the socket is already connected or accepting connections, or if no delegate has been set.

-
-

If the receiver returns YES, it will continue to establish a connection. When the connection is successfully established, or fails to be established, the receiver will send an appropriate message to its delegate object.

-

Read and write operations may be queued before the connection is successfully established. They will be executed after the connection is complete.

- - -

-acceptOnPort:error:

-

This message establishes the receiver as a listen socket that will accept incoming connections.

- - - - - - - - - - - - - - - - - - - - - - -

Syntax

-(BOOL)acceptOnPort:(UInt16)port error:(NSError **)err

Arguments

port

-

A port number at which the receiver should accept connections.

-

err

-

The address of an NSError object pointer. In the event of an error, the pointer will be set to the NSError object describing the error.

-

The sender may pass nil if it does not wish to retrieve any NSError object.

-

Return Value

-

Returns YES if the receiver is successfully accepting connections at the specified port.

-

Errors

-

Returns NO and an NSError object if the socket cannot be created, or cannot accept connections on the specified port.

-

Raises an AsyncSocketException if the socket is already connected or accepting connections, or if no delegate has been set.

-
-

The receiver establishes a listen socket with the SO_REUSEADDR option set.

-

In the event of a connection from a remote socket, the receiver will create a new AsyncSocket instance. The new instance will have the same delegate object as the receiver, and will attempt to complete the connection to the remote socket.

-

There are three possible outcomes of the new instance’s attempt. First, the attempt could succeed. Second, the attempt could fail because a local socket could not be created. Third, the attempt could fail because of another issue.

-

If successful, the receiver will send -onSocket:didAcceptNewSocket: and -onSocket:wantsRunLoopForNewSocket: to its delegate object. At this point the delegate object can change the new instance’s delegate object or assign a run-loop. After the delegate methods return, the new instance will send -onSocket:didConnectToHost:port: to its delegate object.

-

If unsuccessful because a local socket could not be created, the new instance will be silently destroyed, and the receiver will continue to accept connections.

-

If unsuccessful for some other reason, the receiver will send -onSocket:didAcceptNewSocket: and -onSocket:wantsRunLoopForNewSocket: to its delegate object. After the delegate method returns, the new instance will send -onSocket:willDisconnectWithError: to its delegate with details about the failure condition.

- - -

-acceptOnAddress:port:error:

-

This message establishes the receiver as a listen socket that will accept incoming connections on a particular host address and port.

- - - - - - - - - - - - - - - - - - - - - - - - -

Syntax

-(BOOL)acceptOnAddress:(NSString *)hostaddr port:(UInt16)port error:(NSError **)err

Arguments

hostaddr

-

A host address at which the receiver should accept connections. The address should be an IPv4 or IPv6 address, such as “192.168.3.1” or “fe80::230:65ff:fe29:aa9d.”

-

If nil or an empty string, the effect is the same as -acceptOnPort:error: message. -

port

-

A port number at which the receiver should accept connections.

-

err

-

The address of an NSError object pointer. In the event of an error, the pointer will be set to the NSError object describing the error.

-

The sender may pass nil if it does not wish to retrieve any NSError object.

-

Return Value

-

Returns YES if the receiver is successfully accepting connections at the specified port.

-

Errors

-

Returns NO and an NSError object if the socket cannot be created, or cannot accept connections on the specified address or port.

-

Raises an AsyncSocketException if the socket is already connected or accepting connections, or if no delegate has been set.

-
-

See -acceptOnPort:error: for more information.

- - -

-isConnected

-

This message may be sent to determine whether the receiver is connected and capable of reading and writing.

- - - - - - - - - - - - - - - - - -

Syntax

-(BOOL)isConnected

Arguments

None.

Return Value

-

YES, if the receiver is connected and able to send and receive data.

-

NO, if the receiver is not connected, accepting connections, or not able to send and receive data.

-

Errors

None.

-

If the input or output streams have reached EOF, the receiver returns NO. If the input or output streams are open, but in some other error state, the receiver returns YES.

-

If the receiver is accepting incoming connections, it always returns NO.

- - -

-disconnect

-

This message immediately disconnects the receiver.

- - - - - - - - - - - - - - - - - -

Syntax

-(void)disconnect

Arguments

None.

Return Value

None.

Errors

None.

-

If the receiver was accepting incoming connections, it will no stop doing so. Any pending read or write operations are dropped.

-

After this method returns, the client application may send a -connectToHost:onPort:error:, -acceptOnPort:error:, or -acceptOnAddress:port:error: messages again.

- - -

-disconnectAfterWriting

-

This message will disconnect the receiver after all pending write operations are completed. Pending read operations will not prevent the receiver from disconnecting.

- - - - - - - - - - - - - - - - - -

Syntax

-(void)disconnectAfterWriting

Arguments

None.

Return Value

None.

Errors

None.

-

While the pending write operations are completing, the receiver will ignore any further read or write messages. Other messages may be sent as usual.

- - -

-connectedHost

-

This message returns the IP address of the connected remote socket as a string.

- - - - - - - - - - - - - - - - - -

Syntax

-(NSString *)connectedHost

Arguments

None.

Return Value

-

If the receiver is connected, an IP address.

-

If the receiver is not connected, nil.

-

Errors

None.

- - -

-connectedPort

-

This message returns the port number of the connected remote socket.

- - - - - - - - - - - - - - - - - -

Syntax

-(UInt16)connectedPort

Arguments

None.

Return Value

-

If the receiver is connected, a port number.

-

If the receiver is not connected, 0.

-

Errors

None.

- - -

-localHost

-

This method returns the local IP address of the receiver as a string.

- - - - - - - - - - - - - - - - - -

Syntax

-(NSString *)localHost

Arguments

None.

Return Value

-

If the receiver is connected, an IP address.

-

If the receiver is not connected, nil.

-

Errors

None.

-

If the computer has more than one IP address, the one in use by the receiver will be returned. If the computer is behind a NAT, the returned IP address will be a LAN address, not useable outside the LAN.

- - -

-localPort

-

This method returns the port number of the receiver.

- - - - - - - - - - - - - - - - - -

Syntax

-(UInt16)localPort

Arguments

None.

Return Value

-

If the receiver is connected, a port number.

-

If the receiver is not connected, 0.

-

Errors

None.

-

If the computer is behind a NAT, the returned port number will be a LAN address, not accurate outside the LAN.

- - -

Read and Write Messages

-

-readDataToLength:withTimeout:tag: -readDataToData:withTimeout:tag: -readDataWithTimeout:tag: -writeData:withTimeout:tag: -progressOfReadReturningTag:bytesDone:total: -progressOfWriteReturningTag:bytesDone:total:

- - -

-readDataToLength:withTimeout:tag:

-

This message queues a read operation. The receiver will read a certain number of bytes from the socket.

- - - - - - - - - - - - - - - - - - - - - - - - - - -

Syntax

-(void)readDataToLength:(CFIndex)length withTimeout:(NSTimeInterval)timeout tag:(long)tag

Arguments

length

-

Number of bytes that the receiver should read.

-

If 0, the receiver does nothing, and does not send -onSocket:didReadData:withTag: to its delegate.

-

timeout

-

The number of seconds from the start of the read operation in which the operation must complete. If the operation takes longer than this interval, the operation times out.

-

If negative, the read operation will not time out.

-

tag

-

An application-defined integer or pointer that will be sent as an argument to the -onSocket:didReadData:withTag: message sent to the delegate.

-

Return Value

-

The receiver will send -onSocket:didReadData:withTag: to the delegate object when the read operation has completed. The received data will be passed as an argument of that message.

-

Errors

-

The receiver will send -onSocket:willDisconnectWithError: to the delegate object under the following conditions:

-
    -
  • The read operation times out.
  • -
  • The receiver is disconnected from the remote socket.
  • -
  • Some other i/o error occurs.
  • -
-

The -onSocket:willDisconnectWithError: method may retrieve partially received data by sending -readDataWithTimeout:tag: to the socket.

-
-

When the bytes have been successfully received, the receiver will send -onSocket:didReadData:withTag: to its delegate object.

-

The read operation will be performed immediately if possible. If so, the receiver will send the message before returning from this method.

- - -

-readDataToData:withTimeout:tag:

-

This message queues a read operation. The receiver will read bytes until (and including) a sequence of bytes passed in the data argument. That sequence acts as a separator or delimiter.

- - - - - - - - - - - - - - - - - - - - - - - - -

Syntax

-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag

Arguments

data

-

A sequence of bytes that mark the end of the read operation.

-

If nil or empty, the receiver does nothing, and does not send -onSocket:didReadData:withTag: to its delegate.

-

timeout

-
-

The number of seconds from the start of the read operation in which the operation must complete. If the operation takes longer than this interval, the operation times out.

-

If negative, the read operation will not time out.

-

tag

-
-

An application-defined integer or pointer that will be sent as an argument to the -onSocket:didReadData:withTag: message sent to the delegate.

-

Return Value

-

The receiver will send -onSocket:didReadData:withTag: to the delegate object when the read operation has completed. The received data will be passed as an argument of that message. It will include the delimiter.

-

Errors

-

The receiver will send -onSocket:willDisconnectWithError: to the delegate object under the following conditions:

-
    -
  • The read operation times out.
  • -
  • The receiver is disconnected from the remote socket.
  • -
  • Some other i/o error occurs.
  • -
-

The -onSocket:willDisconnectWithError: method may retrieve partially received data by sending -readDataWithTimeout:tag: to the socket.

-
-

When the bytes have been successfully received, the receiver will send -onSocket:didReadData:withTag: to its delegate object, passing all the received bytes as a parameter, including the delimiting sequence.

-

The read operation will be performed immediately if possible. If so, the receiver will send the message before returning from this method.

-

Note that this method is not character-set aware. If a character should happen to be encoded to a sequence of bytes that matches the delimiting sequence, the read operation can prematurely end.

- - -

-readDataWithTimeout:tag:

-

This message queues a read operation. The receiver will retrieve the first available bytes.

- - - - - - - - - - - - - - - - - - - - - - - -

Syntax

-(void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag

Arguments

timeout

-

- The number of seconds from the start of the read operation in which the operation must complete. - If the operation takes longer than this interval, the operation times out. -

-

If negative, the read operation will not time out.

-

tag

-

An application-defined integer or pointer that will be sent as an argument to the -onSocket:didReadData:withTag: message sent to the delegate.

-

Return Value

-

- The receiver will send -onSocket:didReadData:withTag: to its delegate object - when the read operation has completed. The received data, if any, will be passed as an argument. -

-

Errors

-

An error will occur under these conditions:

-
    -
  • The read operation times out.
  • -
  • The receiver is disconnected from the remote socket.
  • -
  • Some other i/o error occurs.
  • -
-
-

- When the bytes have been successfully retrieved, the receiver will then send -onSocket:didReadData:withTag: - to its delegate object, passing all the received bytes as an argument. -

- - -

-writeData:withTimeout:tag:

-

This message queues a write operation. The receiver will write an NSData object to the socket.

- - - - - - - - - - - - - - - - - - - - - - - - - - -

Syntax

-(void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag

Arguments

data

-

The data that should be written to the remote socket.

-

If nil, the receiver does nothing, and does not send -onSocket:didWriteDataWithTag: to its delegate.

-

timeout

-

The number of seconds from the start of the write operation in which the operation must complete. If the operation takes longer than this interval, the operation times out.

-

If negative, the write operation will not time out.

-

tag

-

An application-defined integer or pointer that will be sent as an argument to the -onSocket:didWriteDataWithTag: message sent to the delegate.

-

Return Value

-

None. The receiver will send -onSocket:didWriteDataWithTag: to the delegate object when the write operation has completed. -

Errors

-

The receiver will send -onSocket:willDisconnectWithError: to the delegate object under the following conditions:

-
    -
  • The write operation times out.
  • -
  • The receiver is disconnected from the remote socket.
  • -
  • Some other i/o error occurs.
  • -
-
-

When the bytes have been successfully sent, the receiver will send -onSocket:didWriteData:withTag: to its delegate object.

-

The write operation will be performed immediately if possible. If so, the receiver will send the message before returning from this method.

- - -

-progressOfReadReturningTag:bytesDone:total:

-

This message can be sent to determine the progress of the current read operation.

- - - - - - - - - - - - - - - - - - - - - - - - - -

Syntax

-(float)progressOfReadReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total

Arguments

tag

-

The address of a variable. When this method returns, the variable will contain the tag of the current read operation. If there is no current read operation, the variable will not be changed.

-

The sender may pass NULL if it does not wish to retrieve the tag.

-

done

-

The address of a variable. When this method returns, the variable will contain the number of bytes that the current read operation has read. If there is no current read operation, the variable will not be changed.

-

The sender may pass NULL if it does not wish to retrieve the number of bytes read.

-

total

-

The address of a variable. When this method returns, the variable will contain the total number of bytes that the current read operation expects to read. This value is only meaningful if the current read operation is a “read-to-length” operation. If there is no current read operation, the variable will not be changed.

-

The sender may pass NULL if it does not wish to retrieve the total number of bytes being read.

-

Return Value

-

A decimal value.

-
    -
  • NaN indicates that there is no read operation currently being executed. Use the isnan() function to test for this return value.
  • -
  • 0.0 indicates that no bytes have been read of the total.
  • -
  • 1.0 indicates that all bytes have been read of the total, or the total number of bytes that must be read is not known.
  • -
-

Errors

None.

- - -

-progressOfWriteReturningTag:bytesDone:total:

-

This message can be sent to determine the progress of the current write operation.

- - - - - - - - - - - - - - - - - - - - - - - - - -

Syntax

-(float)progressOfWriteReturningTag(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total

Arguments

tag

-

The address of a variable. When this method returns, the variable will contain the tag of the current read operation. If there is no current read operation, the variable will not be changed.

-

The sender may pass NULL if it does not wish to retrieve the tag.

-

done

-

The address of a variable. When this method returns, the variable will contain the number of bytes that have been sent. If there is no current read operation, the variable will not be changed.

-

The sender may pass NULL if it does not wish to retrieve the number of bytes sent.

-

total

-

The address of a variable. When this method returns, the variable will contain the total number of bytes that the current write operation is sending. If there is no current read operation, the variable will not be changed.

-

The sender may pass NULL if it does not wish to retrieve the total number of bytes being sent.

-

Return Value

-

A decimal value.

-
    -
  • NaN indicates that there is no write operation currently being executed. Use the isnan() function to test for this return value.
  • -
  • 0.0 indicates that no bytes have been sent of the total.
  • -
  • 1.0 indicates that all bytes have been sent of the total.
  • -
-

Errors

None.

- - -

Delimiter Messages

-

The results of these messages are commonly-used delimiters that can be passed as the data argument of a -readDataToData:withTimeout:tag: message.

-

+CRLFData +CRData +LFData +ZeroData

- - -

+CRLFData

-

This method returns the CRLF byte sequence, the line separator for DOS and Windows.

- - - - - - - - - - - - - - - - - -

Syntax

+(NSData *)CRLFData

Arguments

None.

Return Value

-

An instance of NSData containing the bytes 0D0A.

-

Errors

None.

- - -

+CRData

-

This method returns the CR byte sequence, the line separator for Macintosh operating systems before Mac OS X.

- - - - - - - - - - - - - - - - - -

Syntax

+(NSData *)CRData

Arguments

None.

Return Value

-

An instance of NSData containing the byte 0D.

-

Errors

None.

- - -

+LFData

-

This method returns the LF byte sequence, the line separator for most Unix operating systems and Mac OS X.

- - - - - - - - - - - - - - - - - -

Syntax

+(NSData *)LFData

Arguments

None.

Return Value

-

An instance of NSData containing the byte 0A.

-

Errors

None.

- - -

+ZeroData

-

This method returns a zero byte, the delimiter for C strings.

- - - - - - - - - - - - - - - - - -

Syntax

+(NSData *)ZeroData

Arguments

None.

Return Value

-

An instance of NSData containing the byte 00.

-

Errors

None.

- - -

Debugging and Customization Messages

-

-description -getCFSocket -getCFReadStream -getCFWriteStream

- - -

-description

-

This message returns a description of the receiver suitable for debugging purposes.

- - - - - - - - - - - - - - - - - -

Syntax

-(NSString *)description

Arguments

None.

Return Value

An string describing the receiver and its status.

Errors

None.

-

The returned description does not have any newline characters and includes:

-
    -
  • the receiver’s id/address and hash values.
  • -
  • a CFSocket reference number.
  • -
  • the local and remote socket addresses.
  • -
  • the number of queued read and write operations.
  • -
  • progress of current read and write operations.
  • -
  • CFReadStream and CFWriteStream reference numbers.
  • -
  • the status of the read and write streams.
  • -
  • the connection status.
  • -
- - -

-getCFSocket

-

This message returns the internal CFSocket instance being using by the receiver, enabling access to the underlying Unix socket.

- - - - - - - - - - - - - - - - - -

Syntax

-(CFSocketRef)getCFSocket

Arguments

None.

Return Value

-

The receiver’s CFSocket reference number, or NULL if not connected or accepting connections.

-

Errors

None.

-

Do not close, read from, or write to the underlying socket.

- - -

-getCFReadStream

-

This method returns the internal CFReadStream instance being using by the receiver, enabling access to the underlying Carbon stream.

- - - - - - - - - - - - - - - - - -

Syntax

-(CFReadStreamRef)getCFReadStream

Arguments

None.

Return Value

-

The receiver’s CFReadStream reference number, or NULL if not connected or accepting connections.

-

Errors

None.

-

Do not close, read from, or write to the underlying stream.

- - -

-getCFWriteStream

-

This method returns the internal CFWriteStream instance being used by the receiver, enabling access to the underlying Carbon stream.

- - - - - - - - - - - - - - - - - -

Syntax

-(CFWriteStreamRef)getCFWriteStream

Arguments

None.

Return Value

-

The receiver’s CFWriteStream reference number, or NULL if not connected or accepting connections.

-

Errors

None.

-

Do not close, read from, or write to the underlying stream.

- - -

AsyncSocketDelegate Methods

-

Delegate objects may implement these methods to respond to AsyncSocket messages.

-

-onSocket:willDisconnectWithError: -onSocketDidDisconnect: -onSocket:didAcceptNewSocket: -onSocket:wantsRunLoopForNewSocket: -onSocketWillConnect: -onSocket:didConnectToHost:port: -onSocket:didReadData:withTag: -onSocket:didWriteDataWithTag:

- - -

-onSocket:willDisconnectWithError:

-

In the event of an error, the socket is disconnected. The socket sends this message before disconnection.

- - - - - - - - - - - - - - - - - - -

Prototype

-(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err

Arguments

sock

-

The sending AsyncSocket instance.

-

err

-

The error causing the socket to disconnect.

-

If nil, then there is no error. This typically means the socket was cleanly closed by the remote client application.

-

Condition

-

This message is sent:

-
    -
  • if there is an connection, time out, or other i/o error.
  • -
  • if the remote socket cleanly disconnects.
  • -
  • before the local socket is disconnected.
  • -
-
-

This message is primarily intended to give the receiver a chance to retrieve any remaining buffered data from the connection. The receiver may do this by sending -readDataWithTimeout:tag: to the sender.

- - -

-onSocketDidDisconnect:

-

The socket sends this message is called after it has disconnected.

- - - - - - - - - - - - - - -

Prototype

-(void)onSocketDidDisconnect:(AsyncSocket *)sock

Arguments

sock

-

The sending AsyncSocket instance.

-

Condition

This message is sent after the sender disconnects for any reason.

-

The receiver may safely release the sender in this method.

- - -

-onSocket:didAcceptNewSocket:

-

The socket sends this message to provide the receiver with a chance to save a new socket in an appropriate place.

- - - - - - - - - - - - - - - - - - -

Prototype

-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket

Arguments

sock

-

The sending AsyncSocket instance. This instance will be accepting connections.

-

newSocket

-

A new instance of AsyncSocket.

-

Condition

-

This method is sent after the sender accepts an incoming connection and creates a new instance of AsyncSocket to handle it.

-
-

When this message is sent, newSocket is not fully connected to the remote socket.

-

The receiver should assign and retain newSocket, and may also set a more appropriate delegate.

- - -

-onSocket:wantsRunLoopForNewSocket:

-

The socket sends this message to determine which thread and run-loop the new socket and its delegate’s methods should operate on. Defaults to the current run-loop.

- - - - - - - - - - - - - - - - - - - - - - -

Prototype

-(NSRunLoop *)onSocket:(AsyncSocket *)sock wantsRunLoopForNewSocket:(AsyncSocket *)newSocket

Arguments

sock

-

The sending AsyncSocket instance. This instance will be accepting connections.

-

newSocket

-

An instance of AsyncSocket.

-

Return Value

-

The instance of NSRunLoop associated with a target thread.

-

Condition

-

This method is sent after the sender accepts an incoming connection and sends the -onSocket:didAcceptNewSocket: message.

-
-

When this message is sent, newSocket is not fully connected to the remote socket.

-

The receiver should return the instance of NSRunLoop associated with a target thread. All delegate messages from newSocket will be sent in the context of that thread to that run-loop, all timers will run on that run-loop, and all processing will occur on that thread.

-

If the receiver does not implement this method, the sender will use the current thread and run-loop.

- - -

-onSocketWillConnect:

-

The socket sends this message when it is about to connect to a remote socket.

- - - - - - - - - - - - - - - - - -

Prototype

-(BOOL)onSocketWillConnect:(AsyncSocket *)sock

Argument

sock

The sender.

Return Value

-

YES if the socket should continue to connect to the remote socket. This is the default if this method is not implemented by the receiver.

-

NO to cancel the connection.

-

Condition

-

This message is sent before the sender attempts to connect to:

- -
-

This message is primarily intended to give the receiver a chance to configure properties of the internal CFReadStream or CFWriteStream instances. The receiver should return YES or NO, indicating whether the sender should continue connecting.

-

If the receiver returns NO and the connection attempt was initiated by -connectToHost:onPort:error:, that method will return NO to its sender, along with an AsyncSocketCanceledError error object.

-

If the receiver returns NO and the connection attempt was initiated by a remote socket, no error will be reported.

- - -

-onSocket:didConnectToHost:port:

-

The socket sends this message when it is connected and ready for reading or writing.

- - - - - - - - - - - - - - - - - - - - - -

Prototype

-(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port

Arguments

sock

The sender.

host

-

The remote socket’s IPv4 or IPv6 address.

-

This may differ from the IP address or DNS name sent to the sender as the hostname argument of -connectToHost:onPort:error:.

-

port

-
-

The remote socket’s port.

-

Condition

-

This message is sent after the sender has successfully connected to:

- -
-

The receiver may choose to disconnect the sender, or to queue a read or write operation.

-

The socket will perform any previously-queued read or write operations after the receiver returns from this method.

- - -

-onSocket:didReadData:withTag:

-

The sender sends this message when it successfully completes a read operation. It may send this message before the invoked read method returns, or later.

- - - - - - - - - - - - - - - - - - - - - - -

Prototype

-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData*)data withTag:(long)tag

Arguments

sock

The sender.

data

-

The received data.

-

tag

-

The tag argument passed in the read message.

-

Condition

-

This message is sent after the sender has successfully received the requested data.

-

If a read operation is unsuccessful, the sender will send a -onSocket:willDisconnectWithError: message instead.

-

The receiver should process the data and queue a read or write operation or disconnect as needed.

- - -

-onSocket:didWriteDataWithTag:

-

The sender sends this message when a write operation has successfully completed. It may send this message before the invoked write method returns, or later.

- - - - - - - - - - - - - - - - - - -

Prototype

-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag

Arguments

sock

The sender.

tag

-

The tag argument passed in the write message.

-

Condition

-

This message is sent after the sender has successfully sent the provided data.

-
-

If a write operation is unsuccessful, the sender will send a -onSocket:willDisconnectWithError: message instead.

-

The receiver should queue a read or write operation or disconnect as needed.

- - -

Errors

- - -

AsyncSocketException

-

An instance of AsyncSocket raises this exception when it receives an -acceptOnPort:error:, -acceptOnAddress:port:error:, or -connectToHost:onPort:error: message, and:

-
    -
  • it is already connected or accepting connections.
  • -
  • it has not been assigned a delegate.
  • -
- - -

AsyncSocketErrorDomain

-

This NSError domain includes the following AsyncSocketError codes:

- -

You may provide localized error messages for these codes in an AsyncSocket.strings strings file in the main bundle, using as keys “AsyncSocketNoError,” etc. Default English strings are provided.

- - -

API Changes

-

Changes since AsyncSocket 4.2:

- -

Changes since AsyncSocket 4.0:

-
    -
  • Two methods have been added to allow you to monitor the current read and write operation.

  • -
  • A new method allows the socket to only accept connections on one address.

  • -
  • A new delegate method has been added to allow the underlying streams and CFSocket to be customized.

  • -
  • An error code has been added to support that method.

  • -
- -

Changes since AsyncSocket 3.13:

-
    -
  • AsyncSocket now requires Mac OS X 10.4 or later.

  • -
  • -acceptOnPort: and -connectToHost:onPort: now take an extra error parameter. This may be set to nil if you are not interested in this feature.

  • -
  • -acceptOnPort: and -connectToHost:onPort: also raise an exception if used while the socket is already connected or accepting connections, instead of returning NO.

  • -
  • The error argument of -onSocket:willDisconnectWithError: is now an NSError object instead of a CFStreamError object.

  • -
  • A new delegate method has been added to handle the threading of incoming connections.

  • -
  • +addressFromString: has been removed. Use CFHost or NSHost instead.

  • -
  • It is now possible to distinguish disconnections caused by a read timeout from those caused by a write timeout.

  • -
  • You must add /System/Library/Frameworks/CoreServices.framework to the project; this is necessary to convert certain CFStreamError domains to NSError domains.

  • -
- - -

API Index

- - -
-
Author: Dustin Voss
- - diff --git a/AsyncSocket/AsyncSocket.h b/AsyncSocket/AsyncSocket.h deleted file mode 100755 index 68af02a..0000000 --- a/AsyncSocket/AsyncSocket.h +++ /dev/null @@ -1,511 +0,0 @@ -// -// AsyncSocket.h -// -// This class is in the public domain. -// Originally created by Dustin Voss on Wed Jan 29 2003. -// Updated and maintained by Deusty Designs and the Mac development community. -// -// http://code.google.com/p/cocoaasyncsocket/ -// - -#import - -@class AsyncSocket; -@class AsyncReadPacket; -@class AsyncWritePacket; - -extern NSString *const AsyncSocketException; -extern NSString *const AsyncSocketErrorDomain; - -enum AsyncSocketError -{ - AsyncSocketCFSocketError = kCFSocketError, // From CFSocketError enum. - AsyncSocketNoError = 0, // Never used. - AsyncSocketCanceledError, // onSocketWillConnect: returned NO. - AsyncSocketConnectTimeoutError, - AsyncSocketReadMaxedOutError, // Reached set maxLength without completing - AsyncSocketReadTimeoutError, - AsyncSocketWriteTimeoutError -}; -typedef enum AsyncSocketError AsyncSocketError; - -@interface NSObject (AsyncSocketDelegate) - -/** - * In the event of an error, the socket is closed. - * You may call "unreadData" during this call-back to get the last bit of data off the socket. - * When connecting, this delegate method may be called - * before"onSocket:didAcceptNewSocket:" or "onSocket:didConnectToHost:". -**/ -- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err; - -/** - * Called when a socket disconnects with or without error. If you want to release a socket after it disconnects, - * do so here. It is not safe to do that during "onSocket:willDisconnectWithError:". - * - * If you call the disconnect method, and the socket wasn't already disconnected, - * this delegate method will be called before the disconnect method returns. -**/ -- (void)onSocketDidDisconnect:(AsyncSocket *)sock; - -/** - * Called when a socket accepts a connection. Another socket is spawned to handle it. The new socket will have - * the same delegate and will call "onSocket:didConnectToHost:port:". -**/ -- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket; - -/** - * Called when a new socket is spawned to handle a connection. This method should return the run-loop of the - * thread on which the new socket and its delegate should operate. If omitted, [NSRunLoop currentRunLoop] is used. -**/ -- (NSRunLoop *)onSocket:(AsyncSocket *)sock wantsRunLoopForNewSocket:(AsyncSocket *)newSocket; - -/** - * Called when a socket is about to connect. This method should return YES to continue, or NO to abort. - * If aborted, will result in AsyncSocketCanceledError. - * - * If the connectToHost:onPort:error: method was called, the delegate will be able to access and configure the - * CFReadStream and CFWriteStream as desired prior to connection. - * - * If the connectToAddress:error: method was called, the delegate will be able to access and configure the - * CFSocket and CFSocketNativeHandle (BSD socket) as desired prior to connection. You will be able to access and - * configure the CFReadStream and CFWriteStream in the onSocket:didConnectToHost:port: method. -**/ -- (BOOL)onSocketWillConnect:(AsyncSocket *)sock; - -/** - * Called when a socket connects and is ready for reading and writing. - * The host parameter will be an IP address, not a DNS name. -**/ -- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port; - -/** - * Called when a socket has completed reading the requested data into memory. - * Not called if there is an error. -**/ -- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag; - -/** - * Called when a socket has read in data, but has not yet completed the read. - * This would occur if using readToData: or readToLength: methods. - * It may be used to for things such as updating progress bars. -**/ -- (void)onSocket:(AsyncSocket *)sock didReadPartialDataOfLength:(CFIndex)partialLength tag:(long)tag; - -/** - * Called when a socket has completed writing the requested data. Not called if there is an error. -**/ -- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag; - -/** - * Called when a socket has written some data, but has not yet completed the entire write. - * It may be used to for things such as updating progress bars. -**/ -- (void)onSocket:(AsyncSocket *)sock didWritePartialDataOfLength:(CFIndex)partialLength tag:(long)tag; - -/** - * Called if a read operation has reached its timeout without completing. - * This method allows you to optionally extend the timeout. - * If you return a positive time interval (> 0) the read's timeout will be extended by the given amount. - * If you don't implement this method, or return a non-positive time interval (<= 0) the read will timeout as usual. - * - * The elapsed parameter is the sum of the original timeout, plus any additions previously added via this method. - * The length parameter is the number of bytes that have been read so far for the read operation. - * - * Note that this method may be called multiple times for a single read if you return positive numbers. -**/ -- (NSTimeInterval)onSocket:(AsyncSocket *)sock - shouldTimeoutReadWithTag:(long)tag - elapsed:(NSTimeInterval)elapsed - bytesDone:(CFIndex)length; - -/** - * Called if a write operation has reached its timeout without completing. - * This method allows you to optionally extend the timeout. - * If you return a positive time interval (> 0) the write's timeout will be extended by the given amount. - * If you don't implement this method, or return a non-positive time interval (<= 0) the write will timeout as usual. - * - * The elapsed parameter is the sum of the original timeout, plus any additions previously added via this method. - * The length parameter is the number of bytes that have been written so far for the write operation. - * - * Note that this method may be called multiple times for a single write if you return positive numbers. -**/ -- (NSTimeInterval)onSocket:(AsyncSocket *)sock - shouldTimeoutWriteWithTag:(long)tag - elapsed:(NSTimeInterval)elapsed - bytesDone:(CFIndex)length; - -/** - * Called after the socket has successfully completed SSL/TLS negotiation. - * This method is not called unless you use the provided startTLS method. - * - * If a SSL/TLS negotiation fails (invalid certificate, etc) then the socket will immediately close, - * and the onSocket:willDisconnectWithError: delegate method will be called with the specific SSL error code. -**/ -- (void)onSocketDidSecure:(AsyncSocket *)sock; - -@end - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -@interface AsyncSocket : NSObject -{ - CFSocketRef theSocket4; // IPv4 accept or connect socket - CFSocketRef theSocket6; // IPv6 accept or connect socket - CFReadStreamRef theReadStream; - CFWriteStreamRef theWriteStream; - - CFRunLoopSourceRef theSource4; // For theSocket4 - CFRunLoopSourceRef theSource6; // For theSocket6 - CFRunLoopRef theRunLoop; - CFSocketContext theContext; - NSArray *theRunLoopModes; - - NSTimer *theConnectTimer; - - NSMutableArray *theReadQueue; - AsyncReadPacket *theCurrentRead; - NSTimer *theReadTimer; - NSMutableData *partialReadBuffer; - - NSMutableArray *theWriteQueue; - AsyncWritePacket *theCurrentWrite; - NSTimer *theWriteTimer; - - id theDelegate; - UInt16 theFlags; - - long theUserData; -} - -- (id)init; -- (id)initWithDelegate:(id)delegate; -- (id)initWithDelegate:(id)delegate userData:(long)userData; - -/* String representation is long but has no "\n". */ -- (NSString *)description; - -/** - * Use "canSafelySetDelegate" to see if there is any pending business (reads and writes) with the current delegate - * before changing it. It is, of course, safe to change the delegate before connecting or accepting connections. -**/ -- (id)delegate; -- (BOOL)canSafelySetDelegate; -- (void)setDelegate:(id)delegate; - -/* User data can be a long, or an id or void * cast to a long. */ -- (long)userData; -- (void)setUserData:(long)userData; - -/* Don't use these to read or write. And don't close them either! */ -- (CFSocketRef)getCFSocket; -- (CFReadStreamRef)getCFReadStream; -- (CFWriteStreamRef)getCFWriteStream; - -// Once one of the accept or connect methods are called, the AsyncSocket instance is locked in -// and the other accept/connect methods can't be called without disconnecting the socket first. -// If the attempt fails or times out, these methods either return NO or -// call "onSocket:willDisconnectWithError:" and "onSockedDidDisconnect:". - -// When an incoming connection is accepted, AsyncSocket invokes several delegate methods. -// These methods are (in chronological order): -// 1. onSocket:didAcceptNewSocket: -// 2. onSocket:wantsRunLoopForNewSocket: -// 3. onSocketWillConnect: -// -// Your server code will need to retain the accepted socket (if you want to accept it). -// The best place to do this is probably in the onSocket:didAcceptNewSocket: method. -// -// After the read and write streams have been setup for the newly accepted socket, -// the onSocket:didConnectToHost:port: method will be called on the proper run loop. -// -// Multithreading Note: If you're going to be moving the newly accepted socket to another run -// loop by implementing onSocket:wantsRunLoopForNewSocket:, then you should wait until the -// onSocket:didConnectToHost:port: method before calling read, write, or startTLS methods. -// Otherwise read/write events are scheduled on the incorrect runloop, and chaos may ensue. - -/** - * Tells the socket to begin listening and accepting connections on the given port. - * When a connection comes in, the AsyncSocket instance will call the various delegate methods (see above). - * The socket will listen on all available interfaces (e.g. wifi, ethernet, etc) -**/ -- (BOOL)acceptOnPort:(UInt16)port error:(NSError **)errPtr; - -/** - * This method is the same as acceptOnPort:error: with the additional option - * of specifying which interface to listen on. So, for example, if you were writing code for a server that - * has multiple IP addresses, you could specify which address you wanted to listen on. Or you could use it - * to specify that the socket should only accept connections over ethernet, and not other interfaces such as wifi. - * You may also use the special strings "localhost" or "loopback" to specify that - * the socket only accept connections from the local machine. - * - * To accept connections on any interface pass nil, or simply use the acceptOnPort:error: method. -**/ -- (BOOL)acceptOnInterface:(NSString *)interface port:(UInt16)port error:(NSError **)errPtr; - -/** - * Connects to the given host and port. - * The host may be a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2") -**/ -- (BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr; - -/** - * This method is the same as connectToHost:onPort:error: with an additional timeout option. - * To not time out use a negative time interval, or simply use the connectToHost:onPort:error: method. -**/ -- (BOOL)connectToHost:(NSString *)hostname - onPort:(UInt16)port - withTimeout:(NSTimeInterval)timeout - error:(NSError **)errPtr; - -/** - * Connects to the given address, specified as a sockaddr structure wrapped in a NSData object. - * For example, a NSData object returned from NSNetservice's addresses method. - * - * If you have an existing struct sockaddr you can convert it to a NSData object like so: - * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; - * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; -**/ -- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr; - -/** - * This method is the same as connectToAddress:error: with an additional timeout option. - * To not time out use a negative time interval, or simply use the connectToAddress:error: method. -**/ -- (BOOL)connectToAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr; - -/** - * Disconnects immediately. Any pending reads or writes are dropped. - * If the socket is not already disconnected, the onSocketDidDisconnect delegate method - * will be called immediately, before this method returns. - * - * Please note the recommended way of releasing an AsyncSocket instance (e.g. in a dealloc method) - * [asyncSocket setDelegate:nil]; - * [asyncSocket disconnect]; - * [asyncSocket release]; -**/ -- (void)disconnect; - -/** - * Disconnects after all pending reads have completed. - * After calling this, the read and write methods will do nothing. - * The socket will disconnect even if there are still pending writes. -**/ -- (void)disconnectAfterReading; - -/** - * Disconnects after all pending writes have completed. - * After calling this, the read and write methods will do nothing. - * The socket will disconnect even if there are still pending reads. -**/ -- (void)disconnectAfterWriting; - -/** - * Disconnects after all pending reads and writes have completed. - * After calling this, the read and write methods will do nothing. -**/ -- (void)disconnectAfterReadingAndWriting; - -/* Returns YES if the socket and streams are open, connected, and ready for reading and writing. */ -- (BOOL)isConnected; - -/** - * Returns the local or remote host and port to which this socket is connected, or nil and 0 if not connected. - * The host will be an IP address. -**/ -- (NSString *)connectedHost; -- (UInt16)connectedPort; - -- (NSString *)localHost; -- (UInt16)localPort; - -/** - * Returns the local or remote address to which this socket is connected, - * specified as a sockaddr structure wrapped in a NSData object. - * - * See also the connectedHost, connectedPort, localHost and localPort methods. -**/ -- (NSData *)connectedAddress; -- (NSData *)localAddress; - -/** - * Returns whether the socket is IPv4 or IPv6. - * An accepting socket may be both. -**/ -- (BOOL)isIPv4; -- (BOOL)isIPv6; - -// The readData and writeData methods won't block. -// -// You may optionally set a timeout for any read/write operation. (To not timeout, use a negative time interval.) -// If a read/write opertion times out, the corresponding "onSocket:shouldTimeout..." delegate method -// is called to optionally allow you to extend the timeout. -// Upon a timeout, the "onSocket:willDisconnectWithError:" method is called, followed by "onSocketDidDisconnect". -// -// The tag is for your convenience. -// You can use it as an array index, step number, state id, pointer, etc., just like the socket's user data. - -/** - * This will read a certain number of bytes into memory, and call the delegate method when those bytes have been read. - * - * If the length is 0, this method does nothing and the delegate is not called. - * If the timeout value is negative, the read operation will not use a timeout. -**/ -- (void)readDataToLength:(CFIndex)length withTimeout:(NSTimeInterval)timeout tag:(long)tag; - -/** - * This reads bytes until (and including) the passed "data" parameter, which acts as a separator. - * The bytes and the separator are returned by the delegate method. - * - * If you pass nil or zero-length data as the "data" parameter, - * the method will do nothing, and the delegate will not be called. - * If the timeout value is negative, the read operation will not use a timeout. - * - * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. - * Note that this method is not character-set aware, so if a separator can occur naturally as part of the encoding for - * a character, the read will prematurely end. -**/ -- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; - -/** - * Same as readDataToData:withTimeout:tag, with the additional restriction that the amount of data read - * may not surpass the given maxLength (specified in bytes). - * - * If you pass a maxLength parameter that is less than the length of the data parameter, - * the method will do nothing, and the delegate will not be called. - * - * If the max length is surpassed, it is treated the same as a timeout - the socket is closed. - * - * Pass -1 as maxLength if no length restriction is desired, or simply use the readDataToData:withTimeout:tag method. -**/ -- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(CFIndex)length tag:(long)tag; - -/** - * Reads the first available bytes that become available on the socket. - * - * If the timeout value is negative, the read operation will not use a timeout. -**/ -- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag; - -/** - * Writes data to the socket, and calls the delegate when finished. - * - * If you pass in nil or zero-length data, this method does nothing and the delegate will not be called. - * If the timeout value is negative, the write operation will not use a timeout. -**/ -- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; - -/** - * Returns progress of current read or write, from 0.0 to 1.0, or NaN if no read/write (use isnan() to check). - * "tag", "done" and "total" will be filled in if they aren't NULL. -**/ -- (float)progressOfReadReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total; -- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total; - -/** - * Secures the connection using SSL/TLS. - * - * This method may be called at any time, and the TLS handshake will occur after all pending reads and writes - * are finished. This allows one the option of sending a protocol dependent StartTLS message, and queuing - * the upgrade to TLS at the same time, without having to wait for the write to finish. - * Any reads or writes scheduled after this method is called will occur over the secured connection. - * - * The possible keys and values for the TLS settings are well documented. - * Some possible keys are: - * - kCFStreamSSLLevel - * - kCFStreamSSLAllowsExpiredCertificates - * - kCFStreamSSLAllowsExpiredRoots - * - kCFStreamSSLAllowsAnyRoot - * - kCFStreamSSLValidatesCertificateChain - * - kCFStreamSSLPeerName - * - kCFStreamSSLCertificates - * - kCFStreamSSLIsServer - * - * Please refer to Apple's documentation for associated values, as well as other possible keys. - * - * If you pass in nil or an empty dictionary, the default settings will be used. - * - * The default settings will check to make sure the remote party's certificate is signed by a - * trusted 3rd party certificate agency (e.g. verisign) and that the certificate is not expired. - * However it will not verify the name on the certificate unless you - * give it a name to verify against via the kCFStreamSSLPeerName key. - * The security implications of this are important to understand. - * Imagine you are attempting to create a secure connection to MySecureServer.com, - * but your socket gets directed to MaliciousServer.com because of a hacked DNS server. - * If you simply use the default settings, and MaliciousServer.com has a valid certificate, - * the default settings will not detect any problems since the certificate is valid. - * To properly secure your connection in this particular scenario you - * should set the kCFStreamSSLPeerName property to "MySecureServer.com". - * If you do not know the peer name of the remote host in advance (for example, you're not sure - * if it will be "domain.com" or "www.domain.com"), then you can use the default settings to validate the - * certificate, and then use the X509Certificate class to verify the issuer after the socket has been secured. - * The X509Certificate class is part of the CocoaAsyncSocket open source project. -**/ -- (void)startTLS:(NSDictionary *)tlsSettings; - -/** - * For handling readDataToData requests, data is necessarily read from the socket in small increments. - * The performance can be much improved by allowing AsyncSocket to read larger chunks at a time and - * store any overflow in a small internal buffer. - * This is termed pre-buffering, as some data may be read for you before you ask for it. - * If you use readDataToData a lot, enabling pre-buffering will result in better performance, especially on the iPhone. - * - * The default pre-buffering state is controlled by the DEFAULT_PREBUFFERING definition. - * It is highly recommended one leave this set to YES. - * - * This method exists in case pre-buffering needs to be disabled by default for some unforeseen reason. - * In that case, this method exists to allow one to easily enable pre-buffering when ready. -**/ -- (void)enablePreBuffering; - -/** - * When you create an AsyncSocket, it is added to the runloop of the current thread. - * So for manually created sockets, it is easiest to simply create the socket on the thread you intend to use it. - * - * If a new socket is accepted, the delegate method onSocket:wantsRunLoopForNewSocket: is called to - * allow you to place the socket on a separate thread. This works best in conjunction with a thread pool design. - * - * If, however, you need to move the socket to a separate thread at a later time, this - * method may be used to accomplish the task. - * - * This method must be called from the thread/runloop the socket is currently running on. - * - * Note: After calling this method, all further method calls to this object should be done from the given runloop. - * Also, all delegate calls will be sent on the given runloop. -**/ -- (BOOL)moveToRunLoop:(NSRunLoop *)runLoop; - -/** - * Allows you to configure which run loop modes the socket uses. - * The default set of run loop modes is NSDefaultRunLoopMode. - * - * If you'd like your socket to continue operation during other modes, you may want to add modes such as - * NSModalPanelRunLoopMode or NSEventTrackingRunLoopMode. Or you may simply want to use NSRunLoopCommonModes. - * - * Accepted sockets will automatically inherit the same run loop modes as the listening socket. - * - * Note: NSRunLoopCommonModes is defined in 10.5. For previous versions one can use kCFRunLoopCommonModes. -**/ -- (BOOL)setRunLoopModes:(NSArray *)runLoopModes; - -/** - * Returns the current run loop modes the AsyncSocket instance is operating in. - * The default set of run loop modes is NSDefaultRunLoopMode. -**/ -- (NSArray *)runLoopModes; - -/** - * In the event of an error, this method may be called during onSocket:willDisconnectWithError: to read - * any data that's left on the socket. -**/ -- (NSData *)unreadData; - -/* A few common line separators, for use with the readDataToData:... methods. */ -+ (NSData *)CRLFData; // 0x0D0A -+ (NSData *)CRData; // 0x0D -+ (NSData *)LFData; // 0x0A -+ (NSData *)ZeroData; // 0x00 - -@end diff --git a/AsyncSocket/AsyncSocket.m b/AsyncSocket/AsyncSocket.m deleted file mode 100755 index 30e295e..0000000 --- a/AsyncSocket/AsyncSocket.m +++ /dev/null @@ -1,3184 +0,0 @@ -// -// AsyncSocket.m -// -// This class is in the public domain. -// Originally created by Dustin Voss on Wed Jan 29 2003. -// Updated and maintained by Deusty Designs and the Mac development community. -// -// http://code.google.com/p/cocoaasyncsocket/ -// - -#import "AsyncSocket.h" -#import -#import -#import -#import - -#if TARGET_OS_IPHONE -// Note: You may need to add the CFNetwork Framework to your project -#import -#endif - -#pragma mark Declarations - -#define DEFAULT_PREBUFFERING YES // Whether pre-buffering is enabled by default - -#define READQUEUE_CAPACITY 5 // Initial capacity -#define WRITEQUEUE_CAPACITY 5 // Initial capacity -#define READALL_CHUNKSIZE 256 // Incremental increase in buffer size -#define WRITE_CHUNKSIZE (1024 * 4) // Limit on size of each write pass - -NSString *const AsyncSocketException = @"AsyncSocketException"; -NSString *const AsyncSocketErrorDomain = @"AsyncSocketErrorDomain"; - -#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 -// Mutex lock used by all instances of AsyncSocket, to protect getaddrinfo. -// Prior to Mac OS X 10.5 this method was not thread-safe. -static NSString *getaddrinfoLock = @"lock"; -#endif - -enum AsyncSocketFlags -{ - kEnablePreBuffering = 1 << 0, // If set, pre-buffering is enabled - kDidPassConnectMethod = 1 << 1, // If set, disconnection results in delegate call - kDidCompleteOpenForRead = 1 << 2, // If set, open callback has been called for read stream - kDidCompleteOpenForWrite = 1 << 3, // If set, open callback has been called for write stream - kStartingReadTLS = 1 << 4, // If set, we're waiting for TLS negotiation to complete - kStartingWriteTLS = 1 << 5, // If set, we're waiting for TLS negotiation to complete - kForbidReadsWrites = 1 << 6, // If set, no new reads or writes are allowed - kDisconnectAfterReads = 1 << 7, // If set, disconnect after no more reads are queued - kDisconnectAfterWrites = 1 << 8, // If set, disconnect after no more writes are queued - kClosingWithError = 1 << 9, // If set, the socket is being closed due to an error - kDequeueReadScheduled = 1 << 10, // If set, a maybeDequeueRead operation is already scheduled - kDequeueWriteScheduled = 1 << 11, // If set, a maybeDequeueWrite operation is already scheduled - kSocketCanAcceptBytes = 1 << 12, // If set, we know socket can accept bytes. If unset, it's unknown. - kSocketHasBytesAvailable = 1 << 13, // If set, we know socket has bytes available. If unset, it's unknown. -}; - -@interface AsyncSocket (Private) - -// Connecting -- (void)startConnectTimeout:(NSTimeInterval)timeout; -- (void)endConnectTimeout; - -// Socket Implementation -- (CFSocketRef)newAcceptSocketForAddress:(NSData *)addr error:(NSError **)errPtr; -- (BOOL)createSocketForAddress:(NSData *)remoteAddr error:(NSError **)errPtr; -- (BOOL)attachSocketsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr; -- (BOOL)configureSocketAndReturnError:(NSError **)errPtr; -- (BOOL)connectSocketToAddress:(NSData *)remoteAddr error:(NSError **)errPtr; -- (void)doAcceptWithSocket:(CFSocketNativeHandle)newSocket; -- (void)doSocketOpen:(CFSocketRef)sock withCFSocketError:(CFSocketError)err; - -// Stream Implementation -- (BOOL)createStreamsFromNative:(CFSocketNativeHandle)native error:(NSError **)errPtr; -- (BOOL)createStreamsToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr; -- (BOOL)attachStreamsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr; -- (BOOL)configureStreamsAndReturnError:(NSError **)errPtr; -- (BOOL)openStreamsAndReturnError:(NSError **)errPtr; -- (void)doStreamOpen; -- (BOOL)setSocketFromStreamsAndReturnError:(NSError **)errPtr; - -// Disconnect Implementation -- (void)closeWithError:(NSError *)err; -- (void)recoverUnreadData; -- (void)emptyQueues; -- (void)close; - -// Errors -- (NSError *)getErrnoError; -- (NSError *)getAbortError; -- (NSError *)getStreamError; -- (NSError *)getSocketError; -- (NSError *)getConnectTimeoutError; -- (NSError *)getReadMaxedOutError; -- (NSError *)getReadTimeoutError; -- (NSError *)getWriteTimeoutError; -- (NSError *)errorFromCFStreamError:(CFStreamError)err; - -// Diagnostics -- (BOOL)isSocketConnected; -- (BOOL)areStreamsConnected; -- (NSString *)connectedHost:(CFSocketRef)socket; -- (UInt16)connectedPort:(CFSocketRef)socket; -- (NSString *)localHost:(CFSocketRef)socket; -- (UInt16)localPort:(CFSocketRef)socket; -- (NSString *)addressHost:(CFDataRef)cfaddr; -- (UInt16)addressPort:(CFDataRef)cfaddr; - -// Reading -- (void)doBytesAvailable; -- (void)completeCurrentRead; -- (void)endCurrentRead; -- (void)scheduleDequeueRead; -- (void)maybeDequeueRead; -- (void)doReadTimeout:(NSTimer *)timer; - -// Writing -- (void)doSendBytes; -- (void)completeCurrentWrite; -- (void)endCurrentWrite; -- (void)scheduleDequeueWrite; -- (void)maybeDequeueWrite; -- (void)maybeScheduleDisconnect; -- (void)doWriteTimeout:(NSTimer *)timer; - -// Run Loop -- (void)runLoopAddSource:(CFRunLoopSourceRef)source; -- (void)runLoopRemoveSource:(CFRunLoopSourceRef)source; -- (void)runLoopAddTimer:(NSTimer *)timer; -- (void)runLoopRemoveTimer:(NSTimer *)timer; -- (void)runLoopUnscheduleReadStream; -- (void)runLoopUnscheduleWriteStream; - -// Security -- (void)maybeStartTLS; -- (void)onTLSHandshakeSuccessful; - -// Callbacks -- (void)doCFCallback:(CFSocketCallBackType)type forSocket:(CFSocketRef)sock withAddress:(NSData *)address withData:(const void *)pData; -- (void)doCFReadStreamCallback:(CFStreamEventType)type forStream:(CFReadStreamRef)stream; -- (void)doCFWriteStreamCallback:(CFStreamEventType)type forStream:(CFWriteStreamRef)stream; - -@end - -static void MyCFSocketCallback(CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *); -static void MyCFReadStreamCallback(CFReadStreamRef stream, CFStreamEventType type, void *pInfo); -static void MyCFWriteStreamCallback(CFWriteStreamRef stream, CFStreamEventType type, void *pInfo); - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * The AsyncReadPacket encompasses the instructions for any given read. - * The content of a read packet allows the code to determine if we're: - * - reading to a certain length - * - reading to a certain separator - * - or simply reading the first chunk of available data -**/ -@interface AsyncReadPacket : NSObject -{ - @public - NSMutableData *buffer; - CFIndex bytesDone; - NSTimeInterval timeout; - CFIndex maxLength; - long tag; - NSData *term; - BOOL readAllAvailableData; -} -- (id)initWithData:(NSMutableData *)d - timeout:(NSTimeInterval)t - tag:(long)i - readAllAvailable:(BOOL)a - terminator:(NSData *)e - maxLength:(CFIndex)m; - -- (unsigned)readLengthForTerm; - -- (unsigned)prebufferReadLengthForTerm; -- (CFIndex)searchForTermAfterPreBuffering:(CFIndex)numBytes; -@end - -@implementation AsyncReadPacket - -- (id)initWithData:(NSMutableData *)d - timeout:(NSTimeInterval)t - tag:(long)i - readAllAvailable:(BOOL)a - terminator:(NSData *)e - maxLength:(CFIndex)m -{ - if((self = [super init])) - { - buffer = [d retain]; - timeout = t; - tag = i; - readAllAvailableData = a; - term = [e copy]; - bytesDone = 0; - maxLength = m; - } - return self; -} - -/** - * For read packets with a set terminator, returns the safe length of data that can be read - * without going over a terminator, or the maxLength. - * - * It is assumed the terminator has not already been read. -**/ -- (unsigned)readLengthForTerm -{ - NSAssert(term != nil, @"Searching for term in data when there is no term."); - - // What we're going to do is look for a partial sequence of the terminator at the end of the buffer. - // If a partial sequence occurs, then we must assume the next bytes to arrive will be the rest of the term, - // and we can only read that amount. - // Otherwise, we're safe to read the entire length of the term. - - unsigned result = [term length]; - - // Shortcut when term is a single byte - if(result == 1) return result; - - // i = index within buffer at which to check data - // j = length of term to check against - - // Note: Beware of implicit casting rules - // This could give you -1: MAX(0, (0 - [term length] + 1)); - - CFIndex i = MAX(0, (CFIndex)(bytesDone - [term length] + 1)); - CFIndex j = MIN([term length] - 1, bytesDone); - - while(i < bytesDone) - { - const void *subBuffer = [buffer bytes] + i; - - if(memcmp(subBuffer, [term bytes], j) == 0) - { - result = [term length] - j; - break; - } - - i++; - j--; - } - - if(maxLength > 0) - return MIN(result, (maxLength - bytesDone)); - else - return result; -} - -/** - * Assuming pre-buffering is enabled, returns the amount of data that can be read - * without going over the maxLength. -**/ -- (unsigned)prebufferReadLengthForTerm -{ - if(maxLength > 0) - return MIN(READALL_CHUNKSIZE, (maxLength - bytesDone)); - else - return READALL_CHUNKSIZE; -} - -/** - * For read packets with a set terminator, scans the packet buffer for the term. - * It is assumed the terminator had not been fully read prior to the new bytes. - * - * If the term is found, the number of excess bytes after the term are returned. - * If the term is not found, this method will return -1. - * - * Note: A return value of zero means the term was found at the very end. -**/ -- (CFIndex)searchForTermAfterPreBuffering:(CFIndex)numBytes -{ - NSAssert(term != nil, @"Searching for term in data when there is no term."); - - // We try to start the search such that the first new byte read matches up with the last byte of the term. - // We continue searching forward after this until the term no longer fits into the buffer. - - // Note: Beware of implicit casting rules - // This could give you -1: MAX(0, 1 - 1 - [term length] + 1); - - CFIndex i = MAX(0, (CFIndex)(bytesDone - numBytes - [term length] + 1)); - - while(i + [term length] <= bytesDone) - { - const void *subBuffer = [buffer bytes] + i; - - if(memcmp(subBuffer, [term bytes], [term length]) == 0) - { - return bytesDone - (i + [term length]); - } - - i++; - } - - return -1; -} - -- (void)dealloc -{ - [buffer release]; - [term release]; - [super dealloc]; -} - -@end - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * The AsyncWritePacket encompasses the instructions for any given write. -**/ -@interface AsyncWritePacket : NSObject -{ - @public - NSData *buffer; - CFIndex bytesDone; - long tag; - NSTimeInterval timeout; -} -- (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i; -@end - -@implementation AsyncWritePacket - -- (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i -{ - if((self = [super init])) - { - buffer = [d retain]; - timeout = t; - tag = i; - bytesDone = 0; - } - return self; -} - -- (void)dealloc -{ - [buffer release]; - [super dealloc]; -} - -@end - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * The AsyncSpecialPacket encompasses special instructions for interruptions in the read/write queues. - * This class my be altered to support more than just TLS in the future. -**/ -@interface AsyncSpecialPacket : NSObject -{ - @public - NSDictionary *tlsSettings; -} -- (id)initWithTLSSettings:(NSDictionary *)settings; -@end - -@implementation AsyncSpecialPacket - -- (id)initWithTLSSettings:(NSDictionary *)settings -{ - if((self = [super init])) - { - tlsSettings = [settings copy]; - } - return self; -} - -- (void)dealloc -{ - [tlsSettings release]; - [super dealloc]; -} - -@end - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -@implementation AsyncSocket - -- (id)init -{ - return [self initWithDelegate:nil userData:0]; -} - -- (id)initWithDelegate:(id)delegate -{ - return [self initWithDelegate:delegate userData:0]; -} - -// Designated initializer. -- (id)initWithDelegate:(id)delegate userData:(long)userData -{ - if((self = [super init])) - { - theFlags = DEFAULT_PREBUFFERING ? kEnablePreBuffering : 0; - theDelegate = delegate; - theUserData = userData; - - theSocket4 = NULL; - theSource4 = NULL; - theSocket6 = NULL; - theSource6 = NULL; - theRunLoop = NULL; - theReadStream = NULL; - theWriteStream = NULL; - - theConnectTimer = nil; - - theReadQueue = [[NSMutableArray alloc] initWithCapacity:READQUEUE_CAPACITY]; - theCurrentRead = nil; - theReadTimer = nil; - - partialReadBuffer = [[NSMutableData alloc] initWithCapacity:READALL_CHUNKSIZE]; - - theWriteQueue = [[NSMutableArray alloc] initWithCapacity:WRITEQUEUE_CAPACITY]; - theCurrentWrite = nil; - theWriteTimer = nil; - - // Socket context - NSAssert(sizeof(CFSocketContext) == sizeof(CFStreamClientContext), @"CFSocketContext != CFStreamClientContext"); - theContext.version = 0; - theContext.info = self; - theContext.retain = nil; - theContext.release = nil; - theContext.copyDescription = nil; - - // Default run loop modes - theRunLoopModes = [[NSArray arrayWithObject:NSDefaultRunLoopMode] retain]; - } - return self; -} - -// The socket may been initialized in a connected state and auto-released, so this should close it down cleanly. -- (void)dealloc -{ - [self close]; - [theReadQueue release]; - [theWriteQueue release]; - [theRunLoopModes release]; - [partialReadBuffer release]; - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - [super dealloc]; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Accessors -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (long)userData -{ - return theUserData; -} - -- (void)setUserData:(long)userData -{ - theUserData = userData; -} - -- (id)delegate -{ - return theDelegate; -} - -- (void)setDelegate:(id)delegate -{ - theDelegate = delegate; -} - -- (BOOL)canSafelySetDelegate -{ - return ([theReadQueue count] == 0 && [theWriteQueue count] == 0 && theCurrentRead == nil && theCurrentWrite == nil); -} - -- (CFSocketRef)getCFSocket -{ - if(theSocket4) - return theSocket4; - else - return theSocket6; -} - -- (CFReadStreamRef)getCFReadStream -{ - return theReadStream; -} - -- (CFWriteStreamRef)getCFWriteStream -{ - return theWriteStream; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Progress -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (float)progressOfReadReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total -{ - // Check to make sure we're actually reading something right now, - // and that the read packet isn't an AsyncSpecialPacket (upgrade to TLS). - if (!theCurrentRead || ![theCurrentRead isKindOfClass:[AsyncReadPacket class]]) return NAN; - - // It's only possible to know the progress of our read if we're reading to a certain length - // If we're reading to data, we of course have no idea when the data will arrive - // If we're reading to timeout, then we have no idea when the next chunk of data will arrive. - BOOL hasTotal = (theCurrentRead->readAllAvailableData == NO && theCurrentRead->term == nil); - - CFIndex d = theCurrentRead->bytesDone; - CFIndex t = hasTotal ? [theCurrentRead->buffer length] : 0; - if (tag != NULL) *tag = theCurrentRead->tag; - if (done != NULL) *done = d; - if (total != NULL) *total = t; - float ratio = (float)d/(float)t; - return isnan(ratio) ? 1.0F : ratio; // 0 of 0 bytes is 100% done. -} - -- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total -{ - // Check to make sure we're actually writing something right now, - // and that the write packet isn't an AsyncSpecialPacket (upgrade to TLS). - if (!theCurrentWrite || ![theCurrentWrite isKindOfClass:[AsyncWritePacket class]]) return NAN; - - CFIndex d = theCurrentWrite->bytesDone; - CFIndex t = [theCurrentWrite->buffer length]; - if (tag != NULL) *tag = theCurrentWrite->tag; - if (done != NULL) *done = d; - if (total != NULL) *total = t; - return (float)d/(float)t; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Run Loop -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (void)runLoopAddSource:(CFRunLoopSourceRef)source -{ - unsigned i, count = [theRunLoopModes count]; - for(i = 0; i < count; i++) - { - CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i]; - CFRunLoopAddSource(theRunLoop, source, runLoopMode); - } -} - -- (void)runLoopRemoveSource:(CFRunLoopSourceRef)source -{ - unsigned i, count = [theRunLoopModes count]; - for(i = 0; i < count; i++) - { - CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i]; - CFRunLoopRemoveSource(theRunLoop, source, runLoopMode); - } -} - -- (void)runLoopAddTimer:(NSTimer *)timer -{ - unsigned i, count = [theRunLoopModes count]; - for(i = 0; i < count; i++) - { - CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i]; - CFRunLoopAddTimer(theRunLoop, (CFRunLoopTimerRef)timer, runLoopMode); - } -} - -- (void)runLoopRemoveTimer:(NSTimer *)timer -{ - unsigned i, count = [theRunLoopModes count]; - for(i = 0; i < count; i++) - { - CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i]; - CFRunLoopRemoveTimer(theRunLoop, (CFRunLoopTimerRef)timer, runLoopMode); - } -} - -- (void)runLoopUnscheduleReadStream -{ - unsigned i, count = [theRunLoopModes count]; - for(i = 0; i < count; i++) - { - CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i]; - CFReadStreamUnscheduleFromRunLoop(theReadStream, theRunLoop, runLoopMode); - } - CFReadStreamSetClient(theReadStream, kCFStreamEventNone, NULL, NULL); -} - -- (void)runLoopUnscheduleWriteStream -{ - unsigned i, count = [theRunLoopModes count]; - for(i = 0; i < count; i++) - { - CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i]; - CFWriteStreamUnscheduleFromRunLoop(theWriteStream, theRunLoop, runLoopMode); - } - CFWriteStreamSetClient(theWriteStream, kCFStreamEventNone, NULL, NULL); -} - - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Configuration -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * See the header file for a full explanation of pre-buffering. -**/ -- (void)enablePreBuffering -{ - theFlags |= kEnablePreBuffering; -} - -/** - * See the header file for a full explanation of this method. -**/ -- (BOOL)moveToRunLoop:(NSRunLoop *)runLoop -{ - NSAssert((theRunLoop == NULL) || (theRunLoop == CFRunLoopGetCurrent()), - @"moveToRunLoop must be called from within the current RunLoop!"); - - if(runLoop == nil) - { - return NO; - } - if(theRunLoop == [runLoop getCFRunLoop]) - { - return YES; - } - - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - theFlags &= ~kDequeueReadScheduled; - theFlags &= ~kDequeueWriteScheduled; - - if(theReadStream && theWriteStream) - { - [self runLoopUnscheduleReadStream]; - [self runLoopUnscheduleWriteStream]; - } - - if(theSource4) [self runLoopRemoveSource:theSource4]; - if(theSource6) [self runLoopRemoveSource:theSource6]; - - // We do not retain the timers - they get retained by the runloop when we add them as a source. - // Since we're about to remove them as a source, we retain now, and release again below. - [theReadTimer retain]; - [theWriteTimer retain]; - - if(theReadTimer) [self runLoopRemoveTimer:theReadTimer]; - if(theWriteTimer) [self runLoopRemoveTimer:theWriteTimer]; - - theRunLoop = [runLoop getCFRunLoop]; - - if(theReadTimer) [self runLoopAddTimer:theReadTimer]; - if(theWriteTimer) [self runLoopAddTimer:theWriteTimer]; - - // Release timers since we retained them above - [theReadTimer release]; - [theWriteTimer release]; - - if(theSource4) [self runLoopAddSource:theSource4]; - if(theSource6) [self runLoopAddSource:theSource6]; - - if(theReadStream && theWriteStream) - { - if(![self attachStreamsToRunLoop:runLoop error:nil]) - { - return NO; - } - } - - [runLoop performSelector:@selector(maybeDequeueRead) target:self argument:nil order:0 modes:theRunLoopModes]; - [runLoop performSelector:@selector(maybeDequeueWrite) target:self argument:nil order:0 modes:theRunLoopModes]; - [runLoop performSelector:@selector(maybeScheduleDisconnect) target:self argument:nil order:0 modes:theRunLoopModes]; - - return YES; -} - -/** - * See the header file for a full explanation of this method. -**/ -- (BOOL)setRunLoopModes:(NSArray *)runLoopModes -{ - NSAssert((theRunLoop == NULL) || (theRunLoop == CFRunLoopGetCurrent()), - @"setRunLoopModes must be called from within the current RunLoop!"); - - if([runLoopModes count] == 0) - { - return NO; - } - if([theRunLoopModes isEqualToArray:runLoopModes]) - { - return YES; - } - - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - theFlags &= ~kDequeueReadScheduled; - theFlags &= ~kDequeueWriteScheduled; - - if(theReadStream && theWriteStream) - { - [self runLoopUnscheduleReadStream]; - [self runLoopUnscheduleWriteStream]; - } - - if(theSource4) [self runLoopRemoveSource:theSource4]; - if(theSource6) [self runLoopRemoveSource:theSource6]; - - // We do not retain the timers - they get retained by the runloop when we add them as a source. - // Since we're about to remove them as a source, we retain now, and release again below. - [theReadTimer retain]; - [theWriteTimer retain]; - - if(theReadTimer) [self runLoopRemoveTimer:theReadTimer]; - if(theWriteTimer) [self runLoopRemoveTimer:theWriteTimer]; - - [theRunLoopModes release]; - theRunLoopModes = [runLoopModes copy]; - - if(theReadTimer) [self runLoopAddTimer:theReadTimer]; - if(theWriteTimer) [self runLoopAddTimer:theWriteTimer]; - - // Release timers since we retained them above - [theReadTimer release]; - [theWriteTimer release]; - - if(theSource4) [self runLoopAddSource:theSource4]; - if(theSource6) [self runLoopAddSource:theSource6]; - - if(theReadStream && theWriteStream) - { - // Note: theRunLoop variable is a CFRunLoop, and NSRunLoop is NOT toll-free bridged with CFRunLoop. - // So we cannot pass theRunLoop to the method below, which is expecting a NSRunLoop parameter. - // Instead we pass nil, which will result in the method properly using the current run loop. - - if(![self attachStreamsToRunLoop:nil error:nil]) - { - return NO; - } - } - - [self performSelector:@selector(maybeDequeueRead) withObject:nil afterDelay:0 inModes:theRunLoopModes]; - [self performSelector:@selector(maybeDequeueWrite) withObject:nil afterDelay:0 inModes:theRunLoopModes]; - [self performSelector:@selector(maybeScheduleDisconnect) withObject:nil afterDelay:0 inModes:theRunLoopModes]; - - return YES; -} - -- (NSArray *)runLoopModes -{ - return [[theRunLoopModes retain] autorelease]; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Accepting -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (BOOL)acceptOnPort:(UInt16)port error:(NSError **)errPtr -{ - return [self acceptOnInterface:nil port:port error:errPtr]; -} - -/** - * To accept on a certain interface, pass the address to accept on. - * To accept on any interface, pass nil or an empty string. - * To accept only connections from localhost pass "localhost" or "loopback". -**/ -- (BOOL)acceptOnInterface:(NSString *)interface port:(UInt16)port error:(NSError **)errPtr -{ - if (theDelegate == NULL) - { - [NSException raise:AsyncSocketException - format:@"Attempting to accept without a delegate. Set a delegate first."]; - } - - if (theSocket4 != NULL || theSocket6 != NULL) - { - [NSException raise:AsyncSocketException - format:@"Attempting to accept while connected or accepting connections. Disconnect first."]; - } - - // Set up the listen sockaddr structs if needed. - - NSData *address4 = nil, *address6 = nil; - if(interface == nil || ([interface length] == 0)) - { - // Accept on ANY address - struct sockaddr_in nativeAddr4; - nativeAddr4.sin_len = sizeof(struct sockaddr_in); - nativeAddr4.sin_family = AF_INET; - nativeAddr4.sin_port = htons(port); - nativeAddr4.sin_addr.s_addr = htonl(INADDR_ANY); - memset(&(nativeAddr4.sin_zero), 0, sizeof(nativeAddr4.sin_zero)); - - struct sockaddr_in6 nativeAddr6; - nativeAddr6.sin6_len = sizeof(struct sockaddr_in6); - nativeAddr6.sin6_family = AF_INET6; - nativeAddr6.sin6_port = htons(port); - nativeAddr6.sin6_flowinfo = 0; - nativeAddr6.sin6_addr = in6addr_any; - nativeAddr6.sin6_scope_id = 0; - - // Wrap the native address structures for CFSocketSetAddress. - address4 = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)]; - address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; - } - else if([interface isEqualToString:@"localhost"] || [interface isEqualToString:@"loopback"]) - { - // Accept only on LOOPBACK address - struct sockaddr_in nativeAddr4; - nativeAddr4.sin_len = sizeof(struct sockaddr_in); - nativeAddr4.sin_family = AF_INET; - nativeAddr4.sin_port = htons(port); - nativeAddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - memset(&(nativeAddr4.sin_zero), 0, sizeof(nativeAddr4.sin_zero)); - - struct sockaddr_in6 nativeAddr6; - nativeAddr6.sin6_len = sizeof(struct sockaddr_in6); - nativeAddr6.sin6_family = AF_INET6; - nativeAddr6.sin6_port = htons(port); - nativeAddr6.sin6_flowinfo = 0; - nativeAddr6.sin6_addr = in6addr_loopback; - nativeAddr6.sin6_scope_id = 0; - - // Wrap the native address structures for CFSocketSetAddress. - address4 = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)]; - address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; - } - else - { - NSString *portStr = [NSString stringWithFormat:@"%hu", port]; - -#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - @synchronized (getaddrinfoLock) -#endif - { - struct addrinfo hints, *res, *res0; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - hints.ai_flags = AI_PASSIVE; - - int error = getaddrinfo([interface UTF8String], [portStr UTF8String], &hints, &res0); - - if(error) - { - if(errPtr) - { - NSString *errMsg = [NSString stringWithCString:gai_strerror(error) encoding:NSASCIIStringEncoding]; - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:error userInfo:info]; - } - } - - for(res = res0; res; res = res->ai_next) - { - if(!address4 && (res->ai_family == AF_INET)) - { - // Found IPv4 address - // Wrap the native address structures for CFSocketSetAddress. - address4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]; - } - else if(!address6 && (res->ai_family == AF_INET6)) - { - // Found IPv6 address - // Wrap the native address structures for CFSocketSetAddress. - address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]; - } - } - freeaddrinfo(res0); - } - - if(!address4 && !address6) return NO; - } - - // Create the sockets. - - if (address4) - { - theSocket4 = [self newAcceptSocketForAddress:address4 error:errPtr]; - if (theSocket4 == NULL) goto Failed; - } - - if (address6) - { - theSocket6 = [self newAcceptSocketForAddress:address6 error:errPtr]; - - // Note: The iPhone doesn't currently support IPv6 - -#if !TARGET_OS_IPHONE - if (theSocket6 == NULL) goto Failed; -#endif - } - - // Attach the sockets to the run loop so that callback methods work - - [self attachSocketsToRunLoop:nil error:nil]; - - // Set the SO_REUSEADDR flags. - - int reuseOn = 1; - if (theSocket4) setsockopt(CFSocketGetNative(theSocket4), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn)); - if (theSocket6) setsockopt(CFSocketGetNative(theSocket6), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn)); - - // Set the local bindings which causes the sockets to start listening. - - CFSocketError err; - if (theSocket4) - { - err = CFSocketSetAddress (theSocket4, (CFDataRef)address4); - if (err != kCFSocketSuccess) goto Failed; - - //NSLog(@"theSocket4: %hu", [self localPort:theSocket4]); - } - - if(port == 0 && theSocket4 && theSocket6) - { - // The user has passed in port 0, which means he wants to allow the kernel to choose the port for them - // However, the kernel will choose a different port for both theSocket4 and theSocket6 - // So we grab the port the kernel choose for theSocket4, and set it as the port for theSocket6 - UInt16 chosenPort = [self localPort:theSocket4]; - - struct sockaddr_in6 *pSockAddr6 = (struct sockaddr_in6 *)[address6 bytes]; - pSockAddr6->sin6_port = htons(chosenPort); - } - - if (theSocket6) - { - err = CFSocketSetAddress (theSocket6, (CFDataRef)address6); - if (err != kCFSocketSuccess) goto Failed; - - //NSLog(@"theSocket6: %hu", [self localPort:theSocket6]); - } - - theFlags |= kDidPassConnectMethod; - return YES; - -Failed: - if(errPtr) *errPtr = [self getSocketError]; - if(theSocket4 != NULL) - { - CFSocketInvalidate(theSocket4); - CFRelease(theSocket4); - theSocket4 = NULL; - } - if(theSocket6 != NULL) - { - CFSocketInvalidate(theSocket6); - CFRelease(theSocket6); - theSocket6 = NULL; - } - return NO; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Connecting -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (BOOL)connectToHost:(NSString*)hostname onPort:(UInt16)port error:(NSError **)errPtr -{ - return [self connectToHost:hostname onPort:port withTimeout:-1 error:errPtr]; -} - -/** - * This method creates an initial CFReadStream and CFWriteStream to the given host on the given port. - * The connection is then opened, and the corresponding CFSocket will be extracted after the connection succeeds. - * - * Thus the delegate will have access to the CFReadStream and CFWriteStream prior to connection, - * specifically in the onSocketWillConnect: method. -**/ -- (BOOL)connectToHost:(NSString *)hostname - onPort:(UInt16)port - withTimeout:(NSTimeInterval)timeout - error:(NSError **)errPtr -{ - if(theDelegate == NULL) - { - [NSException raise:AsyncSocketException - format:@"Attempting to connect without a delegate. Set a delegate first."]; - } - - if(theSocket4 != NULL || theSocket6 != NULL) - { - [NSException raise:AsyncSocketException - format:@"Attempting to connect while connected or accepting connections. Disconnect first."]; - } - - if(![self createStreamsToHost:hostname onPort:port error:errPtr]) goto Failed; - if(![self attachStreamsToRunLoop:nil error:errPtr]) goto Failed; - if(![self configureStreamsAndReturnError:errPtr]) goto Failed; - if(![self openStreamsAndReturnError:errPtr]) goto Failed; - - [self startConnectTimeout:timeout]; - theFlags |= kDidPassConnectMethod; - - return YES; - -Failed: - [self close]; - return NO; -} - -- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr -{ - return [self connectToAddress:remoteAddr withTimeout:-1 error:errPtr]; -} - -/** - * This method creates an initial CFSocket to the given address. - * The connection is then opened, and the corresponding CFReadStream and CFWriteStream will be - * created from the low-level sockets after the connection succeeds. - * - * Thus the delegate will have access to the CFSocket and CFSocketNativeHandle (BSD socket) prior to connection, - * specifically in the onSocketWillConnect: method. - * - * Note: The NSData parameter is expected to be a sockaddr structure. For example, an NSData object returned from - * NSNetservice addresses method. - * If you have an existing struct sockaddr you can convert it to an NSData object like so: - * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; - * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; -**/ -- (BOOL)connectToAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr -{ - if (theDelegate == NULL) - { - [NSException raise:AsyncSocketException - format:@"Attempting to connect without a delegate. Set a delegate first."]; - } - - if (theSocket4 != NULL || theSocket6 != NULL) - { - [NSException raise:AsyncSocketException - format:@"Attempting to connect while connected or accepting connections. Disconnect first."]; - } - - if(![self createSocketForAddress:remoteAddr error:errPtr]) goto Failed; - if(![self attachSocketsToRunLoop:nil error:errPtr]) goto Failed; - if(![self configureSocketAndReturnError:errPtr]) goto Failed; - if(![self connectSocketToAddress:remoteAddr error:errPtr]) goto Failed; - - [self startConnectTimeout:timeout]; - theFlags |= kDidPassConnectMethod; - - return YES; - -Failed: - [self close]; - return NO; -} - -- (void)startConnectTimeout:(NSTimeInterval)timeout -{ - if(timeout >= 0.0) - { - theConnectTimer = [NSTimer timerWithTimeInterval:timeout - target:self - selector:@selector(doConnectTimeout:) - userInfo:nil - repeats:NO]; - [self runLoopAddTimer:theConnectTimer]; - } -} - -- (void)endConnectTimeout -{ - [theConnectTimer invalidate]; - theConnectTimer = nil; -} - -- (void)doConnectTimeout:(NSTimer *)timer -{ - [self endConnectTimeout]; - [self closeWithError:[self getConnectTimeoutError]]; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Socket Implementation -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Creates the accept sockets. - * Returns true if either IPv4 or IPv6 is created. - * If either is missing, an error is returned (even though the method may return true). -**/ -- (CFSocketRef)newAcceptSocketForAddress:(NSData *)addr error:(NSError **)errPtr -{ - struct sockaddr *pSockAddr = (struct sockaddr *)[addr bytes]; - int addressFamily = pSockAddr->sa_family; - - CFSocketRef theSocket = CFSocketCreate(kCFAllocatorDefault, - addressFamily, - SOCK_STREAM, - 0, - kCFSocketAcceptCallBack, // Callback flags - (CFSocketCallBack)&MyCFSocketCallback, // Callback method - &theContext); - - if(theSocket == NULL) - { - if(errPtr) *errPtr = [self getSocketError]; - } - - return theSocket; -} - -- (BOOL)createSocketForAddress:(NSData *)remoteAddr error:(NSError **)errPtr -{ - struct sockaddr *pSockAddr = (struct sockaddr *)[remoteAddr bytes]; - - if(pSockAddr->sa_family == AF_INET) - { - theSocket4 = CFSocketCreate(NULL, // Default allocator - PF_INET, // Protocol Family - SOCK_STREAM, // Socket Type - IPPROTO_TCP, // Protocol - kCFSocketConnectCallBack, // Callback flags - (CFSocketCallBack)&MyCFSocketCallback, // Callback method - &theContext); // Socket Context - - if(theSocket4 == NULL) - { - if (errPtr) *errPtr = [self getSocketError]; - return NO; - } - } - else if(pSockAddr->sa_family == AF_INET6) - { - theSocket6 = CFSocketCreate(NULL, // Default allocator - PF_INET6, // Protocol Family - SOCK_STREAM, // Socket Type - IPPROTO_TCP, // Protocol - kCFSocketConnectCallBack, // Callback flags - (CFSocketCallBack)&MyCFSocketCallback, // Callback method - &theContext); // Socket Context - - if(theSocket6 == NULL) - { - if (errPtr) *errPtr = [self getSocketError]; - return NO; - } - } - else - { - if (errPtr) *errPtr = [self getSocketError]; - return NO; - } - - return YES; -} - -/** - * Adds the CFSocket's to the run-loop so that callbacks will work properly. -**/ -- (BOOL)attachSocketsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr -{ - // Get the CFRunLoop to which the socket should be attached. - theRunLoop = (runLoop == nil) ? CFRunLoopGetCurrent() : [runLoop getCFRunLoop]; - - if(theSocket4) - { - theSource4 = CFSocketCreateRunLoopSource (kCFAllocatorDefault, theSocket4, 0); - [self runLoopAddSource:theSource4]; - } - - if(theSocket6) - { - theSource6 = CFSocketCreateRunLoopSource (kCFAllocatorDefault, theSocket6, 0); - [self runLoopAddSource:theSource6]; - } - - return YES; -} - -/** - * Allows the delegate method to configure the CFSocket or CFNativeSocket as desired before we connect. - * Note that the CFReadStream and CFWriteStream will not be available until after the connection is opened. -**/ -- (BOOL)configureSocketAndReturnError:(NSError **)errPtr -{ - // Call the delegate method for further configuration. - if([theDelegate respondsToSelector:@selector(onSocketWillConnect:)]) - { - if([theDelegate onSocketWillConnect:self] == NO) - { - if (errPtr) *errPtr = [self getAbortError]; - return NO; - } - } - return YES; -} - -- (BOOL)connectSocketToAddress:(NSData *)remoteAddr error:(NSError **)errPtr -{ - // Start connecting to the given address in the background - // The MyCFSocketCallback method will be called when the connection succeeds or fails - if(theSocket4) - { - CFSocketError err = CFSocketConnectToAddress(theSocket4, (CFDataRef)remoteAddr, -1); - if(err != kCFSocketSuccess) - { - if (errPtr) *errPtr = [self getSocketError]; - return NO; - } - } - else if(theSocket6) - { - CFSocketError err = CFSocketConnectToAddress(theSocket6, (CFDataRef)remoteAddr, -1); - if(err != kCFSocketSuccess) - { - if (errPtr) *errPtr = [self getSocketError]; - return NO; - } - } - - return YES; -} - -/** - * Attempt to make the new socket. - * If an error occurs, ignore this event. -**/ -- (void)doAcceptWithSocket:(CFSocketNativeHandle)newNative -{ - // New socket inherits same delegate and run loop modes. - // Note: We use [self class] to support subclassing AsyncSocket. - AsyncSocket *newSocket = [[[[self class] alloc] initWithDelegate:theDelegate] autorelease]; - [newSocket setRunLoopModes:theRunLoopModes]; - - if(newSocket) - { - if ([theDelegate respondsToSelector:@selector(onSocket:didAcceptNewSocket:)]) - [theDelegate onSocket:self didAcceptNewSocket:newSocket]; - - NSRunLoop *runLoop = nil; - if ([theDelegate respondsToSelector:@selector(onSocket:wantsRunLoopForNewSocket:)]) - runLoop = [theDelegate onSocket:self wantsRunLoopForNewSocket:newSocket]; - - BOOL pass = YES; - - if(pass && ![newSocket createStreamsFromNative:newNative error:nil]) pass = NO; - if(pass && ![newSocket attachStreamsToRunLoop:runLoop error:nil]) pass = NO; - if(pass && ![newSocket configureStreamsAndReturnError:nil]) pass = NO; - if(pass && ![newSocket openStreamsAndReturnError:nil]) pass = NO; - - if(pass) - newSocket->theFlags |= kDidPassConnectMethod; - else { - // No NSError, but errors will still get logged from the above functions. - [newSocket close]; - } - - } -} - -/** - * Description forthcoming... -**/ -- (void)doSocketOpen:(CFSocketRef)sock withCFSocketError:(CFSocketError)socketError -{ - NSParameterAssert ((sock == theSocket4) || (sock == theSocket6)); - - if(socketError == kCFSocketTimeout || socketError == kCFSocketError) - { - [self closeWithError:[self getSocketError]]; - return; - } - - // Get the underlying native (BSD) socket - CFSocketNativeHandle nativeSocket = CFSocketGetNative(sock); - - // Setup the socket so that invalidating the socket will not close the native socket - CFSocketSetSocketFlags(sock, 0); - - // Invalidate and release the CFSocket - All we need from here on out is the nativeSocket - // Note: If we don't invalidate the socket (leaving the native socket open) - // then theReadStream and theWriteStream won't function properly. - // Specifically, their callbacks won't work, with the exception of kCFStreamEventOpenCompleted. - // I'm not entirely sure why this is, but I'm guessing that events on the socket fire to the CFSocket we created, - // as opposed to the CFReadStream/CFWriteStream. - - CFSocketInvalidate(sock); - CFRelease(sock); - theSocket4 = NULL; - theSocket6 = NULL; - - NSError *err; - BOOL pass = YES; - - if(pass && ![self createStreamsFromNative:nativeSocket error:&err]) pass = NO; - if(pass && ![self attachStreamsToRunLoop:nil error:&err]) pass = NO; - if(pass && ![self openStreamsAndReturnError:&err]) pass = NO; - - if(!pass) - { - [self closeWithError:err]; - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Stream Implementation -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Creates the CFReadStream and CFWriteStream from the given native socket. - * The CFSocket may be extracted from either stream after the streams have been opened. - * - * Note: The given native socket must already be connected! -**/ -- (BOOL)createStreamsFromNative:(CFSocketNativeHandle)native error:(NSError **)errPtr -{ - // Create the socket & streams. - CFStreamCreatePairWithSocket(kCFAllocatorDefault, native, &theReadStream, &theWriteStream); - if (theReadStream == NULL || theWriteStream == NULL) - { - NSError *err = [self getStreamError]; - - NSLog (@"AsyncSocket %p couldn't create streams from accepted socket: %@", self, err); - - if (errPtr) *errPtr = err; - return NO; - } - - // Ensure the CF & BSD socket is closed when the streams are closed. - CFReadStreamSetProperty(theReadStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); - CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); - - return YES; -} - -/** - * Creates the CFReadStream and CFWriteStream from the given hostname and port number. - * The CFSocket may be extracted from either stream after the streams have been opened. -**/ -- (BOOL)createStreamsToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr -{ - // Create the socket & streams. - CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)hostname, port, &theReadStream, &theWriteStream); - if (theReadStream == NULL || theWriteStream == NULL) - { - if (errPtr) *errPtr = [self getStreamError]; - return NO; - } - - // Ensure the CF & BSD socket is closed when the streams are closed. - CFReadStreamSetProperty(theReadStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); - CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); - - return YES; -} - -- (BOOL)attachStreamsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr -{ - // Get the CFRunLoop to which the socket should be attached. - theRunLoop = (runLoop == nil) ? CFRunLoopGetCurrent() : [runLoop getCFRunLoop]; - - // Setup read stream callbacks - - CFOptionFlags readStreamEvents = kCFStreamEventHasBytesAvailable | - kCFStreamEventErrorOccurred | - kCFStreamEventEndEncountered | - kCFStreamEventOpenCompleted; - - if (!CFReadStreamSetClient(theReadStream, - readStreamEvents, - (CFReadStreamClientCallBack)&MyCFReadStreamCallback, - (CFStreamClientContext *)(&theContext))) - { - NSError *err = [self getStreamError]; - - NSLog (@"AsyncSocket %p couldn't attach read stream to run-loop,", self); - NSLog (@"Error: %@", err); - - if (errPtr) *errPtr = err; - return NO; - } - - // Setup write stream callbacks - - CFOptionFlags writeStreamEvents = kCFStreamEventCanAcceptBytes | - kCFStreamEventErrorOccurred | - kCFStreamEventEndEncountered | - kCFStreamEventOpenCompleted; - - if (!CFWriteStreamSetClient (theWriteStream, - writeStreamEvents, - (CFWriteStreamClientCallBack)&MyCFWriteStreamCallback, - (CFStreamClientContext *)(&theContext))) - { - NSError *err = [self getStreamError]; - - NSLog (@"AsyncSocket %p couldn't attach write stream to run-loop,", self); - NSLog (@"Error: %@", err); - - if (errPtr) *errPtr = err; - return NO; - } - - // Add read and write streams to run loop - - unsigned i, count = [theRunLoopModes count]; - for(i = 0; i < count; i++) - { - CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i]; - CFReadStreamScheduleWithRunLoop(theReadStream, theRunLoop, runLoopMode); - CFWriteStreamScheduleWithRunLoop(theWriteStream, theRunLoop, runLoopMode); - } - - return YES; -} - -/** - * Allows the delegate method to configure the CFReadStream and/or CFWriteStream as desired before we connect. - * Note that the CFSocket and CFNativeSocket will not be available until after the connection is opened. -**/ -- (BOOL)configureStreamsAndReturnError:(NSError **)errPtr -{ - // Call the delegate method for further configuration. - if([theDelegate respondsToSelector:@selector(onSocketWillConnect:)]) - { - if([theDelegate onSocketWillConnect:self] == NO) - { - if (errPtr) *errPtr = [self getAbortError]; - return NO; - } - } - return YES; -} - -- (BOOL)openStreamsAndReturnError:(NSError **)errPtr -{ - BOOL pass = YES; - - if(pass && !CFReadStreamOpen (theReadStream)) - { - NSLog (@"AsyncSocket %p couldn't open read stream,", self); - pass = NO; - } - - if(pass && !CFWriteStreamOpen (theWriteStream)) - { - NSLog (@"AsyncSocket %p couldn't open write stream,", self); - pass = NO; - } - - if(!pass) - { - if (errPtr) *errPtr = [self getStreamError]; - } - - return pass; -} - -/** - * Called when read or write streams open. - * When the socket is connected and both streams are open, consider the AsyncSocket instance to be ready. -**/ -- (void)doStreamOpen -{ - NSError *err = nil; - if ((theFlags & kDidCompleteOpenForRead) && (theFlags & kDidCompleteOpenForWrite)) - { - // Get the socket. - if (![self setSocketFromStreamsAndReturnError: &err]) - { - NSLog (@"AsyncSocket %p couldn't get socket from streams, %@. Disconnecting.", self, err); - [self closeWithError:err]; - return; - } - - // Stop the connection attempt timeout timer - [self endConnectTimeout]; - - if ([theDelegate respondsToSelector:@selector(onSocket:didConnectToHost:port:)]) - { - [theDelegate onSocket:self didConnectToHost:[self connectedHost] port:[self connectedPort]]; - } - - // Immediately deal with any already-queued requests. - [self maybeDequeueRead]; - [self maybeDequeueWrite]; - } -} - -- (BOOL)setSocketFromStreamsAndReturnError:(NSError **)errPtr -{ - // Get the CFSocketNativeHandle from theReadStream - CFSocketNativeHandle native; - CFDataRef nativeProp = CFReadStreamCopyProperty(theReadStream, kCFStreamPropertySocketNativeHandle); - if(nativeProp == NULL) - { - if (errPtr) *errPtr = [self getStreamError]; - return NO; - } - - CFDataGetBytes(nativeProp, CFRangeMake(0, CFDataGetLength(nativeProp)), (UInt8 *)&native); - CFRelease(nativeProp); - - CFSocketRef theSocket = CFSocketCreateWithNative(kCFAllocatorDefault, native, 0, NULL, NULL); - if(theSocket == NULL) - { - if (errPtr) *errPtr = [self getSocketError]; - return NO; - } - - // Determine whether the connection was IPv4 or IPv6 - CFDataRef peeraddr = CFSocketCopyPeerAddress(theSocket); - if(peeraddr == NULL) - { - NSLog(@"AsyncSocket couldn't determine IP version of socket"); - - CFRelease(theSocket); - - if (errPtr) *errPtr = [self getSocketError]; - return NO; - } - struct sockaddr *sa = (struct sockaddr *)CFDataGetBytePtr(peeraddr); - - if(sa->sa_family == AF_INET) - { - theSocket4 = theSocket; - } - else - { - theSocket6 = theSocket; - } - - CFRelease(peeraddr); - - return YES; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Disconnect Implementation -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// Sends error message and disconnects -- (void)closeWithError:(NSError *)err -{ - theFlags |= kClosingWithError; - - if (theFlags & kDidPassConnectMethod) - { - // Try to salvage what data we can. - [self recoverUnreadData]; - - // Let the delegate know, so it can try to recover if it likes. - if ([theDelegate respondsToSelector:@selector(onSocket:willDisconnectWithError:)]) - { - [theDelegate onSocket:self willDisconnectWithError:err]; - } - } - [self close]; -} - -// Prepare partially read data for recovery. -- (void)recoverUnreadData -{ - if(theCurrentRead != nil) - { - // We never finished the current read. - // Check to see if it's a normal read packet (not AsyncSpecialPacket) and if it had read anything yet. - - if(([theCurrentRead isKindOfClass:[AsyncReadPacket class]]) && (theCurrentRead->bytesDone > 0)) - { - // We need to move its data into the front of the partial read buffer. - - [partialReadBuffer replaceBytesInRange:NSMakeRange(0, 0) - withBytes:[theCurrentRead->buffer bytes] - length:theCurrentRead->bytesDone]; - } - } - - [self emptyQueues]; -} - -- (void)emptyQueues -{ - if (theCurrentRead != nil) [self endCurrentRead]; - if (theCurrentWrite != nil) [self endCurrentWrite]; - - [theReadQueue removeAllObjects]; - [theWriteQueue removeAllObjects]; - - [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueRead) object:nil]; - [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueWrite) object:nil]; - - theFlags &= ~kDequeueReadScheduled; - theFlags &= ~kDequeueWriteScheduled; -} - -/** - * Disconnects. This is called for both error and clean disconnections. -**/ -- (void)close -{ - // Empty queues - [self emptyQueues]; - - // Clear partialReadBuffer (pre-buffer and also unreadData buffer in case of error) - [partialReadBuffer replaceBytesInRange:NSMakeRange(0, [partialReadBuffer length]) withBytes:NULL length:0]; - - [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(disconnect) object:nil]; - - // Stop the connection attempt timeout timer - if (theConnectTimer != nil) - { - [self endConnectTimeout]; - } - - // Close streams. - if (theReadStream != NULL) - { - [self runLoopUnscheduleReadStream]; - CFReadStreamClose(theReadStream); - CFRelease(theReadStream); - theReadStream = NULL; - } - if (theWriteStream != NULL) - { - [self runLoopUnscheduleWriteStream]; - CFWriteStreamClose(theWriteStream); - CFRelease(theWriteStream); - theWriteStream = NULL; - } - - // Close sockets. - if (theSocket4 != NULL) - { - CFSocketInvalidate (theSocket4); - CFRelease (theSocket4); - theSocket4 = NULL; - } - if (theSocket6 != NULL) - { - CFSocketInvalidate (theSocket6); - CFRelease (theSocket6); - theSocket6 = NULL; - } - if (theSource4 != NULL) - { - [self runLoopRemoveSource:theSource4]; - CFRelease (theSource4); - theSource4 = NULL; - } - if (theSource6 != NULL) - { - [self runLoopRemoveSource:theSource6]; - CFRelease (theSource6); - theSource6 = NULL; - } - theRunLoop = NULL; - - // If the client has passed the connect/accept method, then the connection has at least begun. - // Notify delegate that it is now ending. - BOOL shouldCallDelegate = (theFlags & kDidPassConnectMethod); - - // Clear all flags (except the pre-buffering flag, which should remain as is) - theFlags &= kEnablePreBuffering; - - if (shouldCallDelegate) - { - if ([theDelegate respondsToSelector: @selector(onSocketDidDisconnect:)]) - { - [theDelegate onSocketDidDisconnect:self]; - } - } - - // Do not access any instance variables after calling onSocketDidDisconnect. - // This gives the delegate freedom to release us without returning here and crashing. -} - -/** - * Disconnects immediately. Any pending reads or writes are dropped. -**/ -- (void)disconnect -{ - [self close]; -} - -/** - * Diconnects after all pending reads have completed. -**/ -- (void)disconnectAfterReading -{ - theFlags |= (kForbidReadsWrites | kDisconnectAfterReads); - - [self maybeScheduleDisconnect]; -} - -/** - * Disconnects after all pending writes have completed. -**/ -- (void)disconnectAfterWriting -{ - theFlags |= (kForbidReadsWrites | kDisconnectAfterWrites); - - [self maybeScheduleDisconnect]; -} - -/** - * Disconnects after all pending reads and writes have completed. -**/ -- (void)disconnectAfterReadingAndWriting -{ - theFlags |= (kForbidReadsWrites | kDisconnectAfterReads | kDisconnectAfterWrites); - - [self maybeScheduleDisconnect]; -} - -/** - * Schedules a call to disconnect if possible. - * That is, if all writes have completed, and we're set to disconnect after writing, - * or if all reads have completed, and we're set to disconnect after reading. -**/ -- (void)maybeScheduleDisconnect -{ - BOOL shouldDisconnect = NO; - - if(theFlags & kDisconnectAfterReads) - { - if(([theReadQueue count] == 0) && (theCurrentRead == nil)) - { - if(theFlags & kDisconnectAfterWrites) - { - if(([theWriteQueue count] == 0) && (theCurrentWrite == nil)) - { - shouldDisconnect = YES; - } - } - else - { - shouldDisconnect = YES; - } - } - } - else if(theFlags & kDisconnectAfterWrites) - { - if(([theWriteQueue count] == 0) && (theCurrentWrite == nil)) - { - shouldDisconnect = YES; - } - } - - if(shouldDisconnect) - { - [self performSelector:@selector(disconnect) withObject:nil afterDelay:0 inModes:theRunLoopModes]; - } -} - -/** - * In the event of an error, this method may be called during onSocket:willDisconnectWithError: to read - * any data that's left on the socket. -**/ -- (NSData *)unreadData -{ - // Ensure this method will only return data in the event of an error - if(!(theFlags & kClosingWithError)) return nil; - - if(theReadStream == NULL) return nil; - - CFIndex totalBytesRead = [partialReadBuffer length]; - BOOL error = NO; - while(!error && CFReadStreamHasBytesAvailable(theReadStream)) - { - [partialReadBuffer increaseLengthBy:READALL_CHUNKSIZE]; - - // Number of bytes to read is space left in packet buffer. - CFIndex bytesToRead = [partialReadBuffer length] - totalBytesRead; - - // Read data into packet buffer - UInt8 *packetbuf = (UInt8 *)( [partialReadBuffer mutableBytes] + totalBytesRead ); - CFIndex bytesRead = CFReadStreamRead(theReadStream, packetbuf, bytesToRead); - - // Check results - if(bytesRead < 0) - { - error = YES; - } - else - { - totalBytesRead += bytesRead; - } - } - - [partialReadBuffer setLength:totalBytesRead]; - - return partialReadBuffer; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Errors -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Returns a standard error object for the current errno value. - * Errno is used for low-level BSD socket errors. -**/ -- (NSError *)getErrnoError -{ - NSString *errorMsg = [NSString stringWithUTF8String:strerror(errno)]; - NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errorMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:userInfo]; -} - -/** - * Returns a standard error message for a CFSocket error. - * Unfortunately, CFSocket offers no feedback on its errors. -**/ -- (NSError *)getSocketError -{ - NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketCFSocketError", - @"AsyncSocket", [NSBundle mainBundle], - @"General CFSocket error", nil); - - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketCFSocketError userInfo:info]; -} - -- (NSError *)getStreamError -{ - CFStreamError err; - if (theReadStream != NULL) - { - err = CFReadStreamGetError (theReadStream); - if (err.error != 0) return [self errorFromCFStreamError: err]; - } - - if (theWriteStream != NULL) - { - err = CFWriteStreamGetError (theWriteStream); - if (err.error != 0) return [self errorFromCFStreamError: err]; - } - - return nil; -} - -/** - * Returns a standard AsyncSocket abort error. -**/ -- (NSError *)getAbortError -{ - NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketCanceledError", - @"AsyncSocket", [NSBundle mainBundle], - @"Connection canceled", nil); - - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketCanceledError userInfo:info]; -} - -/** - * Returns a standard AsyncSocket connect timeout error. -**/ -- (NSError *)getConnectTimeoutError -{ - NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketConnectTimeoutError", - @"AsyncSocket", [NSBundle mainBundle], - @"Attempt to connect to host timed out", nil); - - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketConnectTimeoutError userInfo:info]; -} - -/** - * Returns a standard AsyncSocket maxed out error. -**/ -- (NSError *)getReadMaxedOutError -{ - NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketReadMaxedOutError", - @"AsyncSocket", [NSBundle mainBundle], - @"Read operation reached set maximum length", nil); - - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketReadMaxedOutError userInfo:info]; -} - -/** - * Returns a standard AsyncSocket read timeout error. -**/ -- (NSError *)getReadTimeoutError -{ - NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketReadTimeoutError", - @"AsyncSocket", [NSBundle mainBundle], - @"Read operation timed out", nil); - - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketReadTimeoutError userInfo:info]; -} - -/** - * Returns a standard AsyncSocket write timeout error. -**/ -- (NSError *)getWriteTimeoutError -{ - NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketWriteTimeoutError", - @"AsyncSocket", [NSBundle mainBundle], - @"Write operation timed out", nil); - - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketWriteTimeoutError userInfo:info]; -} - -- (NSError *)errorFromCFStreamError:(CFStreamError)err -{ - if (err.domain == 0 && err.error == 0) return nil; - - // Can't use switch; these constants aren't int literals. - NSString *domain = @"CFStreamError (unlisted domain)"; - NSString *message = nil; - - if(err.domain == kCFStreamErrorDomainPOSIX) { - domain = NSPOSIXErrorDomain; - } - else if(err.domain == kCFStreamErrorDomainMacOSStatus) { - domain = NSOSStatusErrorDomain; - } - else if(err.domain == kCFStreamErrorDomainMach) { - domain = NSMachErrorDomain; - } - else if(err.domain == kCFStreamErrorDomainNetDB) - { - domain = @"kCFStreamErrorDomainNetDB"; - message = [NSString stringWithCString:gai_strerror(err.error) encoding:NSASCIIStringEncoding]; - } - else if(err.domain == kCFStreamErrorDomainNetServices) { - domain = @"kCFStreamErrorDomainNetServices"; - } - else if(err.domain == kCFStreamErrorDomainSOCKS) { - domain = @"kCFStreamErrorDomainSOCKS"; - } - else if(err.domain == kCFStreamErrorDomainSystemConfiguration) { - domain = @"kCFStreamErrorDomainSystemConfiguration"; - } - else if(err.domain == kCFStreamErrorDomainSSL) { - domain = @"kCFStreamErrorDomainSSL"; - } - - NSDictionary *info = nil; - if(message != nil) - { - info = [NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey]; - } - return [NSError errorWithDomain:domain code:err.error userInfo:info]; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Diagnostics -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (BOOL)isConnected -{ - return [self isSocketConnected] && [self areStreamsConnected]; -} - -- (NSString *)connectedHost -{ - if(theSocket4) - return [self connectedHost:theSocket4]; - else - return [self connectedHost:theSocket6]; -} - -- (UInt16)connectedPort -{ - if(theSocket4) - return [self connectedPort:theSocket4]; - else - return [self connectedPort:theSocket6]; -} - -- (NSString *)localHost -{ - if(theSocket4) - return [self localHost:theSocket4]; - else - return [self localHost:theSocket6]; -} - -- (UInt16)localPort -{ - if(theSocket4) - return [self localPort:theSocket4]; - else - return [self localPort:theSocket6]; -} - -- (NSString *)connectedHost:(CFSocketRef)theSocket -{ - if (theSocket == NULL) return nil; - - CFDataRef peeraddr; - NSString *peerstr = nil; - - if((peeraddr = CFSocketCopyPeerAddress(theSocket))) - { - peerstr = [self addressHost:peeraddr]; - CFRelease (peeraddr); - } - - return peerstr; -} - -- (UInt16)connectedPort:(CFSocketRef)theSocket -{ - if (theSocket == NULL) return 0; - - CFDataRef peeraddr; - UInt16 peerport = 0; - - if((peeraddr = CFSocketCopyPeerAddress(theSocket))) - { - peerport = [self addressPort:peeraddr]; - CFRelease (peeraddr); - } - - return peerport; -} - -- (NSString *)localHost:(CFSocketRef)theSocket -{ - if (theSocket == NULL) return nil; - - CFDataRef selfaddr; - NSString *selfstr = nil; - - if((selfaddr = CFSocketCopyAddress(theSocket))) - { - selfstr = [self addressHost:selfaddr]; - CFRelease (selfaddr); - } - - return selfstr; -} - -- (UInt16)localPort:(CFSocketRef)theSocket -{ - if (theSocket == NULL) return 0; - - CFDataRef selfaddr; - UInt16 selfport = 0; - - if ((selfaddr = CFSocketCopyAddress(theSocket))) - { - selfport = [self addressPort:selfaddr]; - CFRelease (selfaddr); - } - - return selfport; -} - -- (NSString *)addressHost:(CFDataRef)cfaddr -{ - if (cfaddr == NULL) return nil; - - char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ]; - struct sockaddr *pSockAddr = (struct sockaddr *) CFDataGetBytePtr (cfaddr); - struct sockaddr_in *pSockAddrV4 = (struct sockaddr_in *)pSockAddr; - struct sockaddr_in6 *pSockAddrV6 = (struct sockaddr_in6 *)pSockAddr; - - const void *pAddr = (pSockAddr->sa_family == AF_INET) ? - (void *)(&(pSockAddrV4->sin_addr)) : - (void *)(&(pSockAddrV6->sin6_addr)); - - const char *pStr = inet_ntop (pSockAddr->sa_family, pAddr, addrBuf, sizeof(addrBuf)); - if (pStr == NULL) [NSException raise: NSInternalInconsistencyException - format: @"Cannot convert address to string."]; - - return [NSString stringWithCString:pStr encoding:NSASCIIStringEncoding]; -} - -- (UInt16)addressPort:(CFDataRef)cfaddr -{ - if (cfaddr == NULL) return 0; - - struct sockaddr_in *pAddr = (struct sockaddr_in *) CFDataGetBytePtr (cfaddr); - return ntohs (pAddr->sin_port); -} - -- (NSData *)connectedAddress -{ - CFSocketRef theSocket; - - if (theSocket4) - theSocket = theSocket4; - else - theSocket = theSocket6; - - if (theSocket == NULL) return nil; - - CFDataRef peeraddr = CFSocketCopyPeerAddress(theSocket); - - if (peeraddr == NULL) return nil; - -#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - NSData *result = [NSData dataWithBytes:CFDataGetBytePtr(peeraddr) length:CFDataGetLength(peeraddr)]; - CFRelease(peeraddr); - return result; -#else - return [(NSData *)NSMakeCollectable(peeraddr) autorelease]; -#endif -} - -- (NSData *)localAddress -{ - CFSocketRef theSocket; - - if (theSocket4) - theSocket = theSocket4; - else - theSocket = theSocket6; - - if (theSocket == NULL) return nil; - - CFDataRef selfaddr = CFSocketCopyAddress(theSocket); - - if (selfaddr == NULL) return nil; - -#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - NSData *result = [NSData dataWithBytes:CFDataGetBytePtr(selfaddr) length:CFDataGetLength(selfaddr)]; - CFRelease(selfaddr); - return result; -#else - return [(NSData *)NSMakeCollectable(selfaddr) autorelease]; -#endif -} - -- (BOOL)isIPv4 -{ - return (theSocket4 != NULL); -} - -- (BOOL)isIPv6 -{ - return (theSocket6 != NULL); -} - -- (BOOL)isSocketConnected -{ - if(theSocket4 != NULL) - return CFSocketIsValid(theSocket4); - else if(theSocket6 != NULL) - return CFSocketIsValid(theSocket6); - else - return NO; -} - -- (BOOL)areStreamsConnected -{ - CFStreamStatus s; - - if (theReadStream != NULL) - { - s = CFReadStreamGetStatus (theReadStream); - if ( !(s == kCFStreamStatusOpen || s == kCFStreamStatusReading || s == kCFStreamStatusError) ) - return NO; - } - else return NO; - - if (theWriteStream != NULL) - { - s = CFWriteStreamGetStatus (theWriteStream); - if ( !(s == kCFStreamStatusOpen || s == kCFStreamStatusWriting || s == kCFStreamStatusError) ) - return NO; - } - else return NO; - - return YES; -} - -- (NSString *)description -{ - static const char *statstr[] = {"not open","opening","open","reading","writing","at end","closed","has error"}; - CFStreamStatus rs = (theReadStream != NULL) ? CFReadStreamGetStatus(theReadStream) : 0; - CFStreamStatus ws = (theWriteStream != NULL) ? CFWriteStreamGetStatus(theWriteStream) : 0; - - NSString *peerstr, *selfstr; - CFDataRef peeraddr4 = NULL, peeraddr6 = NULL, selfaddr4 = NULL, selfaddr6 = NULL; - - if (theSocket4 || theSocket6) - { - if (theSocket4) peeraddr4 = CFSocketCopyPeerAddress(theSocket4); - if (theSocket6) peeraddr6 = CFSocketCopyPeerAddress(theSocket6); - - if(theSocket4 && theSocket6) - { - peerstr = [NSString stringWithFormat: @"%@/%@ %u", - [self addressHost:peeraddr4], [self addressHost:peeraddr6], [self addressPort:peeraddr4]]; - } - else if(theSocket4) - { - peerstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:peeraddr4], [self addressPort:peeraddr4]]; - } - else - { - peerstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:peeraddr6], [self addressPort:peeraddr6]]; - } - - if(peeraddr4) CFRelease(peeraddr4); - if(peeraddr6) CFRelease(peeraddr6); - peeraddr4 = NULL; - peeraddr6 = NULL; - } - else peerstr = @"nowhere"; - - if (theSocket4 || theSocket6) - { - if (theSocket4) selfaddr4 = CFSocketCopyAddress (theSocket4); - if (theSocket6) selfaddr6 = CFSocketCopyAddress (theSocket6); - - if (theSocket4 && theSocket6) - { - selfstr = [NSString stringWithFormat: @"%@/%@ %u", - [self addressHost:selfaddr4], [self addressHost:selfaddr6], [self addressPort:selfaddr4]]; - } - else if (theSocket4) - { - selfstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:selfaddr4], [self addressPort:selfaddr4]]; - } - else - { - selfstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:selfaddr6], [self addressPort:selfaddr6]]; - } - - if(selfaddr4) CFRelease(selfaddr4); - if(selfaddr6) CFRelease(selfaddr6); - selfaddr4 = NULL; - selfaddr6 = NULL; - } - else selfstr = @"nowhere"; - - NSMutableString *ms = [[NSMutableString alloc] initWithCapacity:150]; - - [ms appendString:[NSString stringWithFormat:@"buffer length] != 0) - percentDone = (float)theCurrentRead->bytesDone / - (float)[theCurrentRead->buffer length] * 100.0F; - else - percentDone = 100.0F; - - [ms appendString: [NSString stringWithFormat:@"currently read %u bytes (%d%% done), ", - (unsigned int)[theCurrentRead->buffer length], - theCurrentRead->bytesDone ? percentDone : 0]]; - } - - if (theCurrentWrite == nil) - [ms appendString: @"no current write, "]; - else - { - int percentDone; - if ([theCurrentWrite->buffer length] != 0) - percentDone = (float)theCurrentWrite->bytesDone / - (float)[theCurrentWrite->buffer length] * 100.0F; - else - percentDone = 100.0F; - - [ms appendString: [NSString stringWithFormat:@"currently written %u (%d%%), ", - (unsigned int)[theCurrentWrite->buffer length], - theCurrentWrite->bytesDone ? percentDone : 0]]; - } - - [ms appendString:[NSString stringWithFormat:@"read stream %p %s, ", theReadStream, statstr[rs]]]; - [ms appendString:[NSString stringWithFormat:@"write stream %p %s", theWriteStream, statstr[ws]]]; - - if(theFlags & kDisconnectAfterReads) - { - if(theFlags & kDisconnectAfterWrites) - [ms appendString: @", will disconnect after reads & writes"]; - else - [ms appendString: @", will disconnect after reads"]; - } - else if(theFlags & kDisconnectAfterWrites) - { - [ms appendString: @", will disconnect after writes"]; - } - - if (![self isConnected]) [ms appendString: @", not connected"]; - - [ms appendString:@">"]; - - return [ms autorelease]; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Reading -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (void)readDataToLength:(CFIndex)length withTimeout:(NSTimeInterval)timeout tag:(long)tag -{ - if(length == 0) return; - if(theFlags & kForbidReadsWrites) return; - - NSMutableData *buffer = [[NSMutableData alloc] initWithLength:length]; - AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer - timeout:timeout - tag:tag - readAllAvailable:NO - terminator:nil - maxLength:length]; - - [theReadQueue addObject:packet]; - [self scheduleDequeueRead]; - - [packet release]; - [buffer release]; -} - -- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag -{ - [self readDataToData:data withTimeout:timeout maxLength:-1 tag:tag]; -} - -- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(CFIndex)length tag:(long)tag -{ - if(data == nil || [data length] == 0) return; - if(length >= 0 && length < [data length]) return; - if(theFlags & kForbidReadsWrites) return; - - NSMutableData *buffer = [[NSMutableData alloc] initWithLength:0]; - AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer - timeout:timeout - tag:tag - readAllAvailable:NO - terminator:data - maxLength:length]; - - [theReadQueue addObject:packet]; - [self scheduleDequeueRead]; - - [packet release]; - [buffer release]; -} - -- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag -{ - if (theFlags & kForbidReadsWrites) return; - - NSMutableData *buffer = [[NSMutableData alloc] initWithLength:0]; - AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer - timeout:timeout - tag:tag - readAllAvailable:YES - terminator:nil - maxLength:-1]; - - [theReadQueue addObject:packet]; - [self scheduleDequeueRead]; - - [packet release]; - [buffer release]; -} - -/** - * Puts a maybeDequeueRead on the run loop. - * An assumption here is that selectors will be performed consecutively within their priority. -**/ -- (void)scheduleDequeueRead -{ - if((theFlags & kDequeueReadScheduled) == 0) - { - theFlags |= kDequeueReadScheduled; - [self performSelector:@selector(maybeDequeueRead) withObject:nil afterDelay:0 inModes:theRunLoopModes]; - } -} - -/** - * This method starts a new read, if needed. - * It is called when a user requests a read, - * or when a stream opens that may have requested reads sitting in the queue, etc. -**/ -- (void)maybeDequeueRead -{ - // Unset the flag indicating a call to this method is scheduled - theFlags &= ~kDequeueReadScheduled; - - // If we're not currently processing a read AND we have an available read stream - if((theCurrentRead == nil) && (theReadStream != NULL)) - { - if([theReadQueue count] > 0) - { - // Dequeue the next object in the write queue - theCurrentRead = [[theReadQueue objectAtIndex:0] retain]; - [theReadQueue removeObjectAtIndex:0]; - - if([theCurrentRead isKindOfClass:[AsyncSpecialPacket class]]) - { - // Attempt to start TLS - theFlags |= kStartingReadTLS; - - // This method won't do anything unless both kStartingReadTLS and kStartingWriteTLS are both set - [self maybeStartTLS]; - } - else - { - // Start time-out timer - if(theCurrentRead->timeout >= 0.0) - { - theReadTimer = [NSTimer timerWithTimeInterval:theCurrentRead->timeout - target:self - selector:@selector(doReadTimeout:) - userInfo:nil - repeats:NO]; - [self runLoopAddTimer:theReadTimer]; - } - - // Immediately read, if possible - [self doBytesAvailable]; - } - } - else if(theFlags & kDisconnectAfterReads) - { - if(theFlags & kDisconnectAfterWrites) - { - if(([theWriteQueue count] == 0) && (theCurrentWrite == nil)) - { - [self disconnect]; - } - } - else - { - [self disconnect]; - } - } - } -} - -/** - * Call this method in doBytesAvailable instead of CFReadStreamHasBytesAvailable(). - * This method supports pre-buffering properly as well as the kSocketHasBytesAvailable flag. -**/ -- (BOOL)hasBytesAvailable -{ - if ((theFlags & kSocketHasBytesAvailable) || ([partialReadBuffer length] > 0)) - { - return YES; - } - else - { - return CFReadStreamHasBytesAvailable(theReadStream); - } -} - -/** - * Call this method in doBytesAvailable instead of CFReadStreamRead(). - * This method support pre-buffering properly. -**/ -- (CFIndex)readIntoBuffer:(UInt8 *)buffer maxLength:(CFIndex)length -{ - if([partialReadBuffer length] > 0) - { - // Determine the maximum amount of data to read - CFIndex bytesToRead = MIN(length, [partialReadBuffer length]); - - // Copy the bytes from the buffer - memcpy(buffer, [partialReadBuffer bytes], bytesToRead); - - // Remove the copied bytes from the buffer - [partialReadBuffer replaceBytesInRange:NSMakeRange(0, bytesToRead) withBytes:NULL length:0]; - - return bytesToRead; - } - else - { - // Unset the "has-bytes-available" flag - theFlags &= ~kSocketHasBytesAvailable; - - return CFReadStreamRead(theReadStream, buffer, length); - } -} - -/** - * This method is called when a new read is taken from the read queue or when new data becomes available on the stream. -**/ -- (void)doBytesAvailable -{ - // If data is available on the stream, but there is no read request, then we don't need to process the data yet. - // Also, if there is a read request, but no read stream setup yet, we can't process any data yet. - if((theCurrentRead != nil) && (theReadStream != NULL)) - { - // Note: This method is not called if theCurrentRead is an AsyncSpecialPacket (startTLS packet) - - CFIndex totalBytesRead = 0; - - BOOL done = NO; - BOOL socketError = NO; - BOOL maxoutError = NO; - - while(!done && !socketError && !maxoutError && [self hasBytesAvailable]) - { - BOOL didPreBuffer = NO; - - // There are 3 types of read packets: - // - // 1) Read a specific length of data. - // 2) Read all available data. - // 3) Read up to a particular terminator. - - if(theCurrentRead->readAllAvailableData == YES) - { - // We're reading all available data. - // - // Make sure there is at least READALL_CHUNKSIZE bytes available. - // We don't want to increase the buffer any more than this or we'll waste space. - // With prebuffering it's possible to read in a small chunk on the first read. - - unsigned buffInc = READALL_CHUNKSIZE - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone); - [theCurrentRead->buffer increaseLengthBy:buffInc]; - } - else if(theCurrentRead->term != nil) - { - // We're reading up to a terminator. - // - // We may only want to read a few bytes. - // Just enough to ensure we don't go past our term or over our max limit. - // Unless pre-buffering is enabled, in which case we may want to read in a larger chunk. - - // If we already have data pre-buffered, we obviously don't want to pre-buffer it again. - // So in this case we'll just read as usual. - - if(([partialReadBuffer length] > 0) || !(theFlags & kEnablePreBuffering)) - { - unsigned maxToRead = [theCurrentRead readLengthForTerm]; - - unsigned bufInc = maxToRead - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone); - [theCurrentRead->buffer increaseLengthBy:bufInc]; - } - else - { - didPreBuffer = YES; - unsigned maxToRead = [theCurrentRead prebufferReadLengthForTerm]; - - unsigned buffInc = maxToRead - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone); - [theCurrentRead->buffer increaseLengthBy:buffInc]; - - } - } - - // Number of bytes to read is space left in packet buffer. - CFIndex bytesToRead = [theCurrentRead->buffer length] - theCurrentRead->bytesDone; - - // Read data into packet buffer - UInt8 *subBuffer = (UInt8 *)([theCurrentRead->buffer mutableBytes] + theCurrentRead->bytesDone); - CFIndex bytesRead = [self readIntoBuffer:subBuffer maxLength:bytesToRead]; - - // Check results - if(bytesRead < 0) - { - socketError = YES; - } - else - { - // Update total amount read for the current read - theCurrentRead->bytesDone += bytesRead; - - // Update total amount read in this method invocation - totalBytesRead += bytesRead; - } - - // Is packet done? - if(theCurrentRead->readAllAvailableData != YES) - { - if(theCurrentRead->term != nil) - { - if(didPreBuffer) - { - // Search for the terminating sequence within the big chunk we just read. - CFIndex overflow = [theCurrentRead searchForTermAfterPreBuffering:bytesRead]; - - if(overflow > 0) - { - // Copy excess data into partialReadBuffer - NSMutableData *buffer = theCurrentRead->buffer; - const void *overflowBuffer = [buffer bytes] + theCurrentRead->bytesDone - overflow; - - [partialReadBuffer appendBytes:overflowBuffer length:overflow]; - - // Update the bytesDone variable. - // Note: The completeCurrentRead method will trim the buffer for us. - theCurrentRead->bytesDone -= overflow; - } - - done = (overflow >= 0); - } - else - { - // Search for the terminating sequence at the end of the buffer - int termlen = [theCurrentRead->term length]; - if(theCurrentRead->bytesDone >= termlen) - { - const void *buf = [theCurrentRead->buffer bytes] + (theCurrentRead->bytesDone - termlen); - const void *seq = [theCurrentRead->term bytes]; - done = (memcmp (buf, seq, termlen) == 0); - } - } - - if(!done && theCurrentRead->maxLength >= 0 && theCurrentRead->bytesDone >= theCurrentRead->maxLength) - { - // There's a set maxLength, and we've reached that maxLength without completing the read - maxoutError = YES; - } - } - else - { - // Done when (sized) buffer is full. - done = ([theCurrentRead->buffer length] == theCurrentRead->bytesDone); - } - } - // else readAllAvailable doesn't end until all readable is read. - } - - if(theCurrentRead->readAllAvailableData && theCurrentRead->bytesDone > 0) - { - // Ran out of bytes, so the "read-all-available-data" type packet is done - done = YES; - } - - if(done) - { - [self completeCurrentRead]; - if (!socketError) [self scheduleDequeueRead]; - } - else if(totalBytesRead > 0) - { - // We're not done with the readToLength or readToData yet, but we have read in some bytes - if ([theDelegate respondsToSelector:@selector(onSocket:didReadPartialDataOfLength:tag:)]) - { - [theDelegate onSocket:self didReadPartialDataOfLength:totalBytesRead tag:theCurrentRead->tag]; - } - } - - if(socketError) - { - CFStreamError err = CFReadStreamGetError(theReadStream); - [self closeWithError:[self errorFromCFStreamError:err]]; - return; - } - if(maxoutError) - { - [self closeWithError:[self getReadMaxedOutError]]; - return; - } - } -} - -// Ends current read and calls delegate. -- (void)completeCurrentRead -{ - NSAssert(theCurrentRead, @"Trying to complete current read when there is no current read."); - - [theCurrentRead->buffer setLength:theCurrentRead->bytesDone]; - if([theDelegate respondsToSelector:@selector(onSocket:didReadData:withTag:)]) - { - [theDelegate onSocket:self didReadData:theCurrentRead->buffer withTag:theCurrentRead->tag]; - } - - if (theCurrentRead != nil) [self endCurrentRead]; // Caller may have disconnected. -} - -// Ends current read. -- (void)endCurrentRead -{ - NSAssert(theCurrentRead, @"Trying to end current read when there is no current read."); - - [theReadTimer invalidate]; - theReadTimer = nil; - - [theCurrentRead release]; - theCurrentRead = nil; -} - -- (void)doReadTimeout:(NSTimer *)timer -{ - NSTimeInterval timeoutExtension = 0.0; - - if([theDelegate respondsToSelector:@selector(onSocket:shouldTimeoutReadWithTag:elapsed:bytesDone:)]) - { - timeoutExtension = [theDelegate onSocket:self shouldTimeoutReadWithTag:theCurrentRead->tag - elapsed:theCurrentRead->timeout - bytesDone:theCurrentRead->bytesDone]; - } - - if(timeoutExtension > 0.0) - { - theCurrentRead->timeout += timeoutExtension; - - theReadTimer = [NSTimer timerWithTimeInterval:timeoutExtension - target:self - selector:@selector(doReadTimeout:) - userInfo:nil - repeats:NO]; - [self runLoopAddTimer:theReadTimer]; - } - else - { - // Do not call endCurrentRead here. - // We must allow the delegate access to any partial read in the unreadData method. - - [self closeWithError:[self getReadTimeoutError]]; - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Writing -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag -{ - if (data == nil || [data length] == 0) return; - if (theFlags & kForbidReadsWrites) return; - - AsyncWritePacket *packet = [[AsyncWritePacket alloc] initWithData:data timeout:timeout tag:tag]; - - [theWriteQueue addObject:packet]; - [self scheduleDequeueWrite]; - - [packet release]; -} - -- (void)scheduleDequeueWrite -{ - if((theFlags & kDequeueWriteScheduled) == 0) - { - theFlags |= kDequeueWriteScheduled; - [self performSelector:@selector(maybeDequeueWrite) withObject:nil afterDelay:0 inModes:theRunLoopModes]; - } -} - -/** - * Conditionally starts a new write. - * - * IF there is not another write in process - * AND there is a write queued - * AND we have a write stream available - * - * This method also handles auto-disconnect post read/write completion. -**/ -- (void)maybeDequeueWrite -{ - // Unset the flag indicating a call to this method is scheduled - theFlags &= ~kDequeueWriteScheduled; - - // If we're not currently processing a write AND we have an available write stream - if((theCurrentWrite == nil) && (theWriteStream != NULL)) - { - if([theWriteQueue count] > 0) - { - // Dequeue the next object in the write queue - theCurrentWrite = [[theWriteQueue objectAtIndex:0] retain]; - [theWriteQueue removeObjectAtIndex:0]; - - if([theCurrentWrite isKindOfClass:[AsyncSpecialPacket class]]) - { - // Attempt to start TLS - theFlags |= kStartingWriteTLS; - - // This method won't do anything unless both kStartingReadTLS and kStartingWriteTLS are both set - [self maybeStartTLS]; - } - else - { - // Start time-out timer - if(theCurrentWrite->timeout >= 0.0) - { - theWriteTimer = [NSTimer timerWithTimeInterval:theCurrentWrite->timeout - target:self - selector:@selector(doWriteTimeout:) - userInfo:nil - repeats:NO]; - [self runLoopAddTimer:theWriteTimer]; - } - - // Immediately write, if possible - [self doSendBytes]; - } - } - else if(theFlags & kDisconnectAfterWrites) - { - if(theFlags & kDisconnectAfterReads) - { - if(([theReadQueue count] == 0) && (theCurrentRead == nil)) - { - [self disconnect]; - } - } - else - { - [self disconnect]; - } - } - } -} - -/** - * Call this method in doSendBytes instead of CFWriteStreamCanAcceptBytes(). - * This method supports the kSocketCanAcceptBytes flag. -**/ -- (BOOL)canAcceptBytes -{ - if (theFlags & kSocketCanAcceptBytes) - { - return YES; - } - else - { - return CFWriteStreamCanAcceptBytes(theWriteStream); - } -} - -- (void)doSendBytes -{ - if((theCurrentWrite != nil) && (theWriteStream != NULL)) - { - // Note: This method is not called if theCurrentWrite is an AsyncSpecialPacket (startTLS packet) - - CFIndex totalBytesWritten = 0; - - BOOL done = NO; - BOOL error = NO; - - while (!done && !error && [self canAcceptBytes]) - { - // Figure out what to write. - CFIndex bytesRemaining = [theCurrentWrite->buffer length] - theCurrentWrite->bytesDone; - CFIndex bytesToWrite = (bytesRemaining < WRITE_CHUNKSIZE) ? bytesRemaining : WRITE_CHUNKSIZE; - UInt8 *writestart = (UInt8 *)([theCurrentWrite->buffer bytes] + theCurrentWrite->bytesDone); - - // Write. - CFIndex bytesWritten = CFWriteStreamWrite(theWriteStream, writestart, bytesToWrite); - - // Unset the "can accept bytes" flag - theFlags &= ~kSocketCanAcceptBytes; - - // Check results - if (bytesWritten < 0) - { - error = YES; - } - else - { - // Update total amount read for the current write - theCurrentWrite->bytesDone += bytesWritten; - - // Update total amount written in this method invocation - totalBytesWritten += bytesWritten; - - // Is packet done? - done = ([theCurrentWrite->buffer length] == theCurrentWrite->bytesDone); - } - } - - if(done) - { - [self completeCurrentWrite]; - [self scheduleDequeueWrite]; - } - else if(error) - { - CFStreamError err = CFWriteStreamGetError(theWriteStream); - [self closeWithError:[self errorFromCFStreamError:err]]; - return; - } - else - { - // We're not done with the entire write, but we have written some bytes - if ([theDelegate respondsToSelector:@selector(onSocket:didWritePartialDataOfLength:tag:)]) - { - [theDelegate onSocket:self didWritePartialDataOfLength:totalBytesWritten tag:theCurrentWrite->tag]; - } - } - } -} - -// Ends current write and calls delegate. -- (void)completeCurrentWrite -{ - NSAssert(theCurrentWrite, @"Trying to complete current write when there is no current write."); - - if ([theDelegate respondsToSelector:@selector(onSocket:didWriteDataWithTag:)]) - { - [theDelegate onSocket:self didWriteDataWithTag:theCurrentWrite->tag]; - } - - if (theCurrentWrite != nil) [self endCurrentWrite]; // Caller may have disconnected. -} - -// Ends current write. -- (void)endCurrentWrite -{ - NSAssert(theCurrentWrite, @"Trying to complete current write when there is no current write."); - - [theWriteTimer invalidate]; - theWriteTimer = nil; - - [theCurrentWrite release]; - theCurrentWrite = nil; -} - -- (void)doWriteTimeout:(NSTimer *)timer -{ - NSTimeInterval timeoutExtension = 0.0; - - if([theDelegate respondsToSelector:@selector(onSocket:shouldTimeoutWriteWithTag:elapsed:bytesDone:)]) - { - timeoutExtension = [theDelegate onSocket:self shouldTimeoutWriteWithTag:theCurrentWrite->tag - elapsed:theCurrentWrite->timeout - bytesDone:theCurrentWrite->bytesDone]; - } - - if(timeoutExtension > 0.0) - { - theCurrentWrite->timeout += timeoutExtension; - - theWriteTimer = [NSTimer timerWithTimeInterval:timeoutExtension - target:self - selector:@selector(doWriteTimeout:) - userInfo:nil - repeats:NO]; - [self runLoopAddTimer:theWriteTimer]; - } - else - { - [self closeWithError:[self getWriteTimeoutError]]; - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Security -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (void)startTLS:(NSDictionary *)tlsSettings -{ - if(tlsSettings == nil) - { - // Passing nil/NULL to CFReadStreamSetProperty will appear to work the same as passing an empty dictionary, - // but causes problems if we later try to fetch the remote host's certificate. - // - // To be exact, it causes the following to return NULL instead of the normal result: - // CFReadStreamCopyProperty(readStream, kCFStreamPropertySSLPeerCertificates) - // - // So we use an empty dictionary instead, which works perfectly. - - tlsSettings = [NSDictionary dictionary]; - } - - AsyncSpecialPacket *packet = [[AsyncSpecialPacket alloc] initWithTLSSettings:tlsSettings]; - - [theReadQueue addObject:packet]; - [self scheduleDequeueRead]; - - [theWriteQueue addObject:packet]; - [self scheduleDequeueWrite]; - - [packet release]; -} - -- (void)maybeStartTLS -{ - // We can't start TLS until: - // - All queued reads prior to the user calling StartTLS are complete - // - All queued writes prior to the user calling StartTLS are complete - // - // We'll know these conditions are met when both kStartingReadTLS and kStartingWriteTLS are set - - if((theFlags & kStartingReadTLS) && (theFlags & kStartingWriteTLS)) - { - AsyncSpecialPacket *tlsPacket = (AsyncSpecialPacket *)theCurrentRead; - - BOOL didStartOnReadStream = CFReadStreamSetProperty(theReadStream, kCFStreamPropertySSLSettings, - (CFDictionaryRef)tlsPacket->tlsSettings); - BOOL didStartOnWriteStream = CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertySSLSettings, - (CFDictionaryRef)tlsPacket->tlsSettings); - - if(!didStartOnReadStream || !didStartOnWriteStream) - { - [self closeWithError:[self getSocketError]]; - } - } -} - -- (void)onTLSHandshakeSuccessful -{ - if((theFlags & kStartingReadTLS) && (theFlags & kStartingWriteTLS)) - { - theFlags &= ~kStartingReadTLS; - theFlags &= ~kStartingWriteTLS; - - if([theDelegate respondsToSelector:@selector(onSocketDidSecure:)]) - { - [theDelegate onSocketDidSecure:self]; - } - - [self endCurrentRead]; - [self endCurrentWrite]; - - [self scheduleDequeueRead]; - [self scheduleDequeueWrite]; - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark CF Callbacks -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (void)doCFSocketCallback:(CFSocketCallBackType)type - forSocket:(CFSocketRef)sock - withAddress:(NSData *)address - withData:(const void *)pData -{ - NSParameterAssert ((sock == theSocket4) || (sock == theSocket6)); - - switch (type) - { - case kCFSocketConnectCallBack: - // The data argument is either NULL or a pointer to an SInt32 error code, if the connect failed. - if(pData) - [self doSocketOpen:sock withCFSocketError:kCFSocketError]; - else - [self doSocketOpen:sock withCFSocketError:kCFSocketSuccess]; - break; - case kCFSocketAcceptCallBack: - [self doAcceptWithSocket: *((CFSocketNativeHandle *)pData)]; - break; - default: - NSLog (@"AsyncSocket %p received unexpected CFSocketCallBackType %lu.", self, (unsigned long) type); - break; - } -} - -- (void)doCFReadStreamCallback:(CFStreamEventType)type forStream:(CFReadStreamRef)stream -{ - NSParameterAssert(theReadStream != NULL); - - CFStreamError err; - switch (type) - { - case kCFStreamEventOpenCompleted: - theFlags |= kDidCompleteOpenForRead; - [self doStreamOpen]; - break; - case kCFStreamEventHasBytesAvailable: - if(theFlags & kStartingReadTLS) { - [self onTLSHandshakeSuccessful]; - } - else { - theFlags |= kSocketHasBytesAvailable; - [self doBytesAvailable]; - } - break; - case kCFStreamEventErrorOccurred: - case kCFStreamEventEndEncountered: - err = CFReadStreamGetError (theReadStream); - [self closeWithError: [self errorFromCFStreamError:err]]; - break; - default: - NSLog (@"AsyncSocket %p received unexpected CFReadStream callback, CFStreamEventType %lu.", self, type); - } -} - -- (void)doCFWriteStreamCallback:(CFStreamEventType)type forStream:(CFWriteStreamRef)stream -{ - NSParameterAssert(theWriteStream != NULL); - - CFStreamError err; - switch (type) - { - case kCFStreamEventOpenCompleted: - theFlags |= kDidCompleteOpenForWrite; - [self doStreamOpen]; - break; - case kCFStreamEventCanAcceptBytes: - if(theFlags & kStartingWriteTLS) { - [self onTLSHandshakeSuccessful]; - } - else { - theFlags |= kSocketCanAcceptBytes; - [self doSendBytes]; - } - break; - case kCFStreamEventErrorOccurred: - case kCFStreamEventEndEncountered: - err = CFWriteStreamGetError (theWriteStream); - [self closeWithError: [self errorFromCFStreamError:err]]; - break; - default: - NSLog (@"AsyncSocket %p received unexpected CFWriteStream callback, CFStreamEventType %lu.", self, type); - } -} - -/** - * This is the callback we setup for CFSocket. - * This method does nothing but forward the call to it's Objective-C counterpart -**/ -static void MyCFSocketCallback (CFSocketRef sref, CFSocketCallBackType type, CFDataRef address, const void *pData, void *pInfo) -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - AsyncSocket *theSocket = [[(AsyncSocket *)pInfo retain] autorelease]; - [theSocket doCFSocketCallback:type forSocket:sref withAddress:(NSData *)address withData:pData]; - - [pool release]; -} - -/** - * This is the callback we setup for CFReadStream. - * This method does nothing but forward the call to it's Objective-C counterpart -**/ -static void MyCFReadStreamCallback (CFReadStreamRef stream, CFStreamEventType type, void *pInfo) -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - AsyncSocket *theSocket = [[(AsyncSocket *)pInfo retain] autorelease]; - [theSocket doCFReadStreamCallback:type forStream:stream]; - - [pool release]; -} - -/** - * This is the callback we setup for CFWriteStream. - * This method does nothing but forward the call to it's Objective-C counterpart -**/ -static void MyCFWriteStreamCallback (CFWriteStreamRef stream, CFStreamEventType type, void *pInfo) -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - AsyncSocket *theSocket = [[(AsyncSocket *)pInfo retain] autorelease]; - [theSocket doCFWriteStreamCallback:type forStream:stream]; - - [pool release]; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Class Methods -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// Return line separators. -+ (NSData *)CRLFData -{ - return [NSData dataWithBytes:"\x0D\x0A" length:2]; -} - -+ (NSData *)CRData -{ - return [NSData dataWithBytes:"\x0D" length:1]; -} - -+ (NSData *)LFData -{ - return [NSData dataWithBytes:"\x0A" length:1]; -} - -+ (NSData *)ZeroData -{ - return [NSData dataWithBytes:"" length:1]; -} - -@end diff --git a/AsyncSocket/AsyncUdpSocket.h b/AsyncSocket/AsyncUdpSocket.h deleted file mode 100755 index 5355efa..0000000 --- a/AsyncSocket/AsyncUdpSocket.h +++ /dev/null @@ -1,365 +0,0 @@ -// -// AsyncUdpSocket.h -// -// This class is in the public domain. -// Originally created by Robbie Hanson on Wed Oct 01 2008. -// Updated and maintained by Deusty Designs and the Mac development community. -// -// http://code.google.com/p/cocoaasyncsocket/ -// - -#import - -@class AsyncSendPacket; -@class AsyncReceivePacket; - -extern NSString *const AsyncUdpSocketException; -extern NSString *const AsyncUdpSocketErrorDomain; - -enum AsyncUdpSocketError -{ - AsyncUdpSocketCFSocketError = kCFSocketError, // From CFSocketError enum - AsyncUdpSocketNoError = 0, // Never used - AsyncUdpSocketBadParameter, // Used if given a bad parameter (such as an improper address) - AsyncUdpSocketIPv4Unavailable, // Used if you bind/connect using IPv6 only - AsyncUdpSocketIPv6Unavailable, // Used if you bind/connect using IPv4 only (or iPhone) - AsyncUdpSocketSendTimeoutError, - AsyncUdpSocketReceiveTimeoutError -}; -typedef enum AsyncUdpSocketError AsyncUdpSocketError; - -@interface AsyncUdpSocket : NSObject -{ - CFSocketRef theSocket4; // IPv4 socket - CFSocketRef theSocket6; // IPv6 socket - - CFRunLoopSourceRef theSource4; // For theSocket4 - CFRunLoopSourceRef theSource6; // For theSocket6 - CFRunLoopRef theRunLoop; - CFSocketContext theContext; - NSArray *theRunLoopModes; - - NSMutableArray *theSendQueue; - AsyncSendPacket *theCurrentSend; - NSTimer *theSendTimer; - - NSMutableArray *theReceiveQueue; - AsyncReceivePacket *theCurrentReceive; - NSTimer *theReceiveTimer; - - id theDelegate; - UInt16 theFlags; - - long theUserData; - - NSString *cachedLocalHost; - UInt16 cachedLocalPort; - - NSString *cachedConnectedHost; - UInt16 cachedConnectedPort; - - UInt32 maxReceiveBufferSize; -} - -/** - * Creates new instances of AsyncUdpSocket. -**/ -- (id)init; -- (id)initWithDelegate:(id)delegate; -- (id)initWithDelegate:(id)delegate userData:(long)userData; - -/** - * Creates new instances of AsyncUdpSocket that support only IPv4 or IPv6. - * The other init methods will support both, unless specifically binded or connected to one protocol. - * If you know you'll only be using one protocol, these init methods may be a bit more efficient. -**/ -- (id)initIPv4; -- (id)initIPv6; - -- (id)delegate; -- (void)setDelegate:(id)delegate; - -- (long)userData; -- (void)setUserData:(long)userData; - -/** - * Returns the local address info for the socket. - * - * Note: Address info may not be available until after the socket has been bind'ed, - * or until after data has been sent. -**/ -- (NSString *)localHost; -- (UInt16)localPort; - -/** - * Returns the remote address info for the socket. - * - * Note: Since UDP is connectionless by design, connected address info - * will not be available unless the socket is explicitly connected to a remote host/port -**/ -- (NSString *)connectedHost; -- (UInt16)connectedPort; - -/** - * Returns whether or not this socket has been connected to a single host. - * By design, UDP is a connectionless protocol, and connecting is not needed. - * If connected, the socket will only be able to send/receive data to/from the connected host. -**/ -- (BOOL)isConnected; - -/** - * Returns whether or not this socket has been closed. - * The only way a socket can be closed is if you explicitly call one of the close methods. -**/ -- (BOOL)isClosed; - -/** - * Returns whether or not this socket supports IPv4. - * By default this will be true, unless the socket is specifically initialized as IPv6 only, - * or is binded or connected to an IPv6 address. -**/ -- (BOOL)isIPv4; - -/** - * Returns whether or not this socket supports IPv6. - * By default this will be true, unless the socket is specifically initialized as IPv4 only, - * or is binded or connected to an IPv4 address. - * - * This method will also return false on platforms that do not support IPv6. - * Note: The iPhone does not currently support IPv6. -**/ -- (BOOL)isIPv6; - -/** - * Returns the mtu of the socket. - * If unknown, returns zero. - * - * Sending data larger than this may result in an error. - * This is an advanced topic, and one should understand the wide range of mtu's on networks and the internet. - * Therefore this method is only for reference and may be of little use in many situations. -**/ -- (unsigned int)maximumTransmissionUnit; - -/** - * Binds the UDP socket to the given port and optional address. - * Binding should be done for server sockets that receive data prior to sending it. - * Client sockets can skip binding, - * as the OS will automatically assign the socket an available port when it starts sending data. - * - * You cannot bind a socket after its been connected. - * You can only bind a socket once. - * You can still connect a socket (if desired) after binding. - * - * On success, returns YES. - * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. -**/ -- (BOOL)bindToPort:(UInt16)port error:(NSError **)errPtr; -- (BOOL)bindToAddress:(NSString *)localAddr port:(UInt16)port error:(NSError **)errPtr; - -/** - * Connects the UDP socket to the given host and port. - * By design, UDP is a connectionless protocol, and connecting is not needed. - * - * Choosing to connect to a specific host/port has the following effect: - * - You will only be able to send data to the connected host/port. - * - You will only be able to receive data from the connected host/port. - * - You will receive ICMP messages that come from the connected host/port, such as "connection refused". - * - * Connecting a UDP socket does not result in any communication on the socket. - * It simply changes the internal state of the socket. - * - * You cannot bind a socket after its been connected. - * You can only connect a socket once. - * - * On success, returns YES. - * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. -**/ -- (BOOL)connectToHost:(NSString *)host onPort:(UInt16)port error:(NSError **)errPtr; -- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr; - -/** - * Join multicast group - * - * Group should be an IP address (eg @"225.228.0.1") -**/ -- (BOOL)joinMulticastGroup:(NSString *)group error:(NSError **)errPtr; -- (BOOL)joinMulticastGroup:(NSString *)group withAddress:(NSString *)interface error:(NSError **)errPtr; - -/** - * By default, the underlying socket in the OS will not allow you to send broadcast messages. - * In order to send broadcast messages, you need to enable this functionality in the socket. - * - * A broadcast is a UDP message to addresses like "192.168.255.255" or "255.255.255.255" that is - * delivered to every host on the network. - * The reason this is generally disabled by default is to prevent - * accidental broadcast messages from flooding the network. -**/ -- (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr; - -/** - * Asynchronously sends the given data, with the given timeout and tag. - * - * This method may only be used with a connected socket. - * - * If data is nil or zero-length, this method does nothing and immediately returns NO. - * If the socket is not connected, this method does nothing and immediately returns NO. -**/ -- (BOOL)sendData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; - -/** - * Asynchronously sends the given data, with the given timeout and tag, to the given host and port. - * - * This method cannot be used with a connected socket. - * - * If data is nil or zero-length, this method does nothing and immediately returns NO. - * If the socket is connected, this method does nothing and immediately returns NO. - * If unable to resolve host to a valid IPv4 or IPv6 address, this method returns NO. -**/ -- (BOOL)sendData:(NSData *)data toHost:(NSString *)host port:(UInt16)port withTimeout:(NSTimeInterval)timeout tag:(long)tag; - -/** - * Asynchronously sends the given data, with the given timeout and tag, to the given address. - * - * This method cannot be used with a connected socket. - * - * If data is nil or zero-length, this method does nothing and immediately returns NO. - * If the socket is connected, this method does nothing and immediately returns NO. -**/ -- (BOOL)sendData:(NSData *)data toAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout tag:(long)tag; - -/** - * Asynchronously receives a single datagram packet. - * - * If the receive succeeds, the onUdpSocket:didReceiveData:fromHost:port:tag delegate method will be called. - * Otherwise, a timeout will occur, and the onUdpSocket:didNotReceiveDataWithTag: delegate method will be called. -**/ -- (void)receiveWithTimeout:(NSTimeInterval)timeout tag:(long)tag; - -/** - * Closes the socket immediately. Any pending send or receive operations are dropped. -**/ -- (void)close; - -/** - * Closes after all pending send operations have completed. - * After calling this, the sendData: and receive: methods will do nothing. - * In other words, you won't be able to add any more send or receive operations to the queue. - * The socket will close even if there are still pending receive operations. -**/ -- (void)closeAfterSending; - -/** - * Closes after all pending receive operations have completed. - * After calling this, the sendData: and receive: methods will do nothing. - * In other words, you won't be able to add any more send or receive operations to the queue. - * The socket will close even if there are still pending send operations. -**/ -- (void)closeAfterReceiving; - -/** - * Closes after all pending send and receive operations have completed. - * After calling this, the sendData: and receive: methods will do nothing. - * In other words, you won't be able to add any more send or receive operations to the queue. -**/ -- (void)closeAfterSendingAndReceiving; - -/** - * Gets/Sets the maximum size of the buffer that will be allocated for receive operations. - * The default size is 9216 bytes. - * - * The theoretical maximum size of any IPv4 UDP packet is UINT16_MAX = 65535. - * The theoretical maximum size of any IPv6 UDP packet is UINT32_MAX = 4294967295. - * - * In practice, however, the size of UDP packets will be much smaller. - * Indeed most protocols will send and receive packets of only a few bytes, - * or will set a limit on the size of packets to prevent fragmentation in the IP layer. - * - * If you set the buffer size too small, the sockets API in the OS will silently discard - * any extra data, and you will not be notified of the error. -**/ -- (UInt32)maxReceiveBufferSize; -- (void)setMaxReceiveBufferSize:(UInt32)max; - -/** - * When you create an AsyncUdpSocket, it is added to the runloop of the current thread. - * So it is easiest to simply create the socket on the thread you intend to use it. - * - * If, however, you need to move the socket to a separate thread at a later time, this - * method may be used to accomplish the task. - * - * This method must be called from the thread/runloop the socket is currently running on. - * - * Note: After calling this method, all further method calls to this object should be done from the given runloop. - * Also, all delegate calls will be sent on the given runloop. -**/ -- (BOOL)moveToRunLoop:(NSRunLoop *)runLoop; - -/** - * Allows you to configure which run loop modes the socket uses. - * The default set of run loop modes is NSDefaultRunLoopMode. - * - * If you'd like your socket to continue operation during other modes, you may want to add modes such as - * NSModalPanelRunLoopMode or NSEventTrackingRunLoopMode. Or you may simply want to use NSRunLoopCommonModes. - * - * Note: NSRunLoopCommonModes is defined in 10.5. For previous versions one can use kCFRunLoopCommonModes. -**/ -- (BOOL)setRunLoopModes:(NSArray *)runLoopModes; - -/** - * Returns the current run loop modes the AsyncSocket instance is operating in. - * The default set of run loop modes is NSDefaultRunLoopMode. -**/ -- (NSArray *)runLoopModes; - -@end - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -@interface NSObject (AsyncUdpSocketDelegate) - -/** - * Called when the datagram with the given tag has been sent. -**/ -- (void)onUdpSocket:(AsyncUdpSocket *)sock didSendDataWithTag:(long)tag; - -/** - * Called if an error occurs while trying to send a datagram. - * This could be due to a timeout, or something more serious such as the data being too large to fit in a sigle packet. -**/ -- (void)onUdpSocket:(AsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error; - -/** - * Called when the socket has received the requested datagram. - * - * Due to the nature of UDP, you may occasionally receive undesired packets. - * These may be rogue UDP packets from unknown hosts, - * or they may be delayed packets arriving after retransmissions have already occurred. - * It's important these packets are properly ignored, while not interfering with the flow of your implementation. - * As an aid, this delegate method has a boolean return value. - * If you ever need to ignore a received packet, simply return NO, - * and AsyncUdpSocket will continue as if the packet never arrived. - * That is, the original receive request will still be queued, and will still timeout as usual if a timeout was set. - * For example, say you requested to receive data, and you set a timeout of 500 milliseconds, using a tag of 15. - * If rogue data arrives after 250 milliseconds, this delegate method would be invoked, and you could simply return NO. - * If the expected data then arrives within the next 250 milliseconds, - * this delegate method will be invoked, with a tag of 15, just as if the rogue data never appeared. - * - * Under normal circumstances, you simply return YES from this method. -**/ -- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port; - -/** - * Called if an error occurs while trying to receive a requested datagram. - * This is generally due to a timeout, but could potentially be something else if some kind of OS error occurred. -**/ -- (void)onUdpSocket:(AsyncUdpSocket *)sock didNotReceiveDataWithTag:(long)tag dueToError:(NSError *)error; - -/** - * Called when the socket is closed. - * A socket is only closed if you explicitly call one of the close methods. -**/ -- (void)onUdpSocketDidClose:(AsyncUdpSocket *)sock; - -@end diff --git a/AsyncSocket/AsyncUdpSocket.m b/AsyncSocket/AsyncUdpSocket.m deleted file mode 100755 index 8f903b7..0000000 --- a/AsyncSocket/AsyncUdpSocket.m +++ /dev/null @@ -1,2343 +0,0 @@ -// -// AsyncUdpSocket.m -// -// This class is in the public domain. -// Originally created by Robbie Hanson on Wed Oct 01 2008. -// Updated and maintained by Deusty Designs and the Mac development community. -// -// http://code.google.com/p/cocoaasyncsocket/ -// - -#import "AsyncUdpSocket.h" -#import -#import -#import -#import -#import -#import - -#if TARGET_OS_IPHONE -// Note: You may need to add the CFNetwork Framework to your project -#import -#endif - - -#define SENDQUEUE_CAPACITY 5 // Initial capacity -#define RECEIVEQUEUE_CAPACITY 5 // Initial capacity - -#define DEFAULT_MAX_RECEIVE_BUFFER_SIZE 9216 - -NSString *const AsyncUdpSocketException = @"AsyncUdpSocketException"; -NSString *const AsyncUdpSocketErrorDomain = @"AsyncUdpSocketErrorDomain"; - -#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 -// Mutex lock used by all instances of AsyncUdpSocket, to protect getaddrinfo. -// Prior to Mac OS X 10.5 this method was not thread-safe. -static NSString *getaddrinfoLock = @"lock"; -#endif - -enum AsyncUdpSocketFlags -{ - kDidBind = 1 << 0, // If set, bind has been called. - kDidConnect = 1 << 1, // If set, connect has been called. - kSock4CanAcceptBytes = 1 << 2, // If set, we know socket4 can accept bytes. If unset, it's unknown. - kSock6CanAcceptBytes = 1 << 3, // If set, we know socket6 can accept bytes. If unset, it's unknown. - kSock4HasBytesAvailable = 1 << 4, // If set, we know socket4 has bytes available. If unset, it's unknown. - kSock6HasBytesAvailable = 1 << 5, // If set, we know socket6 has bytes available. If unset, it's unknown. - kForbidSendReceive = 1 << 6, // If set, no new send or receive operations are allowed to be queued. - kCloseAfterSends = 1 << 7, // If set, close as soon as no more sends are queued. - kCloseAfterReceives = 1 << 8, // If set, close as soon as no more receives are queued. - kDidClose = 1 << 9, // If set, the socket has been closed, and should not be used anymore. - kDequeueSendScheduled = 1 << 10, // If set, a maybeDequeueSend operation is already scheduled. - kDequeueReceiveScheduled = 1 << 11, // If set, a maybeDequeueReceive operation is already scheduled. - kFlipFlop = 1 << 12, // Used to alternate between IPv4 and IPv6 sockets. -}; - -@interface AsyncUdpSocket (Private) - -// Run Loop -- (void)runLoopAddSource:(CFRunLoopSourceRef)source; -- (void)runLoopRemoveSource:(CFRunLoopSourceRef)source; -- (void)runLoopAddTimer:(NSTimer *)timer; -- (void)runLoopRemoveTimer:(NSTimer *)timer; - -// Utilities -- (NSString *)addressHost4:(struct sockaddr_in *)pSockaddr4; -- (NSString *)addressHost6:(struct sockaddr_in6 *)pSockaddr6; -- (NSString *)addressHost:(struct sockaddr *)pSockaddr; - -// Disconnect Implementation -- (void)emptyQueues; -- (void)closeSocket4; -- (void)closeSocket6; -- (void)maybeScheduleClose; - -// Errors -- (NSError *)getErrnoError; -- (NSError *)getSocketError; -- (NSError *)getIPv4UnavailableError; -- (NSError *)getIPv6UnavailableError; -- (NSError *)getSendTimeoutError; -- (NSError *)getReceiveTimeoutError; - -// Diagnostics -- (NSString *)connectedHost:(CFSocketRef)socket; -- (UInt16)connectedPort:(CFSocketRef)socket; -- (NSString *)localHost:(CFSocketRef)socket; -- (UInt16)localPort:(CFSocketRef)socket; - -// Sending -- (BOOL)canAcceptBytes:(CFSocketRef)sockRef; -- (void)scheduleDequeueSend; -- (void)maybeDequeueSend; -- (void)doSend:(CFSocketRef)sockRef; -- (void)completeCurrentSend; -- (void)failCurrentSend:(NSError *)error; -- (void)endCurrentSend; -- (void)doSendTimeout:(NSTimer *)timer; - -// Receiving -- (BOOL)hasBytesAvailable:(CFSocketRef)sockRef; -- (void)scheduleDequeueReceive; -- (void)maybeDequeueReceive; -- (void)doReceive4; -- (void)doReceive6; -- (void)doReceive:(CFSocketRef)sockRef; -- (BOOL)maybeCompleteCurrentReceive; -- (void)failCurrentReceive:(NSError *)error; -- (void)endCurrentReceive; -- (void)doReceiveTimeout:(NSTimer *)timer; - -@end - -static void MyCFSocketCallback(CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *); - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * The AsyncSendPacket encompasses the instructions for a single send/write. -**/ -@interface AsyncSendPacket : NSObject -{ -@public - NSData *buffer; - NSData *address; - NSTimeInterval timeout; - long tag; -} -- (id)initWithData:(NSData *)d address:(NSData *)a timeout:(NSTimeInterval)t tag:(long)i; -@end - -@implementation AsyncSendPacket - -- (id)initWithData:(NSData *)d address:(NSData *)a timeout:(NSTimeInterval)t tag:(long)i -{ - if((self = [super init])) - { - buffer = [d retain]; - address = [a retain]; - timeout = t; - tag = i; - } - return self; -} - -- (void)dealloc -{ - [buffer release]; - [address release]; - [super dealloc]; -} - -@end - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * The AsyncReceivePacket encompasses the instructions for a single receive/read. -**/ -@interface AsyncReceivePacket : NSObject -{ -@public - NSTimeInterval timeout; - long tag; - NSMutableData *buffer; - NSString *host; - UInt16 port; -} -- (id)initWithTimeout:(NSTimeInterval)t tag:(long)i; -@end - -@implementation AsyncReceivePacket - -- (id)initWithTimeout:(NSTimeInterval)t tag:(long)i -{ - if((self = [super init])) - { - timeout = t; - tag = i; - - buffer = nil; - host = nil; - port = 0; - } - return self; -} - -- (void)dealloc -{ - [buffer release]; - [host release]; - [super dealloc]; -} - -@end - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -@implementation AsyncUdpSocket - -- (id)initWithDelegate:(id)delegate userData:(long)userData enableIPv4:(BOOL)enableIPv4 enableIPv6:(BOOL)enableIPv6 -{ - if((self = [super init])) - { - theFlags = 0; - theDelegate = delegate; - theUserData = userData; - maxReceiveBufferSize = DEFAULT_MAX_RECEIVE_BUFFER_SIZE; - - theSendQueue = [[NSMutableArray alloc] initWithCapacity:SENDQUEUE_CAPACITY]; - theCurrentSend = nil; - theSendTimer = nil; - - theReceiveQueue = [[NSMutableArray alloc] initWithCapacity:RECEIVEQUEUE_CAPACITY]; - theCurrentReceive = nil; - theReceiveTimer = nil; - - // Socket context - theContext.version = 0; - theContext.info = self; - theContext.retain = nil; - theContext.release = nil; - theContext.copyDescription = nil; - - // Create the sockets - theSocket4 = NULL; - theSocket6 = NULL; - - if(enableIPv4) - { - theSocket4 = CFSocketCreate(kCFAllocatorDefault, - PF_INET, - SOCK_DGRAM, - IPPROTO_UDP, - kCFSocketReadCallBack | kCFSocketWriteCallBack, - (CFSocketCallBack)&MyCFSocketCallback, - &theContext); - } - if(enableIPv6) - { - theSocket6 = CFSocketCreate(kCFAllocatorDefault, - PF_INET6, - SOCK_DGRAM, - IPPROTO_UDP, - kCFSocketReadCallBack | kCFSocketWriteCallBack, - (CFSocketCallBack)&MyCFSocketCallback, - &theContext); - } - - // Disable continuous callbacks for read and write. - // If we don't do this, the socket(s) will just sit there firing read callbacks - // at us hundreds of times a second if we don't immediately read the available data. - if(theSocket4) - { - CFSocketSetSocketFlags(theSocket4, kCFSocketCloseOnInvalidate); - } - if(theSocket6) - { - CFSocketSetSocketFlags(theSocket6, kCFSocketCloseOnInvalidate); - } - - // Get the CFRunLoop to which the socket should be attached. - theRunLoop = CFRunLoopGetCurrent(); - - // Set default run loop modes - theRunLoopModes = [[NSArray arrayWithObject:NSDefaultRunLoopMode] retain]; - - // Attach the sockets to the run loop - - if(theSocket4) - { - theSource4 = CFSocketCreateRunLoopSource(kCFAllocatorDefault, theSocket4, 0); - [self runLoopAddSource:theSource4]; - } - - if(theSocket6) - { - theSource6 = CFSocketCreateRunLoopSource(kCFAllocatorDefault, theSocket6, 0); - [self runLoopAddSource:theSource6]; - } - - cachedLocalPort = 0; - cachedConnectedPort = 0; - } - return self; -} - -- (id)init -{ - return [self initWithDelegate:nil userData:0 enableIPv4:YES enableIPv6:YES]; -} - -- (id)initWithDelegate:(id)delegate -{ - return [self initWithDelegate:delegate userData:0 enableIPv4:YES enableIPv6:YES]; -} - -- (id)initWithDelegate:(id)delegate userData:(long)userData -{ - return [self initWithDelegate:delegate userData:userData enableIPv4:YES enableIPv6:YES]; -} - -- (id)initIPv4 -{ - return [self initWithDelegate:nil userData:0 enableIPv4:YES enableIPv6:NO]; -} - -- (id)initIPv6 -{ - return [self initWithDelegate:nil userData:0 enableIPv4:NO enableIPv6:YES]; -} - -- (void) dealloc -{ - [self close]; - [theSendQueue release]; - [theReceiveQueue release]; - [theRunLoopModes release]; - [cachedLocalHost release]; - [cachedConnectedHost release]; - [NSObject cancelPreviousPerformRequestsWithTarget:theDelegate selector:@selector(onUdpSocketDidClose:) object:self]; - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - [super dealloc]; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Accessors -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (id)delegate -{ - return theDelegate; -} - -- (void)setDelegate:(id)delegate -{ - theDelegate = delegate; -} - -- (long)userData -{ - return theUserData; -} - -- (void)setUserData:(long)userData -{ - theUserData = userData; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Run Loop -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (void)runLoopAddSource:(CFRunLoopSourceRef)source -{ - unsigned i, count = [theRunLoopModes count]; - for(i = 0; i < count; i++) - { - CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i]; - CFRunLoopAddSource(theRunLoop, source, runLoopMode); - } -} - -- (void)runLoopRemoveSource:(CFRunLoopSourceRef)source -{ - unsigned i, count = [theRunLoopModes count]; - for(i = 0; i < count; i++) - { - CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i]; - CFRunLoopRemoveSource(theRunLoop, source, runLoopMode); - } -} - -- (void)runLoopAddTimer:(NSTimer *)timer -{ - unsigned i, count = [theRunLoopModes count]; - for(i = 0; i < count; i++) - { - CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i]; - CFRunLoopAddTimer(theRunLoop, (CFRunLoopTimerRef)timer, runLoopMode); - } -} - -- (void)runLoopRemoveTimer:(NSTimer *)timer -{ - unsigned i, count = [theRunLoopModes count]; - for(i = 0; i < count; i++) - { - CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i]; - CFRunLoopRemoveTimer(theRunLoop, (CFRunLoopTimerRef)timer, runLoopMode); - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Configuration -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (UInt32)maxReceiveBufferSize -{ - return maxReceiveBufferSize; -} - -- (void)setMaxReceiveBufferSize:(UInt32)max -{ - maxReceiveBufferSize = max; -} - -/** - * See the header file for a full explanation of this method. -**/ -- (BOOL)moveToRunLoop:(NSRunLoop *)runLoop -{ - NSAssert((theRunLoop == NULL) || (theRunLoop == CFRunLoopGetCurrent()), - @"moveToRunLoop must be called from within the current RunLoop!"); - - if(runLoop == nil) - { - return NO; - } - if(theRunLoop == [runLoop getCFRunLoop]) - { - return YES; - } - - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - theFlags &= ~kDequeueSendScheduled; - theFlags &= ~kDequeueReceiveScheduled; - - if(theSource4) [self runLoopRemoveSource:theSource4]; - if(theSource6) [self runLoopRemoveSource:theSource6]; - - // We do not retain the timers - they get retained by the runloop when we add them as a source. - // Since we're about to remove them as a source, we retain now, and release again below. - [theSendTimer retain]; - [theReceiveTimer retain]; - - if(theSendTimer) [self runLoopRemoveTimer:theSendTimer]; - if(theReceiveTimer) [self runLoopRemoveTimer:theReceiveTimer]; - - theRunLoop = [runLoop getCFRunLoop]; - - if(theSendTimer) [self runLoopAddTimer:theSendTimer]; - if(theReceiveTimer) [self runLoopAddTimer:theReceiveTimer]; - - // Release timers since we retained them above - [theSendTimer release]; - [theReceiveTimer release]; - - if(theSource4) [self runLoopAddSource:theSource4]; - if(theSource6) [self runLoopAddSource:theSource6]; - - [runLoop performSelector:@selector(maybeDequeueSend) target:self argument:nil order:0 modes:theRunLoopModes]; - [runLoop performSelector:@selector(maybeDequeueReceive) target:self argument:nil order:0 modes:theRunLoopModes]; - [runLoop performSelector:@selector(maybeScheduleClose) target:self argument:nil order:0 modes:theRunLoopModes]; - - return YES; -} - -/** - * See the header file for a full explanation of this method. -**/ -- (BOOL)setRunLoopModes:(NSArray *)runLoopModes -{ - NSAssert((theRunLoop == NULL) || (theRunLoop == CFRunLoopGetCurrent()), - @"setRunLoopModes must be called from within the current RunLoop!"); - - if([runLoopModes count] == 0) - { - return NO; - } - if([theRunLoopModes isEqualToArray:runLoopModes]) - { - return YES; - } - - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - theFlags &= ~kDequeueSendScheduled; - theFlags &= ~kDequeueReceiveScheduled; - - if(theSource4) [self runLoopRemoveSource:theSource4]; - if(theSource6) [self runLoopRemoveSource:theSource6]; - - // We do not retain the timers - they get retained by the runloop when we add them as a source. - // Since we're about to remove them as a source, we retain now, and release again below. - [theSendTimer retain]; - [theReceiveTimer retain]; - - if(theSendTimer) [self runLoopRemoveTimer:theSendTimer]; - if(theReceiveTimer) [self runLoopRemoveTimer:theReceiveTimer]; - - [theRunLoopModes release]; - theRunLoopModes = [runLoopModes copy]; - - if(theSendTimer) [self runLoopAddTimer:theSendTimer]; - if(theReceiveTimer) [self runLoopAddTimer:theReceiveTimer]; - - // Release timers since we retained them above - [theSendTimer release]; - [theReceiveTimer release]; - - if(theSource4) [self runLoopAddSource:theSource4]; - if(theSource6) [self runLoopAddSource:theSource6]; - - [self performSelector:@selector(maybeDequeueSend) withObject:nil afterDelay:0 inModes:theRunLoopModes]; - [self performSelector:@selector(maybeDequeueReceive) withObject:nil afterDelay:0 inModes:theRunLoopModes]; - [self performSelector:@selector(maybeScheduleClose) withObject:nil afterDelay:0 inModes:theRunLoopModes]; - - return YES; -} - -- (NSArray *)runLoopModes -{ - return [[theRunLoopModes retain] autorelease]; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Utilities: -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Attempts to convert the given host/port into and IPv4 and/or IPv6 data structure. - * The data structure is of type sockaddr_in for IPv4 and sockaddr_in6 for IPv6. - * - * Returns zero on success, or one of the error codes listed in gai_strerror if an error occurs (as per getaddrinfo). -**/ -- (int)convertForBindHost:(NSString *)host - port:(UInt16)port - intoAddress4:(NSData **)address4 - address6:(NSData **)address6 -{ - if(host == nil || ([host length] == 0)) - { - // Use ANY address - struct sockaddr_in nativeAddr; - nativeAddr.sin_len = sizeof(struct sockaddr_in); - nativeAddr.sin_family = AF_INET; - nativeAddr.sin_port = htons(port); - nativeAddr.sin_addr.s_addr = htonl(INADDR_ANY); - memset(&(nativeAddr.sin_zero), 0, sizeof(nativeAddr.sin_zero)); - - struct sockaddr_in6 nativeAddr6; - nativeAddr6.sin6_len = sizeof(struct sockaddr_in6); - nativeAddr6.sin6_family = AF_INET6; - nativeAddr6.sin6_port = htons(port); - nativeAddr6.sin6_flowinfo = 0; - nativeAddr6.sin6_addr = in6addr_any; - nativeAddr6.sin6_scope_id = 0; - - // Wrap the native address structures for CFSocketSetAddress. - if(address4) *address4 = [NSData dataWithBytes:&nativeAddr length:sizeof(nativeAddr)]; - if(address6) *address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; - - return 0; - } - else if([host isEqualToString:@"localhost"] || [host isEqualToString:@"loopback"]) - { - // Note: getaddrinfo("localhost",...) fails on 10.5.3 - - // Use LOOPBACK address - struct sockaddr_in nativeAddr; - nativeAddr.sin_len = sizeof(struct sockaddr_in); - nativeAddr.sin_family = AF_INET; - nativeAddr.sin_port = htons(port); - nativeAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - memset(&(nativeAddr.sin_zero), 0, sizeof(nativeAddr.sin_zero)); - - struct sockaddr_in6 nativeAddr6; - nativeAddr6.sin6_len = sizeof(struct sockaddr_in6); - nativeAddr6.sin6_family = AF_INET6; - nativeAddr6.sin6_port = htons(port); - nativeAddr6.sin6_flowinfo = 0; - nativeAddr6.sin6_addr = in6addr_loopback; - nativeAddr6.sin6_scope_id = 0; - - // Wrap the native address structures for CFSocketSetAddress. - if(address4) *address4 = [NSData dataWithBytes:&nativeAddr length:sizeof(nativeAddr)]; - if(address6) *address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; - - return 0; - } - else - { - NSString *portStr = [NSString stringWithFormat:@"%hu", port]; - -#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - @synchronized (getaddrinfoLock) -#endif - { - struct addrinfo hints, *res, *res0; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = IPPROTO_UDP; - hints.ai_flags = AI_PASSIVE; - - int error = getaddrinfo([host UTF8String], [portStr UTF8String], &hints, &res0); - - if(error) return error; - - for(res = res0; res; res = res->ai_next) - { - if(address4 && !*address4 && (res->ai_family == AF_INET)) - { - // Found IPv4 address - // Wrap the native address structures for CFSocketSetAddress. - if(address4) *address4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]; - } - else if(address6 && !*address6 && (res->ai_family == AF_INET6)) - { - // Found IPv6 address - // Wrap the native address structures for CFSocketSetAddress. - if(address6) *address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]; - } - } - freeaddrinfo(res0); - } - - return 0; - } -} - -/** - * Attempts to convert the given host/port into and IPv4 and/or IPv6 data structure. - * The data structure is of type sockaddr_in for IPv4 and sockaddr_in6 for IPv6. - * - * Returns zero on success, or one of the error codes listed in gai_strerror if an error occurs (as per getaddrinfo). -**/ -- (int)convertForSendHost:(NSString *)host - port:(UInt16)port - intoAddress4:(NSData **)address4 - address6:(NSData **)address6 -{ - if(host == nil || ([host length] == 0)) - { - // We're not binding, so what are we supposed to do with this? - return EAI_NONAME; - } - else if([host isEqualToString:@"localhost"] || [host isEqualToString:@"loopback"]) - { - // Note: getaddrinfo("localhost",...) fails on 10.5.3 - - // Use LOOPBACK address - struct sockaddr_in nativeAddr; - nativeAddr.sin_len = sizeof(struct sockaddr_in); - nativeAddr.sin_family = AF_INET; - nativeAddr.sin_port = htons(port); - nativeAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - memset(&(nativeAddr.sin_zero), 0, sizeof(nativeAddr.sin_zero)); - - struct sockaddr_in6 nativeAddr6; - nativeAddr6.sin6_len = sizeof(struct sockaddr_in6); - nativeAddr6.sin6_family = AF_INET6; - nativeAddr6.sin6_port = htons(port); - nativeAddr6.sin6_flowinfo = 0; - nativeAddr6.sin6_addr = in6addr_loopback; - nativeAddr6.sin6_scope_id = 0; - - // Wrap the native address structures for CFSocketSetAddress. - if(address4) *address4 = [NSData dataWithBytes:&nativeAddr length:sizeof(nativeAddr)]; - if(address6) *address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; - - return 0; - } - else - { - NSString *portStr = [NSString stringWithFormat:@"%hu", port]; - -#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - @synchronized (getaddrinfoLock) -#endif - { - struct addrinfo hints, *res, *res0; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = IPPROTO_UDP; - // No passive flag on a send or connect - - int error = getaddrinfo([host UTF8String], [portStr UTF8String], &hints, &res0); - - if(error) return error; - - for(res = res0; res; res = res->ai_next) - { - if(address4 && !*address4 && (res->ai_family == AF_INET)) - { - // Found IPv4 address - // Wrap the native address structures for CFSocketSetAddress. - if(address4) *address4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]; - } - else if(address6 && !*address6 && (res->ai_family == AF_INET6)) - { - // Found IPv6 address - // Wrap the native address structures for CFSocketSetAddress. - if(address6) *address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]; - } - } - freeaddrinfo(res0); - } - - return 0; - } -} - -- (NSString *)addressHost4:(struct sockaddr_in *)pSockaddr4 -{ - char addrBuf[INET_ADDRSTRLEN]; - - if(inet_ntop(AF_INET, &pSockaddr4->sin_addr, addrBuf, sizeof(addrBuf)) == NULL) - { - [NSException raise:NSInternalInconsistencyException format:@"Cannot convert address to string."]; - } - - return [NSString stringWithCString:addrBuf encoding:NSASCIIStringEncoding]; -} - -- (NSString *)addressHost6:(struct sockaddr_in6 *)pSockaddr6 -{ - char addrBuf[INET6_ADDRSTRLEN]; - - if(inet_ntop(AF_INET6, &pSockaddr6->sin6_addr, addrBuf, sizeof(addrBuf)) == NULL) - { - [NSException raise:NSInternalInconsistencyException format:@"Cannot convert address to string."]; - } - - return [NSString stringWithCString:addrBuf encoding:NSASCIIStringEncoding]; -} - -- (NSString *)addressHost:(struct sockaddr *)pSockaddr -{ - if(pSockaddr->sa_family == AF_INET) - { - return [self addressHost4:(struct sockaddr_in *)pSockaddr]; - } - else - { - return [self addressHost6:(struct sockaddr_in6 *)pSockaddr]; - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Socket Implementation: -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Binds the underlying socket(s) to the given port. - * The socket(s) will be able to receive data on any interface. - * - * On success, returns YES. - * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. -**/ -- (BOOL)bindToPort:(UInt16)port error:(NSError **)errPtr -{ - return [self bindToAddress:nil port:port error:errPtr]; -} - -/** - * Binds the underlying socket(s) to the given address and port. - * The sockets(s) will be able to receive data only on the given interface. - * - * To receive data on any interface, pass nil or "". - * To receive data only on the loopback interface, pass "localhost" or "loopback". - * - * On success, returns YES. - * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. -**/ -- (BOOL)bindToAddress:(NSString *)host port:(UInt16)port error:(NSError **)errPtr -{ - if(theFlags & kDidClose) - { - [NSException raise:AsyncUdpSocketException - format:@"The socket is closed."]; - } - if(theFlags & kDidBind) - { - [NSException raise:AsyncUdpSocketException - format:@"Cannot bind a socket more than once."]; - } - if(theFlags & kDidConnect) - { - [NSException raise:AsyncUdpSocketException - format:@"Cannot bind after connecting. If needed, bind first, then connect."]; - } - - // Convert the given host/port into native address structures for CFSocketSetAddress - NSData *address4 = nil, *address6 = nil; - - int gai_error = [self convertForBindHost:host port:port intoAddress4:&address4 address6:&address6]; - if(gai_error) - { - if(errPtr) - { - NSString *errMsg = [NSString stringWithCString:gai_strerror(gai_error) encoding:NSASCIIStringEncoding]; - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:gai_error userInfo:info]; - } - return NO; - } - - NSAssert((address4 || address6), @"address4 and address6 are nil"); - - // Set the SO_REUSEADDR flags - - int reuseOn = 1; - if (theSocket4) setsockopt(CFSocketGetNative(theSocket4), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn)); - if (theSocket6) setsockopt(CFSocketGetNative(theSocket6), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn)); - - // Bind the sockets - - if(address4) - { - if(theSocket4) - { - CFSocketError error = CFSocketSetAddress(theSocket4, (CFDataRef)address4); - if(error != kCFSocketSuccess) - { - if(errPtr) *errPtr = [self getSocketError]; - return NO; - } - - if(!address6) - { - // Using IPv4 only - [self closeSocket6]; - } - } - else if(!address6) - { - if(errPtr) *errPtr = [self getIPv4UnavailableError]; - return NO; - } - } - - if(address6) - { - // Note: The iPhone doesn't currently support IPv6 - - if(theSocket6) - { - CFSocketError error = CFSocketSetAddress(theSocket6, (CFDataRef)address6); - if(error != kCFSocketSuccess) - { - if(errPtr) *errPtr = [self getSocketError]; - return NO; - } - - if(!address4) - { - // Using IPv6 only - [self closeSocket4]; - } - } - else if(!address4) - { - if(errPtr) *errPtr = [self getIPv6UnavailableError]; - return NO; - } - } - - theFlags |= kDidBind; - return YES; -} - -/** - * Connects the underlying UDP socket to the given host and port. - * If an IPv4 address is resolved, the IPv4 socket is connected, and the IPv6 socket is invalidated and released. - * If an IPv6 address is resolved, the IPv6 socket is connected, and the IPv4 socket is invalidated and released. - * - * On success, returns YES. - * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. -**/ -- (BOOL)connectToHost:(NSString *)host onPort:(UInt16)port error:(NSError **)errPtr -{ - if(theFlags & kDidClose) - { - [NSException raise:AsyncUdpSocketException - format:@"The socket is closed."]; - } - if(theFlags & kDidConnect) - { - [NSException raise:AsyncUdpSocketException - format:@"Cannot connect a socket more than once."]; - } - - // Convert the given host/port into native address structures for CFSocketSetAddress - NSData *address4 = nil, *address6 = nil; - - int error = [self convertForSendHost:host port:port intoAddress4:&address4 address6:&address6]; - if(error) - { - if(errPtr) - { - NSString *errMsg = [NSString stringWithCString:gai_strerror(error) encoding:NSASCIIStringEncoding]; - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:error userInfo:info]; - } - return NO; - } - - NSAssert((address4 || address6), @"address4 and address6 are nil"); - - // We only want to connect via a single interface. - // IPv4 is currently preferred, but this may change in the future. - - if(address4) - { - if(theSocket4) - { - CFSocketError sockErr = CFSocketConnectToAddress(theSocket4, (CFDataRef)address4, (CFTimeInterval)0.0); - if(sockErr != kCFSocketSuccess) - { - if(errPtr) *errPtr = [self getSocketError]; - return NO; - } - theFlags |= kDidConnect; - - // We're connected to an IPv4 address, so no need for the IPv6 socket - [self closeSocket6]; - - return YES; - } - else if(!address6) - { - if(errPtr) *errPtr = [self getIPv4UnavailableError]; - return NO; - } - } - - if(address6) - { - // Note: The iPhone doesn't currently support IPv6 - - if(theSocket6) - { - CFSocketError sockErr = CFSocketConnectToAddress(theSocket6, (CFDataRef)address6, (CFTimeInterval)0.0); - if(sockErr != kCFSocketSuccess) - { - if(errPtr) *errPtr = [self getSocketError]; - return NO; - } - theFlags |= kDidConnect; - - // We're connected to an IPv6 address, so no need for the IPv4 socket - [self closeSocket4]; - - return YES; - } - else - { - if(errPtr) *errPtr = [self getIPv6UnavailableError]; - return NO; - } - } - - // It shouldn't be possible to get to this point because either address4 or address6 was non-nil. - if(errPtr) *errPtr = nil; - return NO; -} - -/** - * Connects the underlying UDP socket to the remote address. - * If the address is an IPv4 address, the IPv4 socket is connected, and the IPv6 socket is invalidated and released. - * If the address is an IPv6 address, the IPv6 socket is connected, and the IPv4 socket is invalidated and released. - * - * The address is a native address structure, as may be returned from API's such as Bonjour. - * An address may be created manually by simply wrapping a sockaddr_in or sockaddr_in6 in an NSData object. - * - * On success, returns YES. - * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. -**/ -- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr -{ - if(theFlags & kDidClose) - { - [NSException raise:AsyncUdpSocketException - format:@"The socket is closed."]; - } - if(theFlags & kDidConnect) - { - [NSException raise:AsyncUdpSocketException - format:@"Cannot connect a socket more than once."]; - } - - // Is remoteAddr an IPv4 address? - if([remoteAddr length] == sizeof(struct sockaddr_in)) - { - if(theSocket4) - { - CFSocketError error = CFSocketConnectToAddress(theSocket4, (CFDataRef)remoteAddr, (CFTimeInterval)0.0); - if(error != kCFSocketSuccess) - { - if(errPtr) *errPtr = [self getSocketError]; - return NO; - } - theFlags |= kDidConnect; - - // We're connected to an IPv4 address, so no need for the IPv6 socket - [self closeSocket6]; - - return YES; - } - else - { - if(errPtr) *errPtr = [self getIPv4UnavailableError]; - return NO; - } - } - - // Is remoteAddr an IPv6 address? - if([remoteAddr length] == sizeof(struct sockaddr_in6)) - { - if(theSocket6) - { - CFSocketError error = CFSocketConnectToAddress(theSocket6, (CFDataRef)remoteAddr, (CFTimeInterval)0.0); - if(error != kCFSocketSuccess) - { - if(errPtr) *errPtr = [self getSocketError]; - return NO; - } - theFlags |= kDidConnect; - - // We're connected to an IPv6 address, so no need for the IPv4 socket - [self closeSocket4]; - - return YES; - } - else - { - if(errPtr) *errPtr = [self getIPv6UnavailableError]; - return NO; - } - } - - // The remoteAddr was invalid - if(errPtr) - { - NSString *errMsg = @"remoteAddr parameter is not a valid address"; - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - *errPtr = [NSError errorWithDomain:AsyncUdpSocketErrorDomain - code:AsyncUdpSocketBadParameter - userInfo:info]; - } - return NO; -} - -/** - * Join multicast group - * - * Group should be a multicast IP address (eg. @"239.255.250.250" for IPv4). - * Address is local interface for IPv4, but currently defaults under IPv6. -**/ -- (BOOL)joinMulticastGroup:(NSString *)group error:(NSError **)errPtr -{ - return [self joinMulticastGroup:group withAddress:nil error:errPtr]; -} - -- (BOOL)joinMulticastGroup:(NSString *)group withAddress:(NSString *)address error:(NSError **)errPtr -{ - if(theFlags & kDidClose) - { - [NSException raise:AsyncUdpSocketException - format:@"The socket is closed."]; - } - if(!(theFlags & kDidBind)) - { - [NSException raise:AsyncUdpSocketException - format:@"Must bind a socket before joining a multicast group."]; - } - if(theFlags & kDidConnect) - { - [NSException raise:AsyncUdpSocketException - format:@"Cannot join a multicast group if connected."]; - } - - // Get local interface address - // Convert the given host/port into native address structures for CFSocketSetAddress - NSData *address4 = nil, *address6 = nil; - - int error = [self convertForBindHost:address port:0 intoAddress4:&address4 address6:&address6]; - if(error) - { - if(errPtr) - { - NSString *errMsg = [NSString stringWithCString:gai_strerror(error) encoding:NSASCIIStringEncoding]; - NSString *errDsc = [NSString stringWithFormat:@"Invalid parameter 'address': %@", errMsg]; - NSDictionary *info = [NSDictionary dictionaryWithObject:errDsc forKey:NSLocalizedDescriptionKey]; - - *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:error userInfo:info]; - } - return NO; - } - - NSAssert((address4 || address6), @"address4 and address6 are nil"); - - // Get multicast address (group) - NSData *group4 = nil, *group6 = nil; - - error = [self convertForBindHost:group port:0 intoAddress4:&group4 address6:&group6]; - if(error) - { - if(errPtr) - { - NSString *errMsg = [NSString stringWithCString:gai_strerror(error) encoding:NSASCIIStringEncoding]; - NSString *errDsc = [NSString stringWithFormat:@"Invalid parameter 'group': %@", errMsg]; - NSDictionary *info = [NSDictionary dictionaryWithObject:errDsc forKey:NSLocalizedDescriptionKey]; - - *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:error userInfo:info]; - } - return NO; - } - - NSAssert((group4 || group6), @"group4 and group6 are nil"); - - if(theSocket4 && group4 && address4) - { - const struct sockaddr_in* nativeAddress = [address4 bytes]; - const struct sockaddr_in* nativeGroup = [group4 bytes]; - - struct ip_mreq imreq; - imreq.imr_multiaddr = nativeGroup->sin_addr; - imreq.imr_interface = nativeAddress->sin_addr; - - // JOIN multicast group on default interface - error = setsockopt(CFSocketGetNative(theSocket4), IPPROTO_IP, IP_ADD_MEMBERSHIP, - (const void *)&imreq, sizeof(struct ip_mreq)); - if(error) - { - if(errPtr) - { - NSString *errMsg = @"Unable to join IPv4 multicast group"; - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainPOSIX" code:error userInfo:info]; - } - return NO; - } - - // Using IPv4 only - [self closeSocket6]; - - return YES; - } - - if(theSocket6 && group6 && address6) - { - const struct sockaddr_in6* nativeGroup = [group6 bytes]; - - struct ipv6_mreq imreq; - imreq.ipv6mr_multiaddr = nativeGroup->sin6_addr; - imreq.ipv6mr_interface = 0; - - // JOIN multicast group on default interface - error = setsockopt(CFSocketGetNative(theSocket6), IPPROTO_IP, IPV6_JOIN_GROUP, - (const void *)&imreq, sizeof(struct ipv6_mreq)); - if(error) - { - if(errPtr) - { - NSString *errMsg = @"Unable to join IPv6 multicast group"; - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainPOSIX" code:error userInfo:info]; - } - return NO; - } - - // Using IPv6 only - [self closeSocket4]; - - return YES; - } - - // The given address and group didn't match the existing socket(s). - // This means there were no compatible combination of all IPv4 or IPv6 socket, group and address. - if(errPtr) - { - NSString *errMsg = @"Invalid group and/or address, not matching existing socket(s)"; - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - *errPtr = [NSError errorWithDomain:AsyncUdpSocketErrorDomain - code:AsyncUdpSocketBadParameter - userInfo:info]; - } - return NO; -} - -/** - * By default, the underlying socket in the OS will not allow you to send broadcast messages. - * In order to send broadcast messages, you need to enable this functionality in the socket. - * - * A broadcast is a UDP message to addresses like "192.168.255.255" or "255.255.255.255" that is - * delivered to every host on the network. - * The reason this is generally disabled by default is to prevent - * accidental broadcast messages from flooding the network. -**/ -- (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr -{ - if (theSocket4) - { - int value = flag ? 1 : 0; - int error = setsockopt(CFSocketGetNative(theSocket4), SOL_SOCKET, SO_BROADCAST, - (const void *)&value, sizeof(value)); - if(error) - { - if(errPtr) - { - NSString *errMsg = @"Unable to enable broadcast message sending"; - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainPOSIX" code:error userInfo:info]; - } - return NO; - } - } - - // IPv6 does not implement broadcast, the ability to send a packet to all hosts on the attached link. - // The same effect can be achieved by sending a packet to the link-local all hosts multicast group. - - return YES; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Disconnect Implementation: -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (void)emptyQueues -{ - if (theCurrentSend) [self endCurrentSend]; - if (theCurrentReceive) [self endCurrentReceive]; - - [theSendQueue removeAllObjects]; - [theReceiveQueue removeAllObjects]; - - [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueSend) object:nil]; - [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueReceive) object:nil]; - - theFlags &= ~kDequeueSendScheduled; - theFlags &= ~kDequeueReceiveScheduled; -} - -- (void)closeSocket4 -{ - if (theSocket4 != NULL) - { - CFSocketInvalidate(theSocket4); - CFRelease(theSocket4); - theSocket4 = NULL; - } - if (theSource4 != NULL) - { - [self runLoopRemoveSource:theSource4]; - CFRelease(theSource4); - theSource4 = NULL; - } -} - -- (void)closeSocket6 -{ - if (theSocket6 != NULL) - { - CFSocketInvalidate(theSocket6); - CFRelease(theSocket6); - theSocket6 = NULL; - } - if (theSource6 != NULL) - { - [self runLoopRemoveSource:theSource6]; - CFRelease(theSource6); - theSource6 = NULL; - } -} - -- (void)close -{ - [self emptyQueues]; - [self closeSocket4]; - [self closeSocket6]; - - theRunLoop = NULL; - - // Delay notification to give user freedom to release without returning here and core-dumping. - if ([theDelegate respondsToSelector:@selector(onUdpSocketDidClose:)]) - { - [theDelegate performSelector:@selector(onUdpSocketDidClose:) - withObject:self - afterDelay:0 - inModes:theRunLoopModes]; - } - - theFlags |= kDidClose; -} - -- (void)closeAfterSending -{ - if(theFlags & kDidClose) return; - - theFlags |= (kForbidSendReceive | kCloseAfterSends); - [self maybeScheduleClose]; -} - -- (void)closeAfterReceiving -{ - if(theFlags & kDidClose) return; - - theFlags |= (kForbidSendReceive | kCloseAfterReceives); - [self maybeScheduleClose]; -} - -- (void)closeAfterSendingAndReceiving -{ - if(theFlags & kDidClose) return; - - theFlags |= (kForbidSendReceive | kCloseAfterSends | kCloseAfterReceives); - [self maybeScheduleClose]; -} - -- (void)maybeScheduleClose -{ - BOOL shouldDisconnect = NO; - - if(theFlags & kCloseAfterSends) - { - if(([theSendQueue count] == 0) && (theCurrentSend == nil)) - { - if(theFlags & kCloseAfterReceives) - { - if(([theReceiveQueue count] == 0) && (theCurrentReceive == nil)) - { - shouldDisconnect = YES; - } - } - else - { - shouldDisconnect = YES; - } - } - } - else if(theFlags & kCloseAfterReceives) - { - if(([theReceiveQueue count] == 0) && (theCurrentReceive == nil)) - { - shouldDisconnect = YES; - } - } - - if(shouldDisconnect) - { - [self performSelector:@selector(close) withObject:nil afterDelay:0 inModes:theRunLoopModes]; - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Errors -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Returns a standard error object for the current errno value. - * Errno is used for low-level BSD socket errors. -**/ -- (NSError *)getErrnoError -{ - NSString *errorMsg = [NSString stringWithUTF8String:strerror(errno)]; - NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errorMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:userInfo]; -} - -/** - * Returns a standard error message for a CFSocket error. - * Unfortunately, CFSocket offers no feedback on its errors. -**/ -- (NSError *)getSocketError -{ - NSString *errMsg = @"General CFSocket error"; - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:AsyncUdpSocketErrorDomain code:AsyncUdpSocketCFSocketError userInfo:info]; -} - -- (NSError *)getIPv4UnavailableError -{ - NSString *errMsg = @"IPv4 is unavailable due to binding/connecting using IPv6 only"; - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:AsyncUdpSocketErrorDomain code:AsyncUdpSocketIPv4Unavailable userInfo:info]; -} - -- (NSError *)getIPv6UnavailableError -{ - NSString *errMsg = @"IPv6 is unavailable due to binding/connecting using IPv4 only or is not supported on this platform"; - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:AsyncUdpSocketErrorDomain code:AsyncUdpSocketIPv6Unavailable userInfo:info]; -} - -- (NSError *)getSendTimeoutError -{ - NSString *errMsg = @"Send operation timed out"; - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:AsyncUdpSocketErrorDomain code:AsyncUdpSocketSendTimeoutError userInfo:info]; -} -- (NSError *)getReceiveTimeoutError -{ - NSString *errMsg = @"Receive operation timed out"; - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:AsyncUdpSocketErrorDomain code:AsyncUdpSocketReceiveTimeoutError userInfo:info]; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Diagnostics -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (NSString *)localHost -{ - if(cachedLocalHost) return cachedLocalHost; - - if(theSocket4) - return [self localHost:theSocket4]; - else - return [self localHost:theSocket6]; -} - -- (UInt16)localPort -{ - if(cachedLocalPort > 0) return cachedLocalPort; - - if(theSocket4) - return [self localPort:theSocket4]; - else - return [self localPort:theSocket6]; -} - -- (NSString *)connectedHost -{ - if(cachedConnectedHost) return cachedConnectedHost; - - if(theSocket4) - return [self connectedHost:theSocket4]; - else - return [self connectedHost:theSocket6]; -} - -- (UInt16)connectedPort -{ - if(cachedConnectedPort > 0) return cachedConnectedPort; - - if(theSocket4) - return [self connectedPort:theSocket4]; - else - return [self connectedPort:theSocket6]; -} - -- (NSString *)localHost:(CFSocketRef)theSocket -{ - if(theSocket == NULL) return nil; - - // Unfortunately we can't use CFSocketCopyAddress. - // The CFSocket library caches the address the first time you call CFSocketCopyAddress. - // So if this is called prior to binding/connecting/sending, it won't be updated again when necessary, - // and will continue to return the old value of the socket address. - - NSString *result = nil; - - if(theSocket == theSocket4) - { - struct sockaddr_in sockaddr4; - socklen_t sockaddr4len = sizeof(sockaddr4); - - if(getsockname(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0) - { - return nil; - } - result = [self addressHost4:&sockaddr4]; - } - else - { - struct sockaddr_in6 sockaddr6; - socklen_t sockaddr6len = sizeof(sockaddr6); - - if(getsockname(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0) - { - return nil; - } - result = [self addressHost6:&sockaddr6]; - } - - if(theFlags & kDidBind) - { - [cachedLocalHost release]; - cachedLocalHost = [result copy]; - } - - return result; -} - -- (UInt16)localPort:(CFSocketRef)theSocket -{ - if(theSocket == NULL) return 0; - - // Unfortunately we can't use CFSocketCopyAddress. - // The CFSocket library caches the address the first time you call CFSocketCopyAddress. - // So if this is called prior to binding/connecting/sending, it won't be updated again when necessary, - // and will continue to return the old value of the socket address. - - UInt16 result = 0; - - if(theSocket == theSocket4) - { - struct sockaddr_in sockaddr4; - socklen_t sockaddr4len = sizeof(sockaddr4); - - if(getsockname(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0) - { - return 0; - } - result = ntohs(sockaddr4.sin_port); - } - else - { - struct sockaddr_in6 sockaddr6; - socklen_t sockaddr6len = sizeof(sockaddr6); - - if(getsockname(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0) - { - return 0; - } - result = ntohs(sockaddr6.sin6_port); - } - - if(theFlags & kDidBind) - { - cachedLocalPort = result; - } - - return result; -} - -- (NSString *)connectedHost:(CFSocketRef)theSocket -{ - if(theSocket == NULL) return nil; - - // Unfortunately we can't use CFSocketCopyPeerAddress. - // The CFSocket library caches the address the first time you call CFSocketCopyPeerAddress. - // So if this is called prior to binding/connecting/sending, it may not be updated again when necessary, - // and will continue to return the old value of the socket peer address. - - NSString *result = nil; - - if(theSocket == theSocket4) - { - struct sockaddr_in sockaddr4; - socklen_t sockaddr4len = sizeof(sockaddr4); - - if(getpeername(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0) - { - return nil; - } - result = [self addressHost4:&sockaddr4]; - } - else - { - struct sockaddr_in6 sockaddr6; - socklen_t sockaddr6len = sizeof(sockaddr6); - - if(getpeername(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0) - { - return nil; - } - result = [self addressHost6:&sockaddr6]; - } - - if(theFlags & kDidConnect) - { - [cachedConnectedHost release]; - cachedConnectedHost = [result copy]; - } - - return result; -} - -- (UInt16)connectedPort:(CFSocketRef)theSocket -{ - if(theSocket == NULL) return 0; - - // Unfortunately we can't use CFSocketCopyPeerAddress. - // The CFSocket library caches the address the first time you call CFSocketCopyPeerAddress. - // So if this is called prior to binding/connecting/sending, it may not be updated again when necessary, - // and will continue to return the old value of the socket peer address. - - UInt16 result = 0; - - if(theSocket == theSocket4) - { - struct sockaddr_in sockaddr4; - socklen_t sockaddr4len = sizeof(sockaddr4); - - if(getpeername(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0) - { - return 0; - } - result = ntohs(sockaddr4.sin_port); - } - else - { - struct sockaddr_in6 sockaddr6; - socklen_t sockaddr6len = sizeof(sockaddr6); - - if(getpeername(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0) - { - return 0; - } - result = ntohs(sockaddr6.sin6_port); - } - - if(theFlags & kDidConnect) - { - cachedConnectedPort = result; - } - - return result; -} - -- (BOOL)isConnected -{ - return (((theFlags & kDidConnect) != 0) && ((theFlags & kDidClose) == 0)); -} - -- (BOOL)isConnectedToHost:(NSString *)host port:(UInt16)port -{ - return [[self connectedHost] isEqualToString:host] && ([self connectedPort] == port); -} - -- (BOOL)isClosed -{ - return (theFlags & kDidClose) ? YES : NO; -} - -- (BOOL)isIPv4 -{ - return (theSocket4 != NULL); -} - -- (BOOL)isIPv6 -{ - return (theSocket6 != NULL); -} - -- (unsigned int)maximumTransmissionUnit -{ - CFSocketNativeHandle theNativeSocket; - if(theSocket4) - theNativeSocket = CFSocketGetNative(theSocket4); - else if(theSocket6) - theNativeSocket = CFSocketGetNative(theSocket6); - else - return 0; - - if(theNativeSocket == 0) - { - return 0; - } - - struct ifreq ifr; - bzero(&ifr, sizeof(ifr)); - - if(if_indextoname(theNativeSocket, ifr.ifr_name) == NULL) - { - return 0; - } - - if(ioctl(theNativeSocket, SIOCGIFMTU, &ifr) >= 0) - { - return ifr.ifr_mtu; - } - - return 0; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Sending -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (BOOL)sendData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag -{ - if((data == nil) || ([data length] == 0)) return NO; - if(theFlags & kForbidSendReceive) return NO; - if(theFlags & kDidClose) return NO; - - // This method is only for connected sockets - if(![self isConnected]) return NO; - - AsyncSendPacket *packet = [[AsyncSendPacket alloc] initWithData:data address:nil timeout:timeout tag:tag]; - - [theSendQueue addObject:packet]; - [self scheduleDequeueSend]; - - [packet release]; - return YES; -} - -- (BOOL)sendData:(NSData *)data toHost:(NSString *)host port:(UInt16)port withTimeout:(NSTimeInterval)timeout tag:(long)tag -{ - if((data == nil) || ([data length] == 0)) return NO; - if(theFlags & kForbidSendReceive) return NO; - if(theFlags & kDidClose) return NO; - - // This method is only for non-connected sockets - if([self isConnected]) return NO; - - NSData *address4 = nil, *address6 = nil; - [self convertForSendHost:host port:port intoAddress4:&address4 address6:&address6]; - - AsyncSendPacket *packet = nil; - - if(address4 && theSocket4) - packet = [[AsyncSendPacket alloc] initWithData:data address:address4 timeout:timeout tag:tag]; - else if(address6 && theSocket6) - packet = [[AsyncSendPacket alloc] initWithData:data address:address6 timeout:timeout tag:tag]; - else - return NO; - - [theSendQueue addObject:packet]; - [self scheduleDequeueSend]; - - [packet release]; - return YES; -} - -- (BOOL)sendData:(NSData *)data toAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout tag:(long)tag -{ - if((data == nil) || ([data length] == 0)) return NO; - if(theFlags & kForbidSendReceive) return NO; - if(theFlags & kDidClose) return NO; - - // This method is only for non-connected sockets - if([self isConnected]) return NO; - - if([remoteAddr length] == sizeof(struct sockaddr_in) && !theSocket4) - return NO; - - if([remoteAddr length] == sizeof(struct sockaddr_in6) && !theSocket6) - return NO; - - AsyncSendPacket *packet = [[AsyncSendPacket alloc] initWithData:data address:remoteAddr timeout:timeout tag:tag]; - - [theSendQueue addObject:packet]; - [self scheduleDequeueSend]; - - [packet release]; - return YES; -} - -- (BOOL)canAcceptBytes:(CFSocketRef)sockRef -{ - if(sockRef == theSocket4) - { - if(theFlags & kSock4CanAcceptBytes) return YES; - } - else - { - if(theFlags & kSock6CanAcceptBytes) return YES; - } - - CFSocketNativeHandle theNativeSocket = CFSocketGetNative(sockRef); - - if(theNativeSocket == 0) - { - NSLog(@"Error - Could not get CFSocketNativeHandle from CFSocketRef"); - return NO; - } - - fd_set fds; - FD_ZERO(&fds); - FD_SET(theNativeSocket, &fds); - - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 0; - - return select(FD_SETSIZE, NULL, &fds, NULL, &timeout) > 0; -} - -- (CFSocketRef)socketForPacket:(AsyncSendPacket *)packet -{ - if(!theSocket4) - return theSocket6; - if(!theSocket6) - return theSocket4; - - return ([packet->address length] == sizeof(struct sockaddr_in)) ? theSocket4 : theSocket6; -} - -/** - * Puts a maybeDequeueSend on the run loop. -**/ -- (void)scheduleDequeueSend -{ - if((theFlags & kDequeueSendScheduled) == 0) - { - theFlags |= kDequeueSendScheduled; - [self performSelector:@selector(maybeDequeueSend) withObject:nil afterDelay:0 inModes:theRunLoopModes]; - } -} - -/** - * This method starts a new send, if needed. - * It is called when a user requests a send. -**/ -- (void)maybeDequeueSend -{ - // Unset the flag indicating a call to this method is scheduled - theFlags &= ~kDequeueSendScheduled; - - if(theCurrentSend == nil) - { - if([theSendQueue count] > 0) - { - // Dequeue next send packet - theCurrentSend = [[theSendQueue objectAtIndex:0] retain]; - [theSendQueue removeObjectAtIndex:0]; - - // Start time-out timer. - if(theCurrentSend->timeout >= 0.0) - { - theSendTimer = [NSTimer timerWithTimeInterval:theCurrentSend->timeout - target:self - selector:@selector(doSendTimeout:) - userInfo:nil - repeats:NO]; - - [self runLoopAddTimer:theSendTimer]; - } - - // Immediately send, if possible. - [self doSend:[self socketForPacket:theCurrentSend]]; - } - else if(theFlags & kCloseAfterSends) - { - if(theFlags & kCloseAfterReceives) - { - if(([theReceiveQueue count] == 0) && (theCurrentReceive == nil)) - { - [self close]; - } - } - else - { - [self close]; - } - } - } -} - -/** - * This method is called when a new read is taken from the read queue or when new data becomes available on the stream. -**/ -- (void)doSend:(CFSocketRef)theSocket -{ - if(theCurrentSend != nil) - { - if(theSocket != [self socketForPacket:theCurrentSend]) - { - // Current send is for the other socket - return; - } - - if([self canAcceptBytes:theSocket]) - { - int result; - CFSocketNativeHandle theNativeSocket = CFSocketGetNative(theSocket); - - const void *buf = [theCurrentSend->buffer bytes]; - unsigned bufSize = [theCurrentSend->buffer length]; - - if([self isConnected]) - { - result = send(theNativeSocket, buf, bufSize, 0); - } - else - { - const void *dst = [theCurrentSend->address bytes]; - unsigned dstSize = [theCurrentSend->address length]; - - result = sendto(theNativeSocket, buf, bufSize, 0, dst, dstSize); - } - - if(theSocket == theSocket4) - theFlags &= ~kSock4CanAcceptBytes; - else - theFlags &= ~kSock6CanAcceptBytes; - - if(result < 0) - { - [self failCurrentSend:[self getErrnoError]]; - } - else - { - // If it wasn't bound before, it's bound now - theFlags |= kDidBind; - - [self completeCurrentSend]; - } - - [self scheduleDequeueSend]; - } - else - { - // Request notification when the socket is ready to send more data - CFSocketEnableCallBacks(theSocket, kCFSocketReadCallBack | kCFSocketWriteCallBack); - } - } -} - -- (void)completeCurrentSend -{ - NSAssert (theCurrentSend, @"Trying to complete current send when there is no current send."); - - if ([theDelegate respondsToSelector:@selector(onUdpSocket:didSendDataWithTag:)]) - { - [theDelegate onUdpSocket:self didSendDataWithTag:theCurrentSend->tag]; - } - - if (theCurrentSend != nil) [self endCurrentSend]; // Caller may have disconnected. -} - -- (void)failCurrentSend:(NSError *)error -{ - NSAssert (theCurrentSend, @"Trying to fail current send when there is no current send."); - - if ([theDelegate respondsToSelector:@selector(onUdpSocket:didNotSendDataWithTag:dueToError:)]) - { - [theDelegate onUdpSocket:self didNotSendDataWithTag:theCurrentSend->tag dueToError:error]; - } - - if (theCurrentSend != nil) [self endCurrentSend]; // Caller may have disconnected. -} - -/** - * Ends the current send, and all associated variables such as the send timer. -**/ -- (void)endCurrentSend -{ - NSAssert (theCurrentSend, @"Trying to end current send when there is no current send."); - - [theSendTimer invalidate]; - theSendTimer = nil; - - [theCurrentSend release]; - theCurrentSend = nil; -} - -- (void)doSendTimeout:(NSTimer *)timer -{ - if (timer != theSendTimer) return; // Old timer. Ignore it. - if (theCurrentSend != nil) - { - [self failCurrentSend:[self getSendTimeoutError]]; - [self scheduleDequeueSend]; - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Receiving -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (void)receiveWithTimeout:(NSTimeInterval)timeout tag:(long)tag -{ - if(theFlags & kForbidSendReceive) return; - if(theFlags & kDidClose) return; - - AsyncReceivePacket *packet = [[AsyncReceivePacket alloc] initWithTimeout:timeout tag:tag]; - - [theReceiveQueue addObject:packet]; - [self scheduleDequeueReceive]; - - [packet release]; -} - -- (BOOL)hasBytesAvailable:(CFSocketRef)sockRef -{ - if(sockRef == theSocket4) - { - if(theFlags & kSock4HasBytesAvailable) return YES; - } - else - { - if(theFlags & kSock6HasBytesAvailable) return YES; - } - - CFSocketNativeHandle theNativeSocket = CFSocketGetNative(sockRef); - - if(theNativeSocket == 0) - { - NSLog(@"Error - Could not get CFSocketNativeHandle from CFSocketRef"); - return NO; - } - - fd_set fds; - FD_ZERO(&fds); - FD_SET(theNativeSocket, &fds); - - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 0; - - return select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0; -} - -/** - * Puts a maybeDequeueReceive on the run loop. -**/ -- (void)scheduleDequeueReceive -{ - if((theFlags & kDequeueReceiveScheduled) == 0) - { - theFlags |= kDequeueReceiveScheduled; - [self performSelector:@selector(maybeDequeueReceive) withObject:nil afterDelay:0 inModes:theRunLoopModes]; - } -} - -/** - * Starts a new receive operation if needed -**/ -- (void)maybeDequeueReceive -{ - // Unset the flag indicating a call to this method is scheduled - theFlags &= ~kDequeueReceiveScheduled; - - if (theCurrentReceive == nil) - { - if([theReceiveQueue count] > 0) - { - // Dequeue next receive packet - theCurrentReceive = [[theReceiveQueue objectAtIndex:0] retain]; - [theReceiveQueue removeObjectAtIndex:0]; - - // Start time-out timer. - if (theCurrentReceive->timeout >= 0.0) - { - theReceiveTimer = [NSTimer timerWithTimeInterval:theCurrentReceive->timeout - target:self - selector:@selector(doReceiveTimeout:) - userInfo:nil - repeats:NO]; - - [self runLoopAddTimer:theReceiveTimer]; - } - - // Immediately receive, if possible - // We always check both sockets so we don't ever starve one of them. - // We also check them in alternating orders to prevent starvation if both of them - // have a continuous flow of incoming data. - if(theFlags & kFlipFlop) - { - [self doReceive4]; - [self doReceive6]; - } - else - { - [self doReceive6]; - [self doReceive4]; - } - - theFlags ^= kFlipFlop; - } - else if(theFlags & kCloseAfterReceives) - { - if(theFlags & kCloseAfterSends) - { - if(([theSendQueue count] == 0) && (theCurrentSend == nil)) - { - [self close]; - } - } - else - { - [self close]; - } - } - } -} - -- (void)doReceive4 -{ - if(theSocket4) [self doReceive:theSocket4]; -} - -- (void)doReceive6 -{ - if(theSocket6) [self doReceive:theSocket6]; -} - -- (void)doReceive:(CFSocketRef)theSocket -{ - if (theCurrentReceive != nil) - { - BOOL appIgnoredReceivedData; - BOOL userIgnoredReceivedData; - - do - { - // Set or reset ignored variables. - // If the app or user ignores the received data, we'll continue this do-while loop. - appIgnoredReceivedData = NO; - userIgnoredReceivedData = NO; - - if([self hasBytesAvailable:theSocket]) - { - int result; - CFSocketNativeHandle theNativeSocket = CFSocketGetNative(theSocket); - - // Allocate buffer for recvfrom operation. - // If the operation is successful, we'll realloc the buffer to the appropriate size, - // and create an NSData wrapper around it without needing to copy any bytes around. - void *buf = malloc(maxReceiveBufferSize); - size_t bufSize = maxReceiveBufferSize; - - if(theSocket == theSocket4) - { - struct sockaddr_in sockaddr4; - socklen_t sockaddr4len = sizeof(sockaddr4); - - result = recvfrom(theNativeSocket, buf, bufSize, 0, (struct sockaddr *)&sockaddr4, &sockaddr4len); - - if(result >= 0) - { - NSString *host = [self addressHost4:&sockaddr4]; - UInt16 port = ntohs(sockaddr4.sin_port); - - if([self isConnected] && ![self isConnectedToHost:host port:port]) - { - // The user connected to an address, and the received data doesn't match the address. - // This may happen if the data is received by the kernel prior to the connect call. - appIgnoredReceivedData = YES; - } - else - { - if(result != bufSize) - { - buf = realloc(buf, result); - } - theCurrentReceive->buffer = [[NSData alloc] initWithBytesNoCopy:buf - length:result - freeWhenDone:YES]; - theCurrentReceive->host = [host retain]; - theCurrentReceive->port = port; - } - } - - theFlags &= ~kSock4HasBytesAvailable; - } - else - { - struct sockaddr_in6 sockaddr6; - socklen_t sockaddr6len = sizeof(sockaddr6); - - result = recvfrom(theNativeSocket, buf, bufSize, 0, (struct sockaddr *)&sockaddr6, &sockaddr6len); - - if(result >= 0) - { - NSString *host = [self addressHost6:&sockaddr6]; - UInt16 port = ntohs(sockaddr6.sin6_port); - - if([self isConnected] && ![self isConnectedToHost:host port:port]) - { - // The user connected to an address, and the received data doesn't match the address. - // This may happen if the data is received by the kernel prior to the connect call. - appIgnoredReceivedData = YES; - } - else - { - if(result != bufSize) - { - buf = realloc(buf, result); - } - theCurrentReceive->buffer = [[NSData alloc] initWithBytesNoCopy:buf - length:result - freeWhenDone:YES]; - theCurrentReceive->host = [host retain]; - theCurrentReceive->port = port; - } - } - - theFlags &= ~kSock6HasBytesAvailable; - } - - // Check to see if we need to free our alloc'd buffer - // If the buffer is non-nil, this means it has taken ownership of the buffer - if(theCurrentReceive->buffer == nil) - { - free(buf); - } - - if(result < 0) - { - [self failCurrentReceive:[self getErrnoError]]; - [self scheduleDequeueReceive]; - } - else if(!appIgnoredReceivedData) - { - BOOL finished = [self maybeCompleteCurrentReceive]; - - if(finished) - { - [self scheduleDequeueReceive]; - } - else - { - [theCurrentReceive->buffer release]; - [theCurrentReceive->host release]; - - theCurrentReceive->buffer = nil; - theCurrentReceive->host = nil; - - userIgnoredReceivedData = YES; - } - } - } - else - { - // Request notification when the socket is ready to receive more data - CFSocketEnableCallBacks(theSocket, kCFSocketReadCallBack | kCFSocketWriteCallBack); - } - - } while(appIgnoredReceivedData || userIgnoredReceivedData); - } -} - -- (BOOL)maybeCompleteCurrentReceive -{ - NSAssert (theCurrentReceive, @"Trying to complete current receive when there is no current receive."); - - BOOL finished = YES; - - if ([theDelegate respondsToSelector:@selector(onUdpSocket:didReceiveData:withTag:fromHost:port:)]) - { - finished = [theDelegate onUdpSocket:self - didReceiveData:theCurrentReceive->buffer - withTag:theCurrentReceive->tag - fromHost:theCurrentReceive->host - port:theCurrentReceive->port]; - } - - if (finished) - { - if (theCurrentReceive != nil) [self endCurrentReceive]; // Caller may have disconnected. - } - return finished; -} - -- (void)failCurrentReceive:(NSError *)error -{ - NSAssert (theCurrentReceive, @"Trying to fail current receive when there is no current receive."); - - if ([theDelegate respondsToSelector:@selector(onUdpSocket:didNotReceiveDataWithTag:dueToError:)]) - { - [theDelegate onUdpSocket:self didNotReceiveDataWithTag:theCurrentReceive->tag dueToError:error]; - } - - if (theCurrentReceive != nil) [self endCurrentReceive]; // Caller may have disconnected. -} - -- (void)endCurrentReceive -{ - NSAssert (theCurrentReceive, @"Trying to end current receive when there is no current receive."); - - [theReceiveTimer invalidate]; - theReceiveTimer = nil; - - [theCurrentReceive release]; - theCurrentReceive = nil; -} - -- (void)doReceiveTimeout:(NSTimer *)timer -{ - if (timer != theReceiveTimer) return; // Old timer. Ignore it. - if (theCurrentReceive != nil) - { - [self failCurrentReceive:[self getReceiveTimeoutError]]; - [self scheduleDequeueReceive]; - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark CF Callbacks -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (void)doCFSocketCallback:(CFSocketCallBackType)type - forSocket:(CFSocketRef)sock - withAddress:(NSData *)address - withData:(const void *)pData -{ - NSParameterAssert((sock == theSocket4) || (sock == theSocket6)); - - switch (type) - { - case kCFSocketReadCallBack: - if(sock == theSocket4) - theFlags |= kSock4HasBytesAvailable; - else - theFlags |= kSock6HasBytesAvailable; - [self doReceive:sock]; - break; - case kCFSocketWriteCallBack: - if(sock == theSocket4) - theFlags |= kSock4CanAcceptBytes; - else - theFlags |= kSock6CanAcceptBytes; - [self doSend:sock]; - break; - default: - NSLog (@"AsyncUdpSocket %p received unexpected CFSocketCallBackType %d.", self, type); - break; - } -} - -/** - * This is the callback we setup for CFSocket. - * This method does nothing but forward the call to it's Objective-C counterpart -**/ -static void MyCFSocketCallback(CFSocketRef sref, CFSocketCallBackType type, CFDataRef address, const void *pData, void *pInfo) -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - AsyncUdpSocket *theSocket = [[(AsyncUdpSocket *)pInfo retain] autorelease]; - [theSocket doCFSocketCallback:type forSocket:sref withAddress:(NSData *)address withData:pData]; - - [pool release]; -} - -@end diff --git a/AsyncSocket/changes.txt b/AsyncSocket/changes.txt deleted file mode 100755 index 3db5157..0000000 --- a/AsyncSocket/changes.txt +++ /dev/null @@ -1,98 +0,0 @@ -The last version of AsyncSocket from Dustin Voss was version 4.3 released on 2005-11-23. This document lists the bug fixes provided by Deusty Designs, prior to the creation of the Google Code project. - -CHANGES IN VERSION 4.3.1: - -Bugfix: - If user called acceptOnPort:0, then the OS would automatically pick a port for you. - This is what is supposed to happen, except that it would pick a different port for IPv4 and IPv6 - Added code to make sure both protocols are listening on the same port - -Added comments in many places - -Altered bits of code to match Apple's coding style guidelines - -Renamed method "attachAcceptSockets" to "attachSocketsToRunLoop:error:" - - - - -CHANGES IN VERSION 4.3.2 - -Removed polling - it's not needed - -Added another delegate method: onSocket:didReadPartialDataOfLength:tag: -Often, when using the AsyncSocket class, it was important to display a progress bar to the user. -This was possible using Timers, and calling progressOfRead, but it wasn't the easiest solution. -This delegate method will allow for automatic notification when using readToLength: or readToData: - - - - -CHANGES IN VERSION 4.3.3 - -Bugfix: - The methods connectedHost, connectedPort, localHost, and localPort all assumed IPv4 connection. - In other words they all assumed theSocket was valid, causing a crash when the OS connected via IPv6. - Updated all methods to properly check either theSocket or theSocket6. - -Bugfix: - In the doStreamOpen method, there was an assumption that IPv4 was used and thus a valid theSocket variable. - This was not always the case, causing this to fail: - CFSocketCopyPeerAddress(theSocket) - Fixed the problem by simply using the connectedHost and connectedPort methods. - -Tweak: - Added safety check in addressPort: method: - if (cfaddr == NULL) return 0; - -Tweak: - The kCFStreamPropertyShouldCloseNativeSocket was previously getting set in the configureStreamsAndReturnError: method. - This may have been OK, but it would have caused a problem if the following sequence occurred: - A socket was accepted and doAcceptWithSocket: method was called, - This method then encoutered an error while attaching the streams to the run loop. - If this sequence happened, the BSD socket would not have been closed. - So I moved this into the createStreams... methods. - - - - -CHANGES IN VERSION 4.3.4 - -Bugfix: - In doReadTimeout: the code properly calls endCurrentWrite and then closes with an error. - In doWriteTimeout: the code was calling completeCurrentWrite and then closes with an error. - This resulted in the delegate being told that his write completed (when it didn't) - immediately prior to disconnecting with an error. - - - - -CHANGES IN VERSION 4.3.5 - -Added support for accepting on IPv6 address. - -Bugfix: - In acceptOnAddress, changed dataWithBytesNoCopy to dataWithBytes. - This was needed because the bytes were about to go out of scope. - -Bugfix: - Added return statement to doStreamOpen after closewithError call. - This was needed or else the didConnect delegate could potentially get called - immediately after willCloseWithError and didClose. - -Bugfix: - We were receiving several reports of crashes in AsyncSocket. - The problem seemed to be that, in specific circumstances, - the readStream callback and/or writeStream callback would be invoked AFTER - the readStream and writeStream were closed. - This isn't supposed to happen, however we did find evidence that it was an issue several years ago. - It was assumed that the problem has since been fixed. - Perhaps the problem still exists, but only in very rare cases which we just happened to be encountering. - In any case, we used the same precautions that were used previously. - In the close methods, we specifically unregister for callbacks now. - - - - -There have been MANY more bug fixes and changes. Please consult the google code changes list: -http://code.google.com/p/cocoaasyncsocket/source/list \ No newline at end of file diff --git a/JSON/NSObject+SBJson.h b/JSON/NSObject+SBJson.h deleted file mode 100644 index 4c92454..0000000 --- a/JSON/NSObject+SBJson.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - Copyright (C) 2009 Stig Brautaset. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name of the author nor the names of its contributors may be used - to endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import - -#pragma mark JSON Writing - -/// Adds JSON generation to NSObject -@interface NSObject (NSObject_SBJsonWriting) - -/** - @brief Encodes the receiver into a JSON string - - Although defined as a category on NSObject it is only defined for NSArray and NSDictionary. - - @return the receiver encoded in JSON, or nil on error. - - @see @ref objc2json - */ -- (NSString *)JSONRepresentation; - -@end - - -#pragma mark JSON Parsing - -/// Adds JSON parsing methods to NSString -@interface NSString (NSString_SBJsonParsing) - -/** - @brief Decodes the receiver's JSON text - - @return the NSDictionary or NSArray represented by the receiver, or nil on error. - - @see @ref json2objc - */ -- (id)JSONValue; - -@end - - diff --git a/JSON/SBJson.h b/JSON/SBJson.h deleted file mode 100644 index b25da4a..0000000 --- a/JSON/SBJson.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - Copyright (C) 2009-2011 Stig Brautaset. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name of the author nor the names of its contributors may be used - to endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - @page json2objc JSON to Objective-C - - JSON is mapped to Objective-C types in the following way: - - @li null -> NSNull - @li string -> NSString - @li array -> NSMutableArray - @li object -> NSMutableDictionary - @li true -> NSNumber's -numberWithBool:YES - @li false -> NSNumber's -numberWithBool:NO - @li integer up to 19 digits -> NSNumber's -numberWithLongLong: - @li all other numbers -> NSDecimalNumber - - Since Objective-C doesn't have a dedicated class for boolean values, - these turns into NSNumber instances. However, since these are - initialised with the -initWithBool: method they round-trip back to JSON - properly. In other words, they won't silently suddenly become 0 or 1; - they'll be represented as 'true' and 'false' again. - - As an optimisation integers up to 19 digits in length (the max length - for signed long long integers) turn into NSNumber instances, while - complex ones turn into NSDecimalNumber instances. We can thus avoid any - loss of precision as JSON allows ridiculously large numbers. - - @page objc2json Objective-C to JSON - - Objective-C types are mapped to JSON types in the following way: - - @li NSNull -> null - @li NSString -> string - @li NSArray -> array - @li NSDictionary -> object - @li NSNumber's -initWithBool:YES -> true - @li NSNumber's -initWithBool:NO -> false - @li NSNumber -> number - - @note In JSON the keys of an object must be strings. NSDictionary - keys need not be, but attempting to convert an NSDictionary with - non-string keys into JSON will throw an exception. - - NSNumber instances created with the -numberWithBool: method are - converted into the JSON boolean "true" and "false" values, and vice - versa. Any other NSNumber instances are converted to a JSON number the - way you would expect. - - */ - -#import "SBJsonParser.h" -#import "SBJsonWriter.h" -#import "SBJsonStreamParser.h" -#import "SBJsonStreamParserAdapter.h" -#import "SBJsonStreamWriter.h" -#import "NSObject+SBJson.h" - diff --git a/JSON/SBJsonTokeniser.h b/JSON/SBJsonTokeniser.h deleted file mode 100644 index 7e30bfd..0000000 --- a/JSON/SBJsonTokeniser.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - Copyright (c) 2010, Stig Brautaset. - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of the the author nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import - -typedef enum { - sbjson_token_error = -1, - sbjson_token_eof, - - sbjson_token_array_start, - sbjson_token_array_end, - - sbjson_token_object_start, - sbjson_token_object_end, - - sbjson_token_separator, - sbjson_token_keyval_separator, - - sbjson_token_number, - sbjson_token_string, - sbjson_token_true, - sbjson_token_false, - sbjson_token_null, - -} sbjson_token_t; - -@class SBJsonUTF8Stream; - -@interface SBJsonTokeniser : NSObject - -@property (retain) SBJsonUTF8Stream *stream; -@property (copy) NSString *error; - -- (void)appendData:(NSData*)data_; - -- (sbjson_token_t)getToken:(NSObject**)token; - -@end diff --git a/JSON/SBJsonTokeniser.m b/JSON/SBJsonTokeniser.m deleted file mode 100644 index 29a9d98..0000000 --- a/JSON/SBJsonTokeniser.m +++ /dev/null @@ -1,463 +0,0 @@ -/* - Copyright (c) 2010-2011, Stig Brautaset. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of the the author nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import "SBJsonTokeniser.h" -#import "SBJsonUTF8Stream.h" - -#define SBStringIsIllegalSurrogateHighCharacter(character) (((character) >= 0xD800UL) && ((character) <= 0xDFFFUL)) -#define SBStringIsSurrogateLowCharacter(character) ((character >= 0xDC00UL) && (character <= 0xDFFFUL)) -#define SBStringIsSurrogateHighCharacter(character) ((character >= 0xD800UL) && (character <= 0xDBFFUL)) - -@implementation SBJsonTokeniser - -@synthesize error = _error; -@synthesize stream = _stream; - -- (id)init { - self = [super init]; - if (self) { - _stream = [[SBJsonUTF8Stream alloc] init]; - - } - - return self; -} - -- (void)dealloc { - [_error release]; - [_stream release]; - [super dealloc]; -} - -- (void)appendData:(NSData *)data_ { - [_stream appendData:data_]; -} - - -- (sbjson_token_t)match:(const char *)pattern length:(NSUInteger)len retval:(sbjson_token_t)token { - if (![_stream haveRemainingCharacters:len]) - return sbjson_token_eof; - - if ([_stream skipCharacters:pattern length:len]) - return token; - - self.error = [NSString stringWithFormat:@"Expected '%s' after initial '%.1s'", pattern, pattern]; - return sbjson_token_error; -} - -- (BOOL)decodeEscape:(unichar)ch into:(unichar*)decoded { - switch (ch) { - case '\\': - case '/': - case '"': - *decoded = ch; - break; - - case 'b': - *decoded = '\b'; - break; - - case 'n': - *decoded = '\n'; - break; - - case 'r': - *decoded = '\r'; - break; - - case 't': - *decoded = '\t'; - break; - - case 'f': - *decoded = '\f'; - break; - - default: - self.error = @"Illegal escape character"; - return NO; - break; - } - return YES; -} - -- (BOOL)decodeHexQuad:(unichar*)quad { - unichar c, tmp = 0; - - for (int i = 0; i < 4; i++) { - (void)[_stream getNextUnichar:&c]; - tmp *= 16; - switch (c) { - case '0' ... '9': - tmp += c - '0'; - break; - - case 'a' ... 'f': - tmp += 10 + c - 'a'; - break; - - case 'A' ... 'F': - tmp += 10 + c - 'A'; - break; - - default: - return NO; - } - } - *quad = tmp; - return YES; -} - -- (sbjson_token_t)getStringToken:(NSObject**)token { - NSMutableString *acc = nil; - - for (;;) { - [_stream skip]; - - unichar ch; - { - NSMutableString *string = nil; - @try { - if (![_stream getRetainedStringFragment:&string]) - return sbjson_token_eof; - - if (!string) { - self.error = @"Broken Unicode encoding"; - return sbjson_token_error; - } - - if (![_stream getUnichar:&ch]) { - return sbjson_token_eof; - } - - if (acc) { - [acc appendString:string]; - - } else if (ch == '"') { - *token = [[string copy] autorelease]; - [_stream skip]; - return sbjson_token_string; - - } else { - acc = [[string mutableCopy] autorelease]; - } - } - @finally { - [string release]; - } - } - - - switch (ch) { - case 0 ... 0x1F: - self.error = [NSString stringWithFormat:@"Unescaped control character [0x%0.2X]", (int)ch]; - return sbjson_token_error; - break; - - case '"': - *token = acc; - [_stream skip]; - return sbjson_token_string; - break; - - case '\\': - if (![_stream getNextUnichar:&ch]) - return sbjson_token_eof; - - if (ch == 'u') { - if (![_stream haveRemainingCharacters:5]) - return sbjson_token_eof; - - unichar hi; - if (![self decodeHexQuad:&hi]) { - self.error = @"Invalid hex quad"; - return sbjson_token_error; - } - - if (SBStringIsSurrogateHighCharacter(hi)) { - unichar lo; - - if (![_stream haveRemainingCharacters:6]) - return sbjson_token_eof; - - (void)[_stream getNextUnichar:&ch]; - (void)[_stream getNextUnichar:&lo]; - if (ch != '\\' || lo != 'u' || ![self decodeHexQuad:&lo]) { - self.error = @"Missing low character in surrogate pair"; - return sbjson_token_error; - } - - if (!SBStringIsSurrogateLowCharacter(lo)) { - self.error = @"Invalid low character in surrogate pair"; - return sbjson_token_error; - } - - [acc appendFormat:@"%C%C", hi, lo]; - } else if (SBStringIsIllegalSurrogateHighCharacter(hi)) { - self.error = @"Invalid high character in surrogate pair"; - return sbjson_token_error; - } else { - [acc appendFormat:@"%C", hi]; - } - - - } else { - unichar decoded; - if (![self decodeEscape:ch into:&decoded]) - return sbjson_token_error; - [acc appendFormat:@"%C", decoded]; - } - - break; - - default: { - self.error = [NSString stringWithFormat:@"Invalid UTF-8: '%x'", (int)ch]; - return sbjson_token_error; - break; - } - } - } - return sbjson_token_eof; -} - -- (sbjson_token_t)getNumberToken:(NSObject**)token { - - NSUInteger numberStart = _stream.index; - NSCharacterSet *digits = [NSCharacterSet decimalDigitCharacterSet]; - - unichar ch; - if (![_stream getUnichar:&ch]) - return sbjson_token_eof; - - BOOL isNegative = NO; - if (ch == '-') { - isNegative = YES; - if (![_stream getNextUnichar:&ch]) - return sbjson_token_eof; - } - - unsigned long long mantissa = 0; - int mantissa_length = 0; - - if (ch == '0') { - mantissa_length++; - if (![_stream getNextUnichar:&ch]) - return sbjson_token_eof; - - if ([digits characterIsMember:ch]) { - self.error = @"Leading zero is illegal in number"; - return sbjson_token_error; - } - } - - while ([digits characterIsMember:ch]) { - mantissa *= 10; - mantissa += (ch - '0'); - mantissa_length++; - - if (![_stream getNextUnichar:&ch]) - return sbjson_token_eof; - } - - short exponent = 0; - BOOL isFloat = NO; - - if (ch == '.') { - isFloat = YES; - if (![_stream getNextUnichar:&ch]) - return sbjson_token_eof; - - while ([digits characterIsMember:ch]) { - mantissa *= 10; - mantissa += (ch - '0'); - mantissa_length++; - exponent--; - - if (![_stream getNextUnichar:&ch]) - return sbjson_token_eof; - } - - if (!exponent) { - self.error = @"No digits after decimal point"; - return sbjson_token_error; - } - } - - BOOL hasExponent = NO; - if (ch == 'e' || ch == 'E') { - hasExponent = YES; - - if (![_stream getNextUnichar:&ch]) - return sbjson_token_eof; - - BOOL expIsNegative = NO; - if (ch == '-') { - expIsNegative = YES; - if (![_stream getNextUnichar:&ch]) - return sbjson_token_eof; - - } else if (ch == '+') { - if (![_stream getNextUnichar:&ch]) - return sbjson_token_eof; - } - - short explicit_exponent = 0; - short explicit_exponent_length = 0; - while ([digits characterIsMember:ch]) { - explicit_exponent *= 10; - explicit_exponent += (ch - '0'); - explicit_exponent_length++; - - if (![_stream getNextUnichar:&ch]) - return sbjson_token_eof; - } - - if (explicit_exponent_length == 0) { - self.error = @"No digits in exponent"; - return sbjson_token_error; - } - - if (expIsNegative) - exponent -= explicit_exponent; - else - exponent += explicit_exponent; - } - - if (!mantissa_length && isNegative) { - self.error = @"No digits after initial minus"; - return sbjson_token_error; - - } else if (mantissa_length >= 19) { - - NSString *number = [_stream stringWithRange:NSMakeRange(numberStart, _stream.index - numberStart)]; - *token = [NSDecimalNumber decimalNumberWithString:number]; - - } else if (!isFloat && !hasExponent) { - if (!isNegative) - *token = [NSNumber numberWithUnsignedLongLong:mantissa]; - else - *token = [NSNumber numberWithLongLong:-mantissa]; - } else { - *token = [NSDecimalNumber decimalNumberWithMantissa:mantissa - exponent:exponent - isNegative:isNegative]; - } - - return sbjson_token_number; -} - -- (sbjson_token_t)getToken:(NSObject **)token { - - [_stream skipWhitespace]; - - unichar ch; - if (![_stream getUnichar:&ch]) - return sbjson_token_eof; - - NSUInteger oldIndexLocation = _stream.index; - sbjson_token_t tok; - - switch (ch) { - case '[': - tok = sbjson_token_array_start; - [_stream skip]; - break; - - case ']': - tok = sbjson_token_array_end; - [_stream skip]; - break; - - case '{': - tok = sbjson_token_object_start; - [_stream skip]; - break; - - case ':': - tok = sbjson_token_keyval_separator; - [_stream skip]; - break; - - case '}': - tok = sbjson_token_object_end; - [_stream skip]; - break; - - case ',': - tok = sbjson_token_separator; - [_stream skip]; - break; - - case 'n': - tok = [self match:"null" length:4 retval:sbjson_token_null]; - break; - - case 't': - tok = [self match:"true" length:4 retval:sbjson_token_true]; - break; - - case 'f': - tok = [self match:"false" length:5 retval:sbjson_token_false]; - break; - - case '"': - tok = [self getStringToken:token]; - break; - - case '0' ... '9': - case '-': - tok = [self getNumberToken:token]; - break; - - case '+': - self.error = @"Leading + is illegal in number"; - tok = sbjson_token_error; - break; - - default: - self.error = [NSString stringWithFormat:@"Illegal start of token [%c]", ch]; - tok = sbjson_token_error; - break; - } - - if (tok == sbjson_token_eof) { - // We ran out of bytes in the middle of a token. - // We don't know how to restart in mid-flight, so - // rewind to the start of the token for next attempt. - // Hopefully we'll have more data then. - _stream.index = oldIndexLocation; - } - - return tok; -} - - -@end diff --git a/JSON/SBJsonUTF8Stream.h b/JSON/SBJsonUTF8Stream.h deleted file mode 100644 index 63687be..0000000 --- a/JSON/SBJsonUTF8Stream.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - Copyright (c) 2011, Stig Brautaset. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of the the author nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import - - -@interface SBJsonUTF8Stream : NSObject { -@private - const char *_bytes; - NSMutableData *_data; - NSUInteger _length; -} - -@property (assign) NSUInteger index; - -- (void)appendData:(NSData*)data_; - -- (BOOL)haveRemainingCharacters:(NSUInteger)chars; - -- (void)skip; -- (void)skipWhitespace; -- (BOOL)skipCharacters:(const char *)chars length:(NSUInteger)len; - -- (BOOL)getUnichar:(unichar*)ch; -- (BOOL)getNextUnichar:(unichar*)ch; -- (BOOL)getRetainedStringFragment:(NSString**)string; - -- (NSString*)stringWithRange:(NSRange)range; - -@end diff --git a/JSON/SBJsonUTF8Stream.m b/JSON/SBJsonUTF8Stream.m deleted file mode 100644 index 8240fdd..0000000 --- a/JSON/SBJsonUTF8Stream.m +++ /dev/null @@ -1,143 +0,0 @@ -/* - Copyright (c) 2011, Stig Brautaset. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of the the author nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import "SBJsonUTF8Stream.h" - - -@implementation SBJsonUTF8Stream - -@synthesize index = _index; - -- (id)init { - self = [super init]; - if (self) { - _data = [[NSMutableData alloc] initWithCapacity:4096u]; - } - return self; -} - -- (void)dealloc { - [_data release]; - [super dealloc]; -} - -- (void)appendData:(NSData *)data_ { - - if (_index) { - // Discard data we've already parsed - [_data replaceBytesInRange:NSMakeRange(0, _index) withBytes:"" length:0]; - - // Reset index to point to current position - _index = 0; - } - - [_data appendData:data_]; - - // This is an optimisation. - _bytes = (const char*)[_data bytes]; - _length = [_data length]; -} - - -- (BOOL)getUnichar:(unichar*)ch { - if (_index < _length) { - *ch = (unichar)_bytes[_index]; - return YES; - } - return NO; -} - -- (BOOL)getNextUnichar:(unichar*)ch { - if (++_index < _length) { - *ch = (unichar)_bytes[_index]; - return YES; - } - return NO; -} - -- (BOOL)getRetainedStringFragment:(NSString **)string { - NSUInteger start = _index; - while (_index < _length) { - switch (_bytes[_index]) { - case '"': - case '\\': - case 0 ... 0x1f: - *string = [[NSString alloc] initWithBytes:(_bytes + start) length:(_index - start) encoding:NSUTF8StringEncoding]; - return YES; - break; - default: - _index++; - break; - } - } - return NO; -} - -- (void)skip { - _index++; -} - -- (void)skipWhitespace { - while (_index < _length) { - switch (_bytes[_index]) { - case ' ': - case '\t': - case '\r': - case '\n': - _index++; - break; - default: - return; - break; - } - } -} - -- (BOOL)haveRemainingCharacters:(NSUInteger)chars { - return [_data length] - _index >= chars; -} - -- (BOOL)skipCharacters:(const char *)chars length:(NSUInteger)len { - const void *bytes = ((const char*)[_data bytes]) + _index; - if (!memcmp(bytes, chars, len)) { - _index += len; - return YES; - } - return NO; -} - -- (NSString*)stringWithRange:(NSRange)range { - return [[[NSString alloc] initWithBytes:_bytes + range.location length:range.length encoding:NSUTF8StringEncoding] autorelease]; - -} - - -@end diff --git a/README.md b/README.md index 148ca67..4250f57 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,26 @@ -#For Client, +Socket.io with objective-c example. - $ npm install socket.io - $ npm install -d - $ cd node_modules/socket.io/examples/chat +- Objective-C code from [https://github.com/pkyeck/socket.IO-objc](https://github.com/pkyeck/socket.IO-objc) +- Socket.io server code from [https://github.com/saturngod/geek_chat](https://github.com/saturngod/geek_chat) -#Video demo, +##For running the app -[http://en.saturngod.net/socketio-with-ios](http://en.saturngod.net/socketio-with-ios) \ No newline at end of file + +Change IP address at sockio-server/views/index.html + +``` +var socket = io.connect("192.168.1.10:3000"); +``` + + +Run socket.io chat like + + node ./sockio-server/app.js + + +Change IP address at Objective-C code + +``` +[_socketIO connectToHost:@"localhost" onPort:3000]; +``` + diff --git a/Reachability.h b/Reachability.h deleted file mode 100644 index af52444..0000000 --- a/Reachability.h +++ /dev/null @@ -1,194 +0,0 @@ -/* - - File: Reachability.h - Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs. - - Version: 2.0.4ddg - */ - -/* - Significant additions made by Andrew W. Donoho, August 11, 2009. - This is a derived work of Apple's Reachability v2.0 class. - - The below license is the new BSD license with the OSI recommended personalizations. - - - Extensions Copyright (C) 2009 Donoho Design Group, LLC. All Rights Reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of Andrew W. Donoho nor Donoho Design Group, L.L.C. - may be used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY DONOHO DESIGN GROUP, L.L.C. "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - */ - - -/* - - Apple's Original License on Reachability v2.0 - - Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. - ("Apple") in consideration of your agreement to the following terms, and your - use, installation, modification or redistribution of this Apple software - constitutes acceptance of these terms. If you do not agree with these terms, - please do not use, install, modify or redistribute this Apple software. - - In consideration of your agreement to abide by the following terms, and subject - to these terms, Apple grants you a personal, non-exclusive license, under - Apple's copyrights in this original Apple software (the "Apple Software"), to - use, reproduce, modify and redistribute the Apple Software, with or without - modifications, in source and/or binary forms; provided that if you redistribute - the Apple Software in its entirety and without modifications, you must retain - this notice and the following text and disclaimers in all such redistributions - of the Apple Software. - - Neither the name, trademarks, service marks or logos of Apple Inc. may be used - to endorse or promote products derived from the Apple Software without specific - prior written permission from Apple. Except as expressly stated in this notice, - no other rights or licenses, express or implied, are granted by Apple herein, - including but not limited to any patent rights that may be infringed by your - derivative works or by other works in which the Apple Software may be - incorporated. - - The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO - WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED - WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN - COMBINATION WITH YOUR PRODUCTS. - - IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR - DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF - CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF - APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - Copyright (C) 2009 Apple Inc. All Rights Reserved. - - */ - - -/* - DDG extensions include: - Each reachability object now has a copy of the key used to store it in a - dictionary. This allows each observer to quickly determine if the event is - important to them. - - -currentReachabilityStatus also has a significantly different decision criteria than - Apple's code. - - A multiple convenience test methods have been added. - */ - -#import -#import -#import - -#define USE_DDG_EXTENSIONS 1 // Use DDG's Extensions to test network criteria. -// Since NSAssert and NSCAssert are used in this code, -// I recommend you set NS_BLOCK_ASSERTIONS=1 in the release versions of your projects. - -enum { - - // DDG NetworkStatus Constant Names. - kNotReachable = 0, // Apple's code depends upon 'NotReachable' being the same value as 'NO'. - kReachableViaWWAN, // Switched order from Apple's enum. WWAN is active before WiFi. - kReachableViaWiFi - -}; -typedef uint32_t NetworkStatus; - -enum { - - // Apple NetworkStatus Constant Names. - NotReachable = kNotReachable, - ReachableViaWiFi = kReachableViaWiFi, - ReachableViaWWAN = kReachableViaWWAN - -}; - - -extern NSString *const kInternetConnection; -extern NSString *const kLocalWiFiConnection; -extern NSString *const kReachabilityChangedNotification; - -@interface Reachability: NSObject { - -@private - NSString *key_; - SCNetworkReachabilityRef reachabilityRef; - -} - -@property (copy) NSString *key; // Atomic because network operations are asynchronous. - -// Designated Initializer. -- (Reachability *) initWithReachabilityRef: (SCNetworkReachabilityRef) ref; - -// Use to check the reachability of a particular host name. -+ (Reachability *) reachabilityWithHostName: (NSString*) hostName; - -// Use to check the reachability of a particular IP address. -+ (Reachability *) reachabilityWithAddress: (const struct sockaddr_in*) hostAddress; - -// Use to check whether the default route is available. -// Should be used to, at minimum, establish network connectivity. -+ (Reachability *) reachabilityForInternetConnection; - -// Use to check whether a local wifi connection is available. -+ (Reachability *) reachabilityForLocalWiFi; - -//Start listening for reachability notifications on the current run loop. -- (BOOL) startNotifier; -- (void) stopNotifier; - -// Comparison routines to enable choosing actions in a notification. -- (BOOL) isEqual: (Reachability *) r; - -// These are the status tests. -- (NetworkStatus) currentReachabilityStatus; - -// The main direct test of reachability. -- (BOOL) isReachable; - -// WWAN may be available, but not active until a connection has been established. -// WiFi may require a connection for VPN on Demand. -- (BOOL) isConnectionRequired; // Identical DDG variant. -- (BOOL) connectionRequired; // Apple's routine. - -// Dynamic, on demand connection? -- (BOOL) isConnectionOnDemand; - -// Is user intervention required? -- (BOOL) isInterventionRequired; - -// Routines for specific connection testing by your app. -- (BOOL) isReachableViaWWAN; -- (BOOL) isReachableViaWiFi; - -- (SCNetworkReachabilityFlags) reachabilityFlags; - -@end diff --git a/Reachability.m b/Reachability.m deleted file mode 100644 index efe99b3..0000000 --- a/Reachability.m +++ /dev/null @@ -1,814 +0,0 @@ -/* - - File: Reachability.m - Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs. - - Version: 2.0.4ddg - */ - -/* - Significant additions made by Andrew W. Donoho, August 11, 2009. - This is a derived work of Apple's Reachability v2.0 class. - - The below license is the new BSD license with the OSI recommended personalizations. - - - Extensions Copyright (C) 2009 Donoho Design Group, LLC. All Rights Reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of Andrew W. Donoho nor Donoho Design Group, L.L.C. - may be used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY DONOHO DESIGN GROUP, L.L.C. "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - */ - - -/* - - Apple's Original License on Reachability v2.0 - - Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. - ("Apple") in consideration of your agreement to the following terms, and your - use, installation, modification or redistribution of this Apple software - constitutes acceptance of these terms. If you do not agree with these terms, - please do not use, install, modify or redistribute this Apple software. - - In consideration of your agreement to abide by the following terms, and subject - to these terms, Apple grants you a personal, non-exclusive license, under - Apple's copyrights in this original Apple software (the "Apple Software"), to - use, reproduce, modify and redistribute the Apple Software, with or without - modifications, in source and/or binary forms; provided that if you redistribute - the Apple Software in its entirety and without modifications, you must retain - this notice and the following text and disclaimers in all such redistributions - of the Apple Software. - - Neither the name, trademarks, service marks or logos of Apple Inc. may be used - to endorse or promote products derived from the Apple Software without specific - prior written permission from Apple. Except as expressly stated in this notice, - no other rights or licenses, express or implied, are granted by Apple herein, - including but not limited to any patent rights that may be infringed by your - derivative works or by other works in which the Apple Software may be - incorporated. - - The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO - WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED - WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN - COMBINATION WITH YOUR PRODUCTS. - - IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR - DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF - CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF - APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - Copyright (C) 2009 Apple Inc. All Rights Reserved. - -*/ - -/* - Each reachability object now has a copy of the key used to store it in a dictionary. - This allows each observer to quickly determine if the event is important to them. -*/ - -#import -#import -#import -#import -#import -#import - -#import - -#import "Reachability.h" - -NSString *const kInternetConnection = @"InternetConnection"; -NSString *const kLocalWiFiConnection = @"LocalWiFiConnection"; -NSString *const kReachabilityChangedNotification = @"NetworkReachabilityChangedNotification"; - -#define CLASS_DEBUG 1 // Turn on logReachabilityFlags. Must also have a project wide defined DEBUG. - -#if (defined DEBUG && defined CLASS_DEBUG) -#define logReachabilityFlags(flags) (logReachabilityFlags_(__PRETTY_FUNCTION__, __LINE__, flags)) - -static NSString *reachabilityFlags_(SCNetworkReachabilityFlags flags) { - -#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 30000) // Apple advises you to use the magic number instead of a symbol. - return [NSString stringWithFormat:@"Reachability Flags: %c%c %c%c%c%c%c%c%c", - (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-', - (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-', - - (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-', - (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-', - (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-', - (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-', - (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-', - (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-', - (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-']; -#else - // Compile out the v3.0 features for v2.2.1 deployment. - return [NSString stringWithFormat:@"Reachability Flags: %c%c %c%c%c%c%c%c", - (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-', - (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-', - - (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-', - (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-', - (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-', - // v3 kSCNetworkReachabilityFlagsConnectionOnTraffic == v2 kSCNetworkReachabilityFlagsConnectionAutomatic - (flags & kSCNetworkReachabilityFlagsConnectionAutomatic) ? 'C' : '-', - // (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-', // No v2 equivalent. - (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-', - (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-']; -#endif - -} // reachabilityFlags_() - -static void logReachabilityFlags_(const char *name, int line, SCNetworkReachabilityFlags flags) { - - NSLog(@"%s (%d) \n\t%@", name, line, reachabilityFlags_(flags)); - -} // logReachabilityFlags_() - -#define logNetworkStatus(status) (logNetworkStatus_(__PRETTY_FUNCTION__, __LINE__, status)) - -static void logNetworkStatus_(const char *name, int line, NetworkStatus status) { - - NSString *statusString = nil; - - switch (status) { - case kNotReachable: - statusString = [NSString stringWithString: @"Not Reachable"]; - break; - case kReachableViaWWAN: - statusString = [NSString stringWithString: @"Reachable via WWAN"]; - break; - case kReachableViaWiFi: - statusString = [NSString stringWithString: @"Reachable via WiFi"]; - break; - } - - NSLog(@"%s (%d) \n\tNetwork Status: %@", name, line, statusString); - -} // logNetworkStatus_() - -#else -#define logReachabilityFlags(flags) -#define logNetworkStatus(status) -#endif - -@interface Reachability (private) - -- (NetworkStatus) networkStatusForFlags: (SCNetworkReachabilityFlags) flags; - -@end - -@implementation Reachability - -@synthesize key = key_; - -// Preclude direct access to ivars. -+ (BOOL) accessInstanceVariablesDirectly { - - return NO; - -} // accessInstanceVariablesDirectly - - -- (void) dealloc { - - [self stopNotifier]; - if(reachabilityRef) { - - CFRelease(reachabilityRef); reachabilityRef = NULL; - - } - - self.key = nil; - - [super dealloc]; - -} // dealloc - - -- (Reachability *) initWithReachabilityRef: (SCNetworkReachabilityRef) ref -{ - self = [super init]; - if (self != nil) - { - reachabilityRef = ref; - } - - return self; - -} // initWithReachabilityRef: - - -#if (defined DEBUG && defined CLASS_DEBUG) -- (NSString *) description { - - NSAssert(reachabilityRef, @"-description called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags = 0; - - SCNetworkReachabilityGetFlags(reachabilityRef, &flags); - - return [NSString stringWithFormat: @"%@\n\t%@", self.key, reachabilityFlags_(flags)]; - -} // description -#endif - - -#pragma mark - -#pragma mark Notification Management Methods - - -//Start listening for reachability notifications on the current run loop -static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) { - - #pragma unused (target, flags) - NSCAssert(info, @"info was NULL in ReachabilityCallback"); - NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], @"info was the wrong class in ReachabilityCallback"); - - //We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively - // in case someone uses the Reachablity object in a different thread. - NSAutoreleasePool* pool = [NSAutoreleasePool new]; - - // Post a notification to notify the client that the network reachability changed. - [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification - object: (Reachability *) info]; - - [pool release]; - -} // ReachabilityCallback() - - -- (BOOL) startNotifier { - - SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL}; - - if(SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context)) { - - if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) { - - return YES; - - } - - } - - return NO; - -} // startNotifier - - -- (void) stopNotifier { - - if(reachabilityRef) { - - SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); - - } - -} // stopNotifier - - -- (BOOL) isEqual: (Reachability *) r { - - return [r.key isEqualToString: self.key]; - -} // isEqual: - - -#pragma mark - -#pragma mark Reachability Allocation Methods - - -+ (Reachability *) reachabilityWithHostName: (NSString *) hostName { - - SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]); - - if (ref) { - - Reachability *r = [[[self alloc] initWithReachabilityRef: ref] autorelease]; - - r.key = hostName; - - return r; - - } - - return nil; - -} // reachabilityWithHostName - - -+ (NSString *) makeAddressKey: (in_addr_t) addr { - // addr is assumed to be in network byte order. - - static const int highShift = 24; - static const int highMidShift = 16; - static const int lowMidShift = 8; - static const in_addr_t mask = 0x000000ff; - - addr = ntohl(addr); - - return [NSString stringWithFormat: @"%d.%d.%d.%d", - (addr >> highShift) & mask, - (addr >> highMidShift) & mask, - (addr >> lowMidShift) & mask, - addr & mask]; - -} // makeAddressKey: - - -+ (Reachability *) reachabilityWithAddress: (const struct sockaddr_in *) hostAddress { - - SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress); - - if (ref) { - - Reachability *r = [[[self alloc] initWithReachabilityRef: ref] autorelease]; - - r.key = [self makeAddressKey: hostAddress->sin_addr.s_addr]; - - return r; - - } - - return nil; - -} // reachabilityWithAddress - - -+ (Reachability *) reachabilityForInternetConnection { - - struct sockaddr_in zeroAddress; - bzero(&zeroAddress, sizeof(zeroAddress)); - zeroAddress.sin_len = sizeof(zeroAddress); - zeroAddress.sin_family = AF_INET; - - Reachability *r = [self reachabilityWithAddress: &zeroAddress]; - - r.key = kInternetConnection; - - return r; - -} // reachabilityForInternetConnection - - -+ (Reachability *) reachabilityForLocalWiFi { - - struct sockaddr_in localWifiAddress; - bzero(&localWifiAddress, sizeof(localWifiAddress)); - localWifiAddress.sin_len = sizeof(localWifiAddress); - localWifiAddress.sin_family = AF_INET; - // IN_LINKLOCALNETNUM is defined in as 169.254.0.0 - localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); - - Reachability *r = [self reachabilityWithAddress: &localWifiAddress]; - - r.key = kLocalWiFiConnection; - - return r; - -} // reachabilityForLocalWiFi - - -#pragma mark - -#pragma mark Network Flag Handling Methods - - -#if USE_DDG_EXTENSIONS -// -// iPhone condition codes as reported by a 3GS running iPhone OS v3.0. -// Airplane Mode turned on: Reachability Flag Status: -- ------- -// WWAN Active: Reachability Flag Status: WR -t----- -// WWAN Connection required: Reachability Flag Status: WR ct----- -// WiFi turned on: Reachability Flag Status: -R ------- Reachable. -// Local WiFi turned on: Reachability Flag Status: -R xxxxxxd Reachable. -// WiFi turned on: Reachability Flag Status: -R ct----- Connection down. (Non-intuitive, empirically determined answer.) -const SCNetworkReachabilityFlags kConnectionDown = kSCNetworkReachabilityFlagsConnectionRequired | - kSCNetworkReachabilityFlagsTransientConnection; -// WiFi turned on: Reachability Flag Status: -R ct-i--- Reachable but it will require user intervention (e.g. enter a WiFi password). -// WiFi turned on: Reachability Flag Status: -R -t----- Reachable via VPN. -// -// In the below method, an 'x' in the flag status means I don't care about its value. -// -// This method differs from Apple's by testing explicitly for empirically observed values. -// This gives me more confidence in it's correct behavior. Apple's code covers more cases -// than mine. My code covers the cases that occur. -// -- (NetworkStatus) networkStatusForFlags: (SCNetworkReachabilityFlags) flags { - - if (flags & kSCNetworkReachabilityFlagsReachable) { - - // Local WiFi -- Test derived from Apple's code: -localWiFiStatusForFlags:. - if (self.key == kLocalWiFiConnection) { - - // Reachability Flag Status: xR xxxxxxd Reachable. - return (flags & kSCNetworkReachabilityFlagsIsDirect) ? kReachableViaWiFi : kNotReachable; - - } - - // Observed WWAN Values: - // WWAN Active: Reachability Flag Status: WR -t----- - // WWAN Connection required: Reachability Flag Status: WR ct----- - // - // Test Value: Reachability Flag Status: WR xxxxxxx - if (flags & kSCNetworkReachabilityFlagsIsWWAN) { return kReachableViaWWAN; } - - // Clear moot bits. - flags &= ~kSCNetworkReachabilityFlagsReachable; - flags &= ~kSCNetworkReachabilityFlagsIsDirect; - flags &= ~kSCNetworkReachabilityFlagsIsLocalAddress; // kInternetConnection is local. - - // Reachability Flag Status: -R ct---xx Connection down. - if (flags == kConnectionDown) { return kNotReachable; } - - // Reachability Flag Status: -R -t---xx Reachable. WiFi + VPN(is up) (Thank you Ling Wang) - if (flags & kSCNetworkReachabilityFlagsTransientConnection) { return kReachableViaWiFi; } - - // Reachability Flag Status: -R -----xx Reachable. - if (flags == 0) { return kReachableViaWiFi; } - - // Apple's code tests for dynamic connection types here. I don't. - // If a connection is required, regardless of whether it is on demand or not, it is a WiFi connection. - // If you care whether a connection needs to be brought up, use -isConnectionRequired. - // If you care about whether user intervention is necessary, use -isInterventionRequired. - // If you care about dynamically establishing the connection, use -isConnectionIsOnDemand. - - // Reachability Flag Status: -R cxxxxxx Reachable. - if (flags & kSCNetworkReachabilityFlagsConnectionRequired) { return kReachableViaWiFi; } - - // Required by the compiler. Should never get here. Default to not connected. -#if (defined DEBUG && defined CLASS_DEBUG) - NSAssert1(NO, @"Uncaught reachability test. Flags: %@", reachabilityFlags_(flags)); -#endif - return kNotReachable; - - } - - // Reachability Flag Status: x- xxxxxxx - return kNotReachable; - -} // networkStatusForFlags: - - -- (NetworkStatus) currentReachabilityStatus { - - NSAssert(reachabilityRef, @"currentReachabilityStatus called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags = 0; - NetworkStatus status = kNotReachable; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - -// logReachabilityFlags(flags); - - status = [self networkStatusForFlags: flags]; - - return status; - - } - - return kNotReachable; - -} // currentReachabilityStatus - - -- (BOOL) isReachable { - - NSAssert(reachabilityRef, @"isReachable called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags = 0; - NetworkStatus status = kNotReachable; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - -// logReachabilityFlags(flags); - - status = [self networkStatusForFlags: flags]; - -// logNetworkStatus(status); - - return (kNotReachable != status); - - } - - return NO; - -} // isReachable - - -- (BOOL) isConnectionRequired { - - NSAssert(reachabilityRef, @"isConnectionRequired called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - - logReachabilityFlags(flags); - - return (flags & kSCNetworkReachabilityFlagsConnectionRequired); - - } - - return NO; - -} // isConnectionRequired - - -- (BOOL) connectionRequired { - - return [self isConnectionRequired]; - -} // connectionRequired -#endif - - -#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 30000) -static const SCNetworkReachabilityFlags kOnDemandConnection = kSCNetworkReachabilityFlagsConnectionOnTraffic | - kSCNetworkReachabilityFlagsConnectionOnDemand; -#else -static const SCNetworkReachabilityFlags kOnDemandConnection = kSCNetworkReachabilityFlagsConnectionAutomatic; -#endif - -- (BOOL) isConnectionOnDemand { - - NSAssert(reachabilityRef, @"isConnectionIsOnDemand called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - - logReachabilityFlags(flags); - - return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) && - (flags & kOnDemandConnection)); - - } - - return NO; - -} // isConnectionOnDemand - - -- (BOOL) isInterventionRequired { - - NSAssert(reachabilityRef, @"isInterventionRequired called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - - logReachabilityFlags(flags); - - return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) && - (flags & kSCNetworkReachabilityFlagsInterventionRequired)); - - } - - return NO; - -} // isInterventionRequired - - -- (BOOL) isReachableViaWWAN { - - NSAssert(reachabilityRef, @"isReachableViaWWAN called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags = 0; - NetworkStatus status = kNotReachable; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - - logReachabilityFlags(flags); - - status = [self networkStatusForFlags: flags]; - - return (kReachableViaWWAN == status); - - } - - return NO; - -} // isReachableViaWWAN - - -- (BOOL) isReachableViaWiFi { - - NSAssert(reachabilityRef, @"isReachableViaWiFi called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags = 0; - NetworkStatus status = kNotReachable; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - - logReachabilityFlags(flags); - - status = [self networkStatusForFlags: flags]; - - return (kReachableViaWiFi == status); - - } - - return NO; - -} // isReachableViaWiFi - - -- (SCNetworkReachabilityFlags) reachabilityFlags { - - NSAssert(reachabilityRef, @"reachabilityFlags called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags = 0; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - - logReachabilityFlags(flags); - - return flags; - - } - - return 0; - -} // reachabilityFlags - - -#pragma mark - -#pragma mark Apple's Network Flag Handling Methods - - -#if !USE_DDG_EXTENSIONS -/* - * - * Apple's Network Status testing code. - * The only changes that have been made are to use the new logReachabilityFlags macro and - * test for local WiFi via the key instead of Apple's boolean. Also, Apple's code was for v3.0 only - * iPhone OS. v2.2.1 and earlier conditional compiling is turned on. Hence, to mirror Apple's behavior, - * set your Base SDK to v3.0 or higher. - * - */ - -- (NetworkStatus) localWiFiStatusForFlags: (SCNetworkReachabilityFlags) flags -{ - logReachabilityFlags(flags); - - BOOL retVal = NotReachable; - if((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect)) - { - retVal = ReachableViaWiFi; - } - return retVal; -} - - -- (NetworkStatus) networkStatusForFlags: (SCNetworkReachabilityFlags) flags -{ - logReachabilityFlags(flags); - if (!(flags & kSCNetworkReachabilityFlagsReachable)) - { - // if target host is not reachable - return NotReachable; - } - - BOOL retVal = NotReachable; - - if (!(flags & kSCNetworkReachabilityFlagsConnectionRequired)) - { - // if target host is reachable and no connection is required - // then we'll assume (for now) that your on Wi-Fi - retVal = ReachableViaWiFi; - } - -#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 30000) // Apple advises you to use the magic number instead of a symbol. - if ((flags & kSCNetworkReachabilityFlagsConnectionOnDemand) || - (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)) -#else - if (flags & kSCNetworkReachabilityFlagsConnectionAutomatic) -#endif - { - // ... and the connection is on-demand (or on-traffic) if the - // calling application is using the CFSocketStream or higher APIs - - if (!(flags & kSCNetworkReachabilityFlagsInterventionRequired)) - { - // ... and no [user] intervention is needed - retVal = ReachableViaWiFi; - } - } - - if (flags & kSCNetworkReachabilityFlagsIsWWAN) - { - // ... but WWAN connections are OK if the calling application - // is using the CFNetwork (CFSocketStream?) APIs. - retVal = ReachableViaWWAN; - } - return retVal; -} - - -- (NetworkStatus) currentReachabilityStatus -{ - NSAssert(reachabilityRef, @"currentReachabilityStatus called with NULL reachabilityRef"); - - NetworkStatus retVal = NotReachable; - SCNetworkReachabilityFlags flags; - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) - { - if(self.key == kLocalWiFiConnection) - { - retVal = [self localWiFiStatusForFlags: flags]; - } - else - { - retVal = [self networkStatusForFlags: flags]; - } - } - return retVal; -} - - -- (BOOL) isReachable { - - NSAssert(reachabilityRef, @"isReachable called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags = 0; - NetworkStatus status = kNotReachable; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - - logReachabilityFlags(flags); - - if(self.key == kLocalWiFiConnection) { - - status = [self localWiFiStatusForFlags: flags]; - - } else { - - status = [self networkStatusForFlags: flags]; - - } - - return (kNotReachable != status); - - } - - return NO; - -} // isReachable - - -- (BOOL) isConnectionRequired { - - return [self connectionRequired]; - -} // isConnectionRequired - - -- (BOOL) connectionRequired { - - NSAssert(reachabilityRef, @"connectionRequired called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - - logReachabilityFlags(flags); - - return (flags & kSCNetworkReachabilityFlagsConnectionRequired); - - } - - return NO; - -} // connectionRequired -#endif - -@end diff --git a/RegexKitLite.h b/RegexKitLite.h deleted file mode 100644 index d467702..0000000 --- a/RegexKitLite.h +++ /dev/null @@ -1,295 +0,0 @@ -// -// RegexKitLite.h -// http://regexkit.sourceforge.net/ -// Licensed under the terms of the BSD License, as specified below. -// - -/* - Copyright (c) 2008-2010, John Engelhart - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the Zang Industries nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifdef __OBJC__ -#import -#import -#import -#import -#import -#endif // __OBJC__ - -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef REGEXKITLITE_VERSION_DEFINED -#define REGEXKITLITE_VERSION_DEFINED - -#define _RKL__STRINGIFY(b) #b -#define _RKL_STRINGIFY(a) _RKL__STRINGIFY(a) -#define _RKL_JOIN_VERSION(a,b) _RKL_STRINGIFY(a##.##b) -#define _RKL_VERSION_STRING(a,b) _RKL_JOIN_VERSION(a,b) - -#define REGEXKITLITE_VERSION_MAJOR 4 -#define REGEXKITLITE_VERSION_MINOR 0 - -#define REGEXKITLITE_VERSION_CSTRING _RKL_VERSION_STRING(REGEXKITLITE_VERSION_MAJOR, REGEXKITLITE_VERSION_MINOR) -#define REGEXKITLITE_VERSION_NSSTRING @REGEXKITLITE_VERSION_CSTRING - -#endif // REGEXKITLITE_VERSION_DEFINED - -#if !defined(RKL_BLOCKS) && defined(NS_BLOCKS_AVAILABLE) && (NS_BLOCKS_AVAILABLE == 1) -#define RKL_BLOCKS 1 -#endif - -#if defined(RKL_BLOCKS) && (RKL_BLOCKS == 1) -#define _RKL_BLOCKS_ENABLED 1 -#endif // defined(RKL_BLOCKS) && (RKL_BLOCKS == 1) - -#if defined(_RKL_BLOCKS_ENABLED) && !defined(__BLOCKS__) -#warning RegexKitLite support for Blocks is enabled, but __BLOCKS__ is not defined. This compiler may not support Blocks, in which case the behavior is undefined. This will probably cause numerous compiler errors. -#endif // defined(_RKL_BLOCKS_ENABLED) && !defined(__BLOCKS__) - -// For Mac OS X < 10.5. -#ifndef NSINTEGER_DEFINED -#define NSINTEGER_DEFINED -#if defined(__LP64__) || defined(NS_BUILD_32_LIKE_64) -typedef long NSInteger; -typedef unsigned long NSUInteger; -#define NSIntegerMin LONG_MIN -#define NSIntegerMax LONG_MAX -#define NSUIntegerMax ULONG_MAX -#else // defined(__LP64__) || defined(NS_BUILD_32_LIKE_64) -typedef int NSInteger; -typedef unsigned int NSUInteger; -#define NSIntegerMin INT_MIN -#define NSIntegerMax INT_MAX -#define NSUIntegerMax UINT_MAX -#endif // defined(__LP64__) || defined(NS_BUILD_32_LIKE_64) -#endif // NSINTEGER_DEFINED - -#ifndef RKLREGEXOPTIONS_DEFINED -#define RKLREGEXOPTIONS_DEFINED - -// These must be identical to their ICU regex counterparts. See http://www.icu-project.org/userguide/regexp.html -enum { - RKLNoOptions = 0, - RKLCaseless = 2, - RKLComments = 4, - RKLDotAll = 32, - RKLMultiline = 8, - RKLUnicodeWordBoundaries = 256 -}; -typedef uint32_t RKLRegexOptions; // This must be identical to the ICU 'flags' argument type. - -#endif // RKLREGEXOPTIONS_DEFINED - -#ifndef RKLREGEXENUMERATIONOPTIONS_DEFINED -#define RKLREGEXENUMERATIONOPTIONS_DEFINED - -enum { - RKLRegexEnumerationNoOptions = 0UL, - RKLRegexEnumerationCapturedStringsNotRequired = 1UL << 9, - RKLRegexEnumerationReleaseStringReturnedByReplacementBlock = 1UL << 10, - RKLRegexEnumerationFastCapturedStringsXXX = 1UL << 11, -}; -typedef NSUInteger RKLRegexEnumerationOptions; - -#endif // RKLREGEXENUMERATIONOPTIONS_DEFINED - -#ifndef _REGEXKITLITE_H_ -#define _REGEXKITLITE_H_ - -#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__APPLE_CC__) && (__APPLE_CC__ >= 5465) -#define RKL_DEPRECATED_ATTRIBUTE __attribute__((deprecated)) -#else -#define RKL_DEPRECATED_ATTRIBUTE -#endif - -#if defined(NS_REQUIRES_NIL_TERMINATION) -#define RKL_REQUIRES_NIL_TERMINATION NS_REQUIRES_NIL_TERMINATION -#else // defined(NS_REQUIRES_NIL_TERMINATION) -#define RKL_REQUIRES_NIL_TERMINATION -#endif // defined(NS_REQUIRES_NIL_TERMINATION) - -// This requires a few levels of rewriting to get the desired results. -#define _RKL_CONCAT_2(c,d) c ## d -#define _RKL_CONCAT(a,b) _RKL_CONCAT_2(a,b) - -#ifdef RKL_PREPEND_TO_METHODS -#define RKL_METHOD_PREPEND(x) _RKL_CONCAT(RKL_PREPEND_TO_METHODS, x) -#else // RKL_PREPEND_TO_METHODS -#define RKL_METHOD_PREPEND(x) x -#endif // RKL_PREPEND_TO_METHODS - -// If it looks like low memory notifications might be available, add code to register and respond to them. -// This is (should be) harmless if it turns out that this isn't the case, since the notification that we register for, -// UIApplicationDidReceiveMemoryWarningNotification, is dynamically looked up via dlsym(). -#if ((defined(TARGET_OS_EMBEDDED) && (TARGET_OS_EMBEDDED != 0)) || (defined(TARGET_OS_IPHONE) && (TARGET_OS_IPHONE != 0))) && (!defined(RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS) || (RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS != 0)) -#define RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS 1 -#endif - -#ifdef __OBJC__ - -// NSException exception name. -extern NSString * const RKLICURegexException; - -// NSError error domains and user info keys. -extern NSString * const RKLICURegexErrorDomain; - -extern NSString * const RKLICURegexEnumerationOptionsErrorKey; -extern NSString * const RKLICURegexErrorCodeErrorKey; -extern NSString * const RKLICURegexErrorNameErrorKey; -extern NSString * const RKLICURegexLineErrorKey; -extern NSString * const RKLICURegexOffsetErrorKey; -extern NSString * const RKLICURegexPreContextErrorKey; -extern NSString * const RKLICURegexPostContextErrorKey; -extern NSString * const RKLICURegexRegexErrorKey; -extern NSString * const RKLICURegexRegexOptionsErrorKey; -extern NSString * const RKLICURegexReplacedCountErrorKey; -extern NSString * const RKLICURegexReplacedStringErrorKey; -extern NSString * const RKLICURegexReplacementStringErrorKey; -extern NSString * const RKLICURegexSubjectRangeErrorKey; -extern NSString * const RKLICURegexSubjectStringErrorKey; - -@interface NSString (RegexKitLiteAdditions) - -+ (void)RKL_METHOD_PREPEND(clearStringCache); - -// Although these are marked as deprecated, a bug in GCC prevents a warning from being issues for + class methods. Filed bug with Apple, #6736857. -+ (NSInteger)RKL_METHOD_PREPEND(captureCountForRegex):(NSString *)regex RKL_DEPRECATED_ATTRIBUTE; -+ (NSInteger)RKL_METHOD_PREPEND(captureCountForRegex):(NSString *)regex options:(RKLRegexOptions)options error:(NSError **)error RKL_DEPRECATED_ATTRIBUTE; - -- (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex; -- (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex range:(NSRange)range; -- (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error; - -- (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex; -- (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex inRange:(NSRange)range; -- (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error; - -- (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex; -- (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex capture:(NSInteger)capture; -- (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex inRange:(NSRange)range; -- (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range capture:(NSInteger)capture error:(NSError **)error; - -- (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex; -- (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex capture:(NSInteger)capture; -- (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex inRange:(NSRange)range; -- (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range capture:(NSInteger)capture error:(NSError **)error; - -- (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement; -- (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement range:(NSRange)searchRange; -- (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement options:(RKLRegexOptions)options range:(NSRange)searchRange error:(NSError **)error; - - //// >= 3.0 - -- (NSInteger)RKL_METHOD_PREPEND(captureCount); -- (NSInteger)RKL_METHOD_PREPEND(captureCountWithOptions):(RKLRegexOptions)options error:(NSError **)error; - -- (BOOL)RKL_METHOD_PREPEND(isRegexValid); -- (BOOL)RKL_METHOD_PREPEND(isRegexValidWithOptions):(RKLRegexOptions)options error:(NSError **)error; - -- (void)RKL_METHOD_PREPEND(flushCachedRegexData); - -- (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex; -- (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex capture:(NSInteger)capture; -- (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex range:(NSRange)range; -- (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range capture:(NSInteger)capture error:(NSError **)error; - - -- (NSArray *)RKL_METHOD_PREPEND(captureComponentsMatchedByRegex):(NSString *)regex; -- (NSArray *)RKL_METHOD_PREPEND(captureComponentsMatchedByRegex):(NSString *)regex range:(NSRange)range; -- (NSArray *)RKL_METHOD_PREPEND(captureComponentsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error; - -- (NSArray *)RKL_METHOD_PREPEND(arrayOfCaptureComponentsMatchedByRegex):(NSString *)regex; -- (NSArray *)RKL_METHOD_PREPEND(arrayOfCaptureComponentsMatchedByRegex):(NSString *)regex range:(NSRange)range; -- (NSArray *)RKL_METHOD_PREPEND(arrayOfCaptureComponentsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error; - - //// >= 4.0 - -- (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex withKeysAndCaptures:(id)firstKey, ... RKL_REQUIRES_NIL_TERMINATION; -- (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex range:(NSRange)range withKeysAndCaptures:(id)firstKey, ... RKL_REQUIRES_NIL_TERMINATION; -- (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeysAndCaptures:(id)firstKey, ... RKL_REQUIRES_NIL_TERMINATION; -- (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withFirstKey:(id)firstKey arguments:(va_list)varArgsList; - -- (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeys:(id *)keys forCaptures:(int *)captures count:(NSUInteger)count; - -- (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex withKeysAndCaptures:(id)firstKey, ... RKL_REQUIRES_NIL_TERMINATION; -- (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex range:(NSRange)range withKeysAndCaptures:(id)firstKey, ... RKL_REQUIRES_NIL_TERMINATION; -- (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeysAndCaptures:(id)firstKey, ... RKL_REQUIRES_NIL_TERMINATION; -- (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withFirstKey:(id)firstKey arguments:(va_list)varArgsList; - -- (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeys:(id *)keys forCaptures:(int *)captures count:(NSUInteger)count; - -#ifdef _RKL_BLOCKS_ENABLED - -- (BOOL)RKL_METHOD_PREPEND(enumerateStringsMatchedByRegex):(NSString *)regex usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; -- (BOOL)RKL_METHOD_PREPEND(enumerateStringsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; - -- (BOOL)RKL_METHOD_PREPEND(enumerateStringsSeparatedByRegex):(NSString *)regex usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; -- (BOOL)RKL_METHOD_PREPEND(enumerateStringsSeparatedByRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; - -- (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; -- (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; - -#endif // _RKL_BLOCKS_ENABLED - -@end - -@interface NSMutableString (RegexKitLiteAdditions) - -- (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement; -- (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement range:(NSRange)searchRange; -- (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement options:(RKLRegexOptions)options range:(NSRange)searchRange error:(NSError **)error; - - //// >= 4.0 - -#ifdef _RKL_BLOCKS_ENABLED - -- (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; -- (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; - -#endif // _RKL_BLOCKS_ENABLED - -@end - -#endif // __OBJC__ - -#endif // _REGEXKITLITE_H_ - -#ifdef __cplusplus -} // extern "C" -#endif diff --git a/RegexKitLite.m b/RegexKitLite.m deleted file mode 100644 index dababa6..0000000 --- a/RegexKitLite.m +++ /dev/null @@ -1,2636 +0,0 @@ -// -// RegexKitLite.m -// http://regexkit.sourceforge.net/ -// Licensed under the terms of the BSD License, as specified below. -// - -/* - Copyright (c) 2008-2010, John Engelhart - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the Zang Industries nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include -#include -#include -#import -#import -#import -#import -#import -#import -#ifdef __OBJC_GC__ -#import -#define RKL_STRONG_REF __strong -#define RKL_GC_VOLATILE volatile -#else // __OBJC_GC__ -#define RKL_STRONG_REF -#define RKL_GC_VOLATILE -#endif // __OBJC_GC__ - -#if (defined(TARGET_OS_EMBEDDED) && (TARGET_OS_EMBEDDED != 0)) || (defined(TARGET_OS_IPHONE) && (TARGET_OS_IPHONE != 0)) || (defined(MAC_OS_X_VERSION_MIN_REQUIRED) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)) -#include -#else -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#import "RegexKitLite.h" - -// If the gcc flag -mmacosx-version-min is used with, for example, '=10.2', give a warning that the libicucore.dylib is only available on >= 10.3. -// If you are reading this comment because of this warning, this is to let you know that linking to /usr/lib/libicucore.dylib will cause your executable to fail on < 10.3. -// You will need to build your own version of the ICU library and link to that in order for RegexKitLite to work successfully on < 10.3. This is not simple. - -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1030 -#warning The ICU dynamic shared library, /usr/lib/libicucore.dylib, is only available on Mac OS X 10.3 and later. -#warning You will need to supply a version of the ICU library to use RegexKitLite on Mac OS X 10.2 and earlier. -#endif - -//////////// -#pragma mark Compile time tunables - -#ifndef RKL_CACHE_SIZE -#define RKL_CACHE_SIZE (13UL) -#endif - -#if RKL_CACHE_SIZE < 1 -#error RKL_CACHE_SIZE must be a non-negative number greater than 0. -#endif // RKL_CACHE_SIZE < 1 - -#ifndef RKL_FIXED_LENGTH -#define RKL_FIXED_LENGTH (2048UL) -#endif - -#if RKL_FIXED_LENGTH < 1 -#error RKL_FIXED_LENGTH must be a non-negative number greater than 0. -#endif // RKL_FIXED_LENGTH < 1 - -#ifndef RKL_STACK_LIMIT -#define RKL_STACK_LIMIT (128UL * 1024UL) -#endif - -#if RKL_STACK_LIMIT < 0 -#error RKL_STACK_LIMIT must be a non-negative number. -#endif // RKL_STACK_LIMIT < 0 - -#ifdef RKL_APPEND_TO_ICU_FUNCTIONS -#define RKL_ICU_FUNCTION_APPEND(x) _RKL_CONCAT(x, RKL_APPEND_TO_ICU_FUNCTIONS) -#else // RKL_APPEND_TO_ICU_FUNCTIONS -#define RKL_ICU_FUNCTION_APPEND(x) x -#endif // RKL_APPEND_TO_ICU_FUNCTIONS - -#if defined(RKL_DTRACE) && (RKL_DTRACE != 0) -#define _RKL_DTRACE_ENABLED 1 -#endif // defined(RKL_DTRACE) && (RKL_DTRACE != 0) - -// These are internal, non-public tunables. -#define _RKL_FIXED_LENGTH ((NSUInteger)RKL_FIXED_LENGTH) -#define _RKL_STACK_LIMIT ((NSUInteger)RKL_STACK_LIMIT) -#define _RKL_SCRATCH_BUFFERS (5UL) -#if _RKL_SCRATCH_BUFFERS != 5 -#error _RKL_SCRATCH_BUFFERS is not tunable, it must be set to 5. -#endif // _RKL_SCRATCH_BUFFERS != 5 -#define _RKL_PREFETCH_SIZE (64UL) -#define _RKL_DTRACE_REGEXUTF8_SIZE (64UL) - -// A LRU Cache Set holds 4 lines, and the LRU algorithm uses 4 bits per line. -// A LRU Cache Set has a type of RKLLRUCacheSet_t and is 16 bits wide (4 lines * 4 bits per line). -// RKLLRUCacheSet_t must be initialized to a value of 0x0137 in order to work correctly. -typedef uint16_t RKLLRUCacheSet_t; -#define _RKL_LRU_CACHE_SET_INIT ((RKLLRUCacheSet_t)0x0137U) -#define _RKL_LRU_CACHE_SET_WAYS (4UL) -#if _RKL_LRU_CACHE_SET_WAYS != 4 -#error _RKL_LRU_CACHE_SET_WAYS is not tunable, it must be set to 4. -#endif // _RKL_LRU_CACHE_SET_WAYS != 4 - -#define _RKL_REGEX_LRU_CACHE_SETS ((NSUInteger)(RKL_CACHE_SIZE)) -#define _RKL_REGEX_CACHE_LINES ((NSUInteger)((NSUInteger)(_RKL_REGEX_LRU_CACHE_SETS) * (NSUInteger)(_RKL_LRU_CACHE_SET_WAYS))) - -// Regex String Lookaside Cache parameters. -#define _RKL_REGEX_LOOKASIDE_CACHE_BITS (6UL) -#if _RKL_REGEX_LOOKASIDE_CACHE_BITS < 0 -#error _RKL_REGEX_LOOKASIDE_CACHE_BITS must be a non-negative number and is not intended to be user tunable. -#endif // _RKL_REGEX_LOOKASIDE_CACHE_BITS < 0 -#define _RKL_REGEX_LOOKASIDE_CACHE_SIZE (1LU << _RKL_REGEX_LOOKASIDE_CACHE_BITS) -#define _RKL_REGEX_LOOKASIDE_CACHE_MASK ((1LU << _RKL_REGEX_LOOKASIDE_CACHE_BITS) - 1LU) -// RKLLookasideCache_t should be large enough to to hold the maximum number of cached regexes, or (RKL_CACHE_SIZE * _RKL_LRU_CACHE_SET_WAYS). -#if (RKL_CACHE_SIZE * _RKL_LRU_CACHE_SET_WAYS) <= (1 << 8) -typedef uint8_t RKLLookasideCache_t; -#elif (RKL_CACHE_SIZE * _RKL_LRU_CACHE_SET_WAYS) <= (1 << 16) -typedef uint16_t RKLLookasideCache_t; -#else // (RKL_CACHE_SIZE * _RKL_LRU_CACHE_SET_WAYS) > (1 << 16) -typedef uint32_t RKLLookasideCache_t; -#endif // (RKL_CACHE_SIZE * _RKL_LRU_CACHE_SET_WAYS) - -////////////// -#pragma mark - -#pragma mark GCC / Compiler macros - -#if defined (__GNUC__) && (__GNUC__ >= 4) -#define RKL_ATTRIBUTES(attr, ...) __attribute__((attr, ##__VA_ARGS__)) -#define RKL_EXPECTED(cond, expect) __builtin_expect((long)(cond), (expect)) -#define RKL_PREFETCH(ptr) __builtin_prefetch(ptr) -#define RKL_PREFETCH_UNICHAR(ptr, off) { const char *p = ((const char *)(ptr)) + ((off) * sizeof(UniChar)) + _RKL_PREFETCH_SIZE; RKL_PREFETCH(p); RKL_PREFETCH(p + _RKL_PREFETCH_SIZE); } -#define RKL_HAVE_CLEANUP -#define RKL_CLEANUP(func) RKL_ATTRIBUTES(cleanup(func)) -#else // defined (__GNUC__) && (__GNUC__ >= 4) -#define RKL_ATTRIBUTES(attr, ...) -#define RKL_EXPECTED(cond, expect) (cond) -#define RKL_PREFETCH(ptr) -#define RKL_PREFETCH_UNICHAR(ptr, off) -#define RKL_CLEANUP(func) -#endif // defined (__GNUC__) && (__GNUC__ >= 4) - -#define RKL_STATIC_INLINE static __inline__ RKL_ATTRIBUTES(always_inline) -#define RKL_ALIGNED(arg) RKL_ATTRIBUTES(aligned(arg)) -#define RKL_UNUSED_ARG RKL_ATTRIBUTES(unused) -#define RKL_WARN_UNUSED RKL_ATTRIBUTES(warn_unused_result) -#define RKL_WARN_UNUSED_CONST RKL_ATTRIBUTES(warn_unused_result, const) -#define RKL_WARN_UNUSED_PURE RKL_ATTRIBUTES(warn_unused_result, pure) -#define RKL_WARN_UNUSED_SENTINEL RKL_ATTRIBUTES(warn_unused_result, sentinel) -#define RKL_NONNULL_ARGS(arg, ...) RKL_ATTRIBUTES(nonnull(arg, ##__VA_ARGS__)) -#define RKL_WARN_UNUSED_NONNULL_ARGS(arg, ...) RKL_ATTRIBUTES(warn_unused_result, nonnull(arg, ##__VA_ARGS__)) -#define RKL_WARN_UNUSED_CONST_NONNULL_ARGS(arg, ...) RKL_ATTRIBUTES(warn_unused_result, const, nonnull(arg, ##__VA_ARGS__)) -#define RKL_WARN_UNUSED_PURE_NONNULL_ARGS(arg, ...) RKL_ATTRIBUTES(warn_unused_result, pure, nonnull(arg, ##__VA_ARGS__)) - -#if defined (__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3) -#define RKL_ALLOC_SIZE_NON_NULL_ARGS_WARN_UNUSED(as, nn, ...) RKL_ATTRIBUTES(warn_unused_result, nonnull(nn, ##__VA_ARGS__), alloc_size(as)) -#else // defined (__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3) -#define RKL_ALLOC_SIZE_NON_NULL_ARGS_WARN_UNUSED(as, nn, ...) RKL_ATTRIBUTES(warn_unused_result, nonnull(nn, ##__VA_ARGS__)) -#endif // defined (__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3) - -#ifdef _RKL_DTRACE_ENABLED -#define RKL_UNUSED_DTRACE_ARG -#else // _RKL_DTRACE_ENABLED -#define RKL_UNUSED_DTRACE_ARG RKL_ATTRIBUTES(unused) -#endif // _RKL_DTRACE_ENABLED - -//////////// -#pragma mark - -#pragma mark Assertion macros - -// These macros are nearly identical to their NSCParameterAssert siblings. -// This is required because nearly everything is done while rkl_cacheSpinLock is locked. -// We need to safely unlock before throwing any of these exceptions. -// @try {} @finally {} significantly slows things down so it's not used. - -#define RKLCHardAbortAssert(c) do { int _c=(c); if(RKL_EXPECTED(!_c, 0L)) { NSLog(@"%@:%ld: Invalid parameter not satisfying: %s\n", [NSString stringWithUTF8String:__FILE__], (long)__LINE__, #c); abort(); } } while(0) -#define RKLCAssertDictionary(d, ...) rkl_makeAssertDictionary(__PRETTY_FUNCTION__, __FILE__, __LINE__, (d), ##__VA_ARGS__) -#define RKLCDelayedHardAssert(c, e, g) do { id *_e=(e); int _c=(c); if(RKL_EXPECTED(_e == NULL, 0L) || RKL_EXPECTED(*_e != NULL, 0L)) { goto g; } if(RKL_EXPECTED(!_c, 0L)) { *_e = RKLCAssertDictionary(@"Invalid parameter not satisfying: %s", #c); goto g; } } while(0) - -#ifdef NS_BLOCK_ASSERTIONS -#define RKLCAbortAssert(c) -#define RKLCDelayedAssert(c, e, g) -#define RKL_UNUSED_ASSERTION_ARG RKL_ATTRIBUTES(unused) -#else // NS_BLOCK_ASSERTIONS -#define RKLCAbortAssert(c) RKLCHardAbortAssert(c) -#define RKLCDelayedAssert(c, e, g) RKLCDelayedHardAssert(c, e, g) -#define RKL_UNUSED_ASSERTION_ARG -#endif // NS_BLOCK_ASSERTIONS - -#define RKL_EXCEPTION(e, f, ...) [NSException exceptionWithName:(e) reason:rkl_stringFromClassAndMethod((self), (_cmd), (f), ##__VA_ARGS__) userInfo:NULL] -#define RKL_RAISE_EXCEPTION(e, f, ...) [RKL_EXCEPTION(e, f, ##__VA_ARGS__) raise] - -//////////// -#pragma mark - -#pragma mark Utility functions and macros - -RKL_STATIC_INLINE BOOL NSRangeInsideRange(NSRange cin, NSRange win) RKL_WARN_UNUSED; -RKL_STATIC_INLINE BOOL NSRangeInsideRange(NSRange cin, NSRange win) { return((((cin.location - win.location) <= win.length) && ((NSMaxRange(cin) - win.location) <= win.length)) ? YES : NO); } - -#define NSMakeRange(loc, len) ((NSRange){.location=(NSUInteger)(loc), .length=(NSUInteger)(len)}) -#define CFMakeRange(loc, len) ((CFRange){.location= (CFIndex)(loc), .length= (CFIndex)(len)}) -#define NSNotFoundRange ((NSRange){.location=(NSUInteger)NSNotFound, .length= 0UL}) -#define NSMaxiumRange ((NSRange){.location= 0UL, .length= NSUIntegerMax}) -// These values are used to help tickle improper usage. -#define RKLIllegalRange ((NSRange){.location= NSIntegerMax, .length= NSIntegerMax}) -#define RKLIllegalPointer ((void * RKL_GC_VOLATILE)0xBAD0C0DE) - -//////////// -#pragma mark - -#pragma mark Exported NSString symbols for exception names, error domains, error keys, etc - -NSString * const RKLICURegexException = @"RKLICURegexException"; - -NSString * const RKLICURegexErrorDomain = @"RKLICURegexErrorDomain"; - -NSString * const RKLICURegexEnumerationOptionsErrorKey = @"RKLICURegexEnumerationOptions"; -NSString * const RKLICURegexErrorCodeErrorKey = @"RKLICURegexErrorCode"; -NSString * const RKLICURegexErrorNameErrorKey = @"RKLICURegexErrorName"; -NSString * const RKLICURegexLineErrorKey = @"RKLICURegexLine"; -NSString * const RKLICURegexOffsetErrorKey = @"RKLICURegexOffset"; -NSString * const RKLICURegexPreContextErrorKey = @"RKLICURegexPreContext"; -NSString * const RKLICURegexPostContextErrorKey = @"RKLICURegexPostContext"; -NSString * const RKLICURegexRegexErrorKey = @"RKLICURegexRegex"; -NSString * const RKLICURegexRegexOptionsErrorKey = @"RKLICURegexRegexOptions"; -NSString * const RKLICURegexReplacedCountErrorKey = @"RKLICURegexReplacedCount"; -NSString * const RKLICURegexReplacedStringErrorKey = @"RKLICURegexReplacedString"; -NSString * const RKLICURegexReplacementStringErrorKey = @"RKLICURegexReplacementString"; -NSString * const RKLICURegexSubjectRangeErrorKey = @"RKLICURegexSubjectRange"; -NSString * const RKLICURegexSubjectStringErrorKey = @"RKLICURegexSubjectString"; - -// Used internally by rkl_userInfoDictionary to specify which arguments should be set in the NSError userInfo dictionary. -enum { - RKLUserInfoNone = 0UL, - RKLUserInfoSubjectRange = 1UL << 0, - RKLUserInfoReplacedCount = 1UL << 1, - RKLUserInfoRegexEnumerationOptions = 1UL << 2, -}; -typedef NSUInteger RKLUserInfoOptions; - -//////////// -#pragma mark - -#pragma mark Type / struct definitions - -// In general, the ICU bits and pieces here must exactly match the definition in the ICU sources. - -#define U_STRING_NOT_TERMINATED_WARNING -124 -#define U_ZERO_ERROR 0 -#define U_INDEX_OUTOFBOUNDS_ERROR 8 -#define U_BUFFER_OVERFLOW_ERROR 15 -#define U_PARSE_CONTEXT_LEN 16 - -typedef struct uregex uregex; // Opaque ICU regex type. - -typedef struct UParseError { // This must be exactly the same as the 'real' ICU declaration. - int32_t line; - int32_t offset; - UniChar preContext[U_PARSE_CONTEXT_LEN]; - UniChar postContext[U_PARSE_CONTEXT_LEN]; -} UParseError; - -// For use with GCC's cleanup() __attribute__. -enum { - RKLLockedCacheSpinLock = 1UL << 0, - RKLUnlockedCacheSpinLock = 1UL << 1, -}; - -enum { - RKLSplitOp = 1UL, - RKLReplaceOp = 2UL, - RKLRangeOp = 3UL, - RKLArrayOfStringsOp = 4UL, - RKLArrayOfCapturesOp = 5UL, - RKLCapturesArrayOp = 6UL, - RKLDictionaryOfCapturesOp = 7UL, - RKLArrayOfDictionariesOfCapturesOp = 8UL, - RKLMaskOp = 0xFUL, - RKLReplaceMutable = 1UL << 4, - RKLSubcapturesArray = 1UL << 5, -}; -typedef NSUInteger RKLRegexOp; - -enum { - RKLBlockEnumerationMatchOp = 1UL, - RKLBlockEnumerationReplaceOp = 2UL, -}; -typedef NSUInteger RKLBlockEnumerationOp; - -typedef struct { - RKL_STRONG_REF NSRange * RKL_GC_VOLATILE ranges; - NSRange findInRange, remainingRange; - NSInteger capacity, found, findUpTo, capture, addedSplitRanges; - size_t size, stackUsed; - RKL_STRONG_REF void ** RKL_GC_VOLATILE rangesScratchBuffer; - RKL_STRONG_REF void ** RKL_GC_VOLATILE stringsScratchBuffer; - RKL_STRONG_REF void ** RKL_GC_VOLATILE arraysScratchBuffer; - RKL_STRONG_REF void ** RKL_GC_VOLATILE dictionariesScratchBuffer; - RKL_STRONG_REF void ** RKL_GC_VOLATILE keysScratchBuffer; -} RKLFindAll; - -typedef struct { - CFStringRef string; - CFHashCode hash; - CFIndex length; - RKL_STRONG_REF UniChar * RKL_GC_VOLATILE uniChar; -} RKLBuffer; - -typedef struct { - CFStringRef regexString; - CFHashCode regexHash; - RKLRegexOptions options; - uregex *icu_regex; - NSInteger captureCount; - - CFStringRef setToString; - CFHashCode setToHash; - CFIndex setToLength; - NSUInteger setToIsImmutable:1; - NSUInteger setToNeedsConversion:1; - RKL_STRONG_REF const UniChar * RKL_GC_VOLATILE setToUniChar; - NSRange setToRange, lastFindRange, lastMatchRange; - - RKLBuffer *buffer; -} RKLCachedRegex; - -//////////// -#pragma mark - -#pragma mark Translation unit scope global variables - -static RKLLRUCacheSet_t rkl_lruFixedBufferCacheSet = _RKL_LRU_CACHE_SET_INIT, rkl_lruDynamicBufferCacheSet = _RKL_LRU_CACHE_SET_INIT; -static RKLBuffer rkl_lruDynamicBuffer[_RKL_LRU_CACHE_SET_WAYS]; -static UniChar rkl_lruFixedUniChar[_RKL_LRU_CACHE_SET_WAYS][_RKL_FIXED_LENGTH]; // This is the fixed sized UTF-16 conversion buffer. -static RKLBuffer rkl_lruFixedBuffer[_RKL_LRU_CACHE_SET_WAYS] = {{NULL, 0UL, 0L, &rkl_lruFixedUniChar[0][0]}, {NULL, 0UL, 0L, &rkl_lruFixedUniChar[1][0]}, {NULL, 0UL, 0L, &rkl_lruFixedUniChar[2][0]}, {NULL, 0UL, 0L, &rkl_lruFixedUniChar[3][0]}}; -static RKLCachedRegex rkl_cachedRegexes[_RKL_REGEX_CACHE_LINES]; -#if defined(__GNUC__) && (__GNUC__ == 4) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ == 2) -static RKLCachedRegex * volatile rkl_lastCachedRegex; // XXX This is a work around for what appears to be a optimizer code generation bug in GCC 4.2. -#else -static RKLCachedRegex *rkl_lastCachedRegex; -#endif // defined(__GNUC__) && (__GNUC__ == 4) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ == 2) -static RKLLRUCacheSet_t rkl_cachedRegexCacheSets[_RKL_REGEX_LRU_CACHE_SETS] = { [0 ... (_RKL_REGEX_LRU_CACHE_SETS - 1UL)] = _RKL_LRU_CACHE_SET_INIT }; -static RKLLookasideCache_t rkl_regexLookasideCache[_RKL_REGEX_LOOKASIDE_CACHE_SIZE] RKL_ALIGNED(64); -static OSSpinLock rkl_cacheSpinLock = OS_SPINLOCK_INIT; -static const UniChar rkl_emptyUniCharString[1]; // For safety, icu_regexes are 'set' to this when the string they were searched is cleared. -static RKL_STRONG_REF void * RKL_GC_VOLATILE rkl_scratchBuffer[_RKL_SCRATCH_BUFFERS]; // Used to hold temporary allocations that are allocated via reallocf(). - -//////////// -#pragma mark - -#pragma mark CFArray and CFDictionary call backs - -// These are used when running under manual memory management for the array that rkl_splitArray creates. -// The split strings are created, but not autoreleased. The (immutable) array is created using these callbacks, which skips the CFRetain() call, effectively transferring ownership to the CFArray object. -// For each split string this saves the overhead of an autorelease, then an array retain, then an NSAutoreleasePool release. This is good for a ~30% speed increase. - -static void rkl_CFCallbackRelease(CFAllocatorRef allocator RKL_UNUSED_ARG, const void *ptr) { CFRelease((CFTypeRef)ptr); } -static const CFArrayCallBacks rkl_transferOwnershipArrayCallBacks = { (CFIndex)0L, NULL, rkl_CFCallbackRelease, CFCopyDescription, CFEqual }; -static const CFDictionaryKeyCallBacks rkl_transferOwnershipDictionaryKeyCallBacks = { (CFIndex)0L, NULL, rkl_CFCallbackRelease, CFCopyDescription, CFEqual, CFHash }; -static const CFDictionaryValueCallBacks rkl_transferOwnershipDictionaryValueCallBacks = { (CFIndex)0L, NULL, rkl_CFCallbackRelease, CFCopyDescription, CFEqual }; - -#ifdef __OBJC_GC__ -//////////// -#pragma mark - -#pragma mark Low-level Garbage Collection aware memory/resource allocation utilities -// If compiled with Garbage Collection, we need to be able to do a few things slightly differently. -// The basic premiss is that under GC we use a trampoline function pointer which is set to a _start function to catch the first invocation. -// The _start function checks if GC is running and then overwrites the function pointer with the appropriate routine. Think of it as 'lazy linking'. - -enum { RKLScannedOption = NSScannedOption }; - -// rkl_collectingEnabled uses objc_getClass() to get the NSGarbageCollector class, which doesn't exist on earlier systems. -// This allows for graceful failure should we find ourselves running on an earlier version of the OS without NSGarbageCollector. -static BOOL rkl_collectingEnabled_first (void); -static BOOL rkl_collectingEnabled_yes (void) { return(YES); } -static BOOL rkl_collectingEnabled_no (void) { return(NO); } -static BOOL(*rkl_collectingEnabled) (void) = rkl_collectingEnabled_first; -static BOOL rkl_collectingEnabled_first (void) { - BOOL gcEnabled = ([objc_getClass("NSGarbageCollector") defaultCollector] != NULL) ? YES : NO; - if(gcEnabled == YES) { - // This section of code is required due to what I consider to be a fundamental design flaw in Cocoas GC system. - // Earlier versions of "Garbage Collection Programming Guide" stated that (paraphrased) "all globals are automatically roots". - // Current versions of the guide now include the following warning: - // "You may pass addresses of strong globals or statics into routines expecting pointers to object pointers (such as id* or NSError**) - // only if they have first been assigned to directly, rather than through a pointer dereference." - // This is a surprisingly non-trivial condition to actually meet in practice and is a recipe for impossible to debug race condition bugs. - // We just happen to be very, very, very lucky in the fact that we can initialize our root set before the first use. - NSUInteger x = 0UL; - for(x = 0UL; x < _RKL_SCRATCH_BUFFERS; x++) { rkl_scratchBuffer[x] = NSAllocateCollectable(16UL, 0UL); rkl_scratchBuffer[x] = NULL; } - for(x = 0UL; x < _RKL_LRU_CACHE_SET_WAYS; x++) { rkl_lruDynamicBuffer[x].uniChar = NSAllocateCollectable(16UL, 0UL); rkl_lruDynamicBuffer[x].uniChar = NULL; } - } - return((rkl_collectingEnabled = (gcEnabled == YES) ? rkl_collectingEnabled_yes : rkl_collectingEnabled_no)()); -} - -// rkl_realloc() -static void *rkl_realloc_first (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr, size_t size, NSUInteger flags); -static void *rkl_realloc_std (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr, size_t size, NSUInteger flags RKL_UNUSED_ARG) { return((*ptr = reallocf(*ptr, size))); } -static void *rkl_realloc_gc (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr, size_t size, NSUInteger flags) { return((*ptr = NSReallocateCollectable(*ptr, (NSUInteger)size, flags))); } -static void *(*rkl_realloc) (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr, size_t size, NSUInteger flags) RKL_ALLOC_SIZE_NON_NULL_ARGS_WARN_UNUSED(2,1) = rkl_realloc_first; -static void *rkl_realloc_first (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr, size_t size, NSUInteger flags) { if(rkl_collectingEnabled()==YES) { rkl_realloc = rkl_realloc_gc; } else { rkl_realloc = rkl_realloc_std; } return(rkl_realloc(ptr, size, flags)); } - -// rkl_free() -static void * rkl_free_first (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr); -static void * rkl_free_std (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr) { if(*ptr != NULL) { free(*ptr); *ptr = NULL; } return(NULL); } -static void * rkl_free_gc (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr) { if(*ptr != NULL) { *ptr = NULL; } return(NULL); } -static void *(*rkl_free) (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr) RKL_NONNULL_ARGS(1) = rkl_free_first; -static void *rkl_free_first (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr) { if(rkl_collectingEnabled()==YES) { rkl_free = rkl_free_gc; } else { rkl_free = rkl_free_std; } return(rkl_free(ptr)); } - -// rkl_CFAutorelease() -static id rkl_CFAutorelease_first (CFTypeRef obj); -static id rkl_CFAutorelease_std (CFTypeRef obj) { return([(id)obj autorelease]); } -static id rkl_CFAutorelease_gc (CFTypeRef obj) { return(NSMakeCollectable(obj)); } -static id(*rkl_CFAutorelease) (CFTypeRef obj) = rkl_CFAutorelease_first; -static id rkl_CFAutorelease_first (CFTypeRef obj) { return((rkl_CFAutorelease = (rkl_collectingEnabled()==YES) ? rkl_CFAutorelease_gc : rkl_CFAutorelease_std)(obj)); } - -// rkl_CreateStringWithSubstring() -static id rkl_CreateStringWithSubstring_first (id string, NSRange range); -static id rkl_CreateStringWithSubstring_std (id string, NSRange range) { return((id)CFStringCreateWithSubstring(NULL, (CFStringRef)string, CFMakeRange((CFIndex)range.location, (CFIndex)range.length))); } -static id rkl_CreateStringWithSubstring_gc (id string, NSRange range) { return([string substringWithRange:range]); } -static id(*rkl_CreateStringWithSubstring) (id string, NSRange range) RKL_WARN_UNUSED_NONNULL_ARGS(1) = rkl_CreateStringWithSubstring_first; -static id rkl_CreateStringWithSubstring_first (id string, NSRange range) { return((rkl_CreateStringWithSubstring = (rkl_collectingEnabled()==YES) ? rkl_CreateStringWithSubstring_gc : rkl_CreateStringWithSubstring_std)(string, range)); } - -// rkl_ReleaseObject() -static id rkl_ReleaseObject_first (id obj); -static id rkl_ReleaseObject_std (id obj) { CFRelease((CFTypeRef)obj); return(NULL); } -static id rkl_ReleaseObject_gc (id obj RKL_UNUSED_ARG) { return(NULL); } -static id (*rkl_ReleaseObject) (id obj) RKL_NONNULL_ARGS(1) = rkl_ReleaseObject_first; -static id rkl_ReleaseObject_first (id obj) { return((rkl_ReleaseObject = (rkl_collectingEnabled()==YES) ? rkl_ReleaseObject_gc : rkl_ReleaseObject_std)(obj)); } - -// rkl_CreateArrayWithObjects() -static id rkl_CreateArrayWithObjects_first (void **objects, NSUInteger count); -static id rkl_CreateArrayWithObjects_std (void **objects, NSUInteger count) { return((id)CFArrayCreate(NULL, (const void **)objects, (CFIndex)count, &rkl_transferOwnershipArrayCallBacks)); } -static id rkl_CreateArrayWithObjects_gc (void **objects, NSUInteger count) { return([NSArray arrayWithObjects:(const id *)objects count:count]); } -static id(*rkl_CreateArrayWithObjects) (void **objects, NSUInteger count) RKL_WARN_UNUSED_NONNULL_ARGS(1) = rkl_CreateArrayWithObjects_first; -static id rkl_CreateArrayWithObjects_first (void **objects, NSUInteger count) { return((rkl_CreateArrayWithObjects = (rkl_collectingEnabled()==YES) ? rkl_CreateArrayWithObjects_gc : rkl_CreateArrayWithObjects_std)(objects, count)); } - -// rkl_CreateAutoreleasedArray() -static id rkl_CreateAutoreleasedArray_first (void **objects, NSUInteger count); -static id rkl_CreateAutoreleasedArray_std (void **objects, NSUInteger count) { return((id)rkl_CFAutorelease(rkl_CreateArrayWithObjects(objects, count))); } -static id rkl_CreateAutoreleasedArray_gc (void **objects, NSUInteger count) { return( rkl_CreateArrayWithObjects(objects, count) ); } -static id(*rkl_CreateAutoreleasedArray) (void **objects, NSUInteger count) RKL_WARN_UNUSED_NONNULL_ARGS(1) = rkl_CreateAutoreleasedArray_first; -static id rkl_CreateAutoreleasedArray_first (void **objects, NSUInteger count) { return((rkl_CreateAutoreleasedArray = (rkl_collectingEnabled()==YES) ? rkl_CreateAutoreleasedArray_gc : rkl_CreateAutoreleasedArray_std)(objects, count)); } - -#else // __OBJC_GC__ not defined -//////////// -#pragma mark - -#pragma mark Low-level explicit memory/resource allocation utilities - -enum { RKLScannedOption = 0 }; - -#define rkl_collectingEnabled() (NO) - -RKL_STATIC_INLINE void *rkl_realloc (void **ptr, size_t size, NSUInteger flags) RKL_ALLOC_SIZE_NON_NULL_ARGS_WARN_UNUSED(2,1); -RKL_STATIC_INLINE void *rkl_free (void **ptr) RKL_NONNULL_ARGS(1); -RKL_STATIC_INLINE id rkl_CFAutorelease (CFTypeRef obj) RKL_WARN_UNUSED_NONNULL_ARGS(1); -RKL_STATIC_INLINE id rkl_CreateAutoreleasedArray (void **objects, NSUInteger count) RKL_WARN_UNUSED_NONNULL_ARGS(1); -RKL_STATIC_INLINE id rkl_CreateArrayWithObjects (void **objects, NSUInteger count) RKL_WARN_UNUSED_NONNULL_ARGS(1); -RKL_STATIC_INLINE id rkl_CreateStringWithSubstring (id string, NSRange range) RKL_WARN_UNUSED_NONNULL_ARGS(1); -RKL_STATIC_INLINE id rkl_ReleaseObject (id obj) RKL_NONNULL_ARGS(1); - -RKL_STATIC_INLINE void *rkl_realloc (void **ptr, size_t size, NSUInteger flags RKL_UNUSED_ARG) { return((*ptr = reallocf(*ptr, size))); } -RKL_STATIC_INLINE void *rkl_free (void **ptr) { if(*ptr != NULL) { free(*ptr); *ptr = NULL; } return(NULL); } -RKL_STATIC_INLINE id rkl_CFAutorelease (CFTypeRef obj) { return([(id)obj autorelease]); } -RKL_STATIC_INLINE id rkl_CreateArrayWithObjects (void **objects, NSUInteger count) { return((id)CFArrayCreate(NULL, (const void **)objects, (CFIndex)count, &rkl_transferOwnershipArrayCallBacks)); } -RKL_STATIC_INLINE id rkl_CreateAutoreleasedArray (void **objects, NSUInteger count) { return(rkl_CFAutorelease(rkl_CreateArrayWithObjects(objects, count))); } -RKL_STATIC_INLINE id rkl_CreateStringWithSubstring (id string, NSRange range) { return((id)CFStringCreateWithSubstring(NULL, (CFStringRef)string, CFMakeRange((CFIndex)range.location, (CFIndex)range.length))); } -RKL_STATIC_INLINE id rkl_ReleaseObject (id obj) { CFRelease((CFTypeRef)obj); return(NULL); } - -#endif // __OBJC_GC__ - -//////////// -#pragma mark - -#pragma mark ICU function prototypes - -// ICU functions. See http://www.icu-project.org/apiref/icu4c/uregex_8h.html Tweaked slightly from the originals, but functionally identical. -const char *RKL_ICU_FUNCTION_APPEND(u_errorName) ( int32_t status) RKL_WARN_UNUSED_PURE; -int32_t RKL_ICU_FUNCTION_APPEND(u_strlen) (const UniChar *s) RKL_WARN_UNUSED_PURE_NONNULL_ARGS(1); -int32_t RKL_ICU_FUNCTION_APPEND(uregex_appendReplacement) ( uregex *regexp, const UniChar *replacementText, int32_t replacementLength, UniChar **destBuf, int32_t *destCapacity, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,2,4,5,6); -int32_t RKL_ICU_FUNCTION_APPEND(uregex_appendTail) ( uregex *regexp, UniChar **destBuf, int32_t *destCapacity, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,2,3,4); -void RKL_ICU_FUNCTION_APPEND(uregex_close) ( uregex *regexp) RKL_NONNULL_ARGS(1); -int32_t RKL_ICU_FUNCTION_APPEND(uregex_end) ( uregex *regexp, int32_t groupNum, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,3); -BOOL RKL_ICU_FUNCTION_APPEND(uregex_find) ( uregex *regexp, int32_t location, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,3); -BOOL RKL_ICU_FUNCTION_APPEND(uregex_findNext) ( uregex *regexp, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,2); -int32_t RKL_ICU_FUNCTION_APPEND(uregex_groupCount) ( uregex *regexp, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,2); -uregex *RKL_ICU_FUNCTION_APPEND(uregex_open) (const UniChar *pattern, int32_t patternLength, RKLRegexOptions flags, UParseError *parseError, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,4,5); -void RKL_ICU_FUNCTION_APPEND(uregex_reset) ( uregex *regexp, int32_t newIndex, int32_t *status) RKL_NONNULL_ARGS(1,3); -void RKL_ICU_FUNCTION_APPEND(uregex_setText) ( uregex *regexp, const UniChar *text, int32_t textLength, int32_t *status) RKL_NONNULL_ARGS(1,2,4); -int32_t RKL_ICU_FUNCTION_APPEND(uregex_start) ( uregex *regexp, int32_t groupNum, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,3); -uregex *RKL_ICU_FUNCTION_APPEND(uregex_clone) (const uregex *regexp, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,2); - -//////////// -#pragma mark - -#pragma mark RegexKitLite internal, private function prototypes - -// Functions used for managing the 4-way set associative LRU cache and regex string hash lookaside cache. -RKL_STATIC_INLINE NSUInteger rkl_leastRecentlyUsedWayInSet ( NSUInteger cacheSetsCount, const RKLLRUCacheSet_t cacheSetsArray[cacheSetsCount], NSUInteger set) RKL_WARN_UNUSED_NONNULL_ARGS(2); -RKL_STATIC_INLINE void rkl_accessCacheSetWay ( NSUInteger cacheSetsCount, RKLLRUCacheSet_t cacheSetsArray[cacheSetsCount], NSUInteger set, NSUInteger way) RKL_NONNULL_ARGS(2); -RKL_STATIC_INLINE NSUInteger rkl_regexLookasideCacheIndexForPointerAndOptions (const void *ptr, RKLRegexOptions options) RKL_WARN_UNUSED_NONNULL_ARGS(1); -RKL_STATIC_INLINE void rkl_setRegexLookasideCacheToCachedRegexForPointer (const RKLCachedRegex *cachedRegex, const void *ptr) RKL_NONNULL_ARGS(1,2); -RKL_STATIC_INLINE RKLCachedRegex *rkl_cachedRegexFromRegexLookasideCacheForString (const void *ptr, RKLRegexOptions options) RKL_WARN_UNUSED_NONNULL_ARGS(1); -RKL_STATIC_INLINE NSUInteger rkl_makeCacheSetHash ( CFHashCode regexHash, RKLRegexOptions options) RKL_WARN_UNUSED; -RKL_STATIC_INLINE NSUInteger rkl_cacheSetForRegexHashAndOptions ( CFHashCode regexHash, RKLRegexOptions options) RKL_WARN_UNUSED; -RKL_STATIC_INLINE NSUInteger rkl_cacheWayForCachedRegex (const RKLCachedRegex *cachedRegex) RKL_WARN_UNUSED_NONNULL_ARGS(1); -RKL_STATIC_INLINE NSUInteger rkl_cacheSetForCachedRegex (const RKLCachedRegex *cachedRegex) RKL_WARN_UNUSED_NONNULL_ARGS(1); -RKL_STATIC_INLINE RKLCachedRegex *rkl_cachedRegexForCacheSetAndWay ( NSUInteger cacheSet, NSUInteger cacheWay) RKL_WARN_UNUSED; -RKL_STATIC_INLINE RKLCachedRegex *rkl_cachedRegexForRegexHashAndOptionsAndWay ( CFHashCode regexHash, RKLRegexOptions options, NSUInteger cacheWay) RKL_WARN_UNUSED; -RKL_STATIC_INLINE void rkl_updateCachesWithCachedRegex ( RKLCachedRegex *cachedRegex, const void *ptr, int hitOrMiss RKL_UNUSED_DTRACE_ARG, int status RKL_UNUSED_DTRACE_ARG) RKL_NONNULL_ARGS(1,2); -RKL_STATIC_INLINE RKLCachedRegex *rkl_leastRecentlyUsedCachedRegexForRegexHashAndOptions ( CFHashCode regexHash, RKLRegexOptions options) RKL_WARN_UNUSED; - -static RKLCachedRegex *rkl_getCachedRegex (NSString *regexString, RKLRegexOptions options, NSError **error, id *exception) RKL_WARN_UNUSED_NONNULL_ARGS(1,4); -static NSUInteger rkl_setCachedRegexToString (RKLCachedRegex *cachedRegex, const NSRange *range, int32_t *status, id *exception RKL_UNUSED_ASSERTION_ARG) RKL_WARN_UNUSED_NONNULL_ARGS(1,2,3,4); -static RKLCachedRegex *rkl_getCachedRegexSetToString (NSString *regexString, RKLRegexOptions options, NSString *matchString, NSUInteger *matchLengthPtr, NSRange *matchRange, NSError **error, id *exception, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,3,4,5,7,8); -static id rkl_performDictionaryVarArgsOp(id self, SEL _cmd, RKLRegexOp regexOp, NSString *regexString, RKLRegexOptions options, NSInteger capture, id matchString, NSRange *matchRange, NSString *replacementString, NSError **error, void *result, id firstKey, va_list varArgsList) RKL_NONNULL_ARGS(1,2); -static id rkl_performRegexOp (id self, SEL _cmd, RKLRegexOp regexOp, NSString *regexString, RKLRegexOptions options, NSInteger capture, id matchString, NSRange *matchRange, NSString *replacementString, NSError **error, void *result, NSUInteger captureKeysCount, id captureKeys[captureKeysCount], const int captureKeyIndexes[captureKeysCount]) RKL_NONNULL_ARGS(1,2); -static void rkl_handleDelayedAssert (id self, SEL _cmd, id exception) RKL_NONNULL_ARGS(3); - -static NSUInteger rkl_search (RKLCachedRegex *cachedRegex, NSRange *searchRange, NSUInteger updateSearchRange, id *exception RKL_UNUSED_ASSERTION_ARG, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,2,4,5); - -static BOOL rkl_findRanges (RKLCachedRegex *cachedRegex, RKLRegexOp regexOp, RKLFindAll *findAll, id *exception, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,3,4,5); -static NSUInteger rkl_growFindRanges (RKLCachedRegex *cachedRegex, NSUInteger lastLocation, RKLFindAll *findAll, id *exception RKL_UNUSED_ASSERTION_ARG) RKL_WARN_UNUSED_NONNULL_ARGS(1,3,4); -static NSArray *rkl_makeArray (RKLCachedRegex *cachedRegex, RKLRegexOp regexOp, RKLFindAll *findAll, id *exception RKL_UNUSED_ASSERTION_ARG) RKL_WARN_UNUSED_NONNULL_ARGS(1,3,4); -static id rkl_makeDictionary (RKLCachedRegex *cachedRegex, RKLRegexOp regexOp, RKLFindAll *findAll, NSUInteger captureKeysCount, id captureKeys[captureKeysCount], const int captureKeyIndexes[captureKeysCount], id *exception RKL_UNUSED_ASSERTION_ARG) RKL_WARN_UNUSED_NONNULL_ARGS(1,3,5,6); - -static NSString *rkl_replaceString (RKLCachedRegex *cachedRegex, id searchString, NSUInteger searchU16Length, NSString *replacementString, NSUInteger replacementU16Length, NSInteger *replacedCount, NSUInteger replaceMutable, id *exception, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,2,4,8,9); -static int32_t rkl_replaceAll (RKLCachedRegex *cachedRegex, RKL_STRONG_REF const UniChar * RKL_GC_VOLATILE replacementUniChar, int32_t replacementU16Length, UniChar *replacedUniChar, int32_t replacedU16Capacity, NSInteger *replacedCount, int32_t *needU16Capacity, id *exception RKL_UNUSED_ASSERTION_ARG, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,2,4,6,7,8,9); - -static NSUInteger rkl_isRegexValid (id self, SEL _cmd, NSString *regex, RKLRegexOptions options, NSInteger *captureCountPtr, NSError **error) RKL_NONNULL_ARGS(1,2); - -static void rkl_clearStringCache (void); -static void rkl_clearBuffer (RKLBuffer *buffer, NSUInteger freeDynamicBuffer) RKL_NONNULL_ARGS(1); -static void rkl_clearCachedRegex (RKLCachedRegex *cachedRegex) RKL_NONNULL_ARGS(1); -static void rkl_clearCachedRegexSetTo (RKLCachedRegex *cachedRegex) RKL_NONNULL_ARGS(1); - -static NSDictionary *rkl_userInfoDictionary (RKLUserInfoOptions userInfoOptions, NSString *regexString, RKLRegexOptions options, const UParseError *parseError, int32_t status, NSString *matchString, NSRange matchRange, NSString *replacementString, NSString *replacedString, NSInteger replacedCount, RKLRegexEnumerationOptions enumerationOptions, ...) RKL_WARN_UNUSED_SENTINEL; -static NSError *rkl_makeNSError (RKLUserInfoOptions userInfoOptions, NSString *regexString, RKLRegexOptions options, const UParseError *parseError, int32_t status, NSString *matchString, NSRange matchRange, NSString *replacementString, NSString *replacedString, NSInteger replacedCount, RKLRegexEnumerationOptions enumerationOptions, NSString *errorDescription) RKL_WARN_UNUSED; - -static NSException *rkl_NSExceptionForRegex (NSString *regexString, RKLRegexOptions options, const UParseError *parseError, int32_t status) RKL_WARN_UNUSED_NONNULL_ARGS(1); -static NSDictionary *rkl_makeAssertDictionary (const char *function, const char *file, int line, NSString *format, ...) RKL_WARN_UNUSED_NONNULL_ARGS(1,2,4); -static NSString *rkl_stringFromClassAndMethod (id object, SEL selector, NSString *format, ...) RKL_WARN_UNUSED_NONNULL_ARGS(3); - -RKL_STATIC_INLINE int32_t rkl_getRangeForCapture(RKLCachedRegex *cr, int32_t *s, int32_t c, NSRange *r) RKL_WARN_UNUSED_NONNULL_ARGS(1,2,4); -RKL_STATIC_INLINE int32_t rkl_getRangeForCapture(RKLCachedRegex *cr, int32_t *s, int32_t c, NSRange *r) { uregex *re = cr->icu_regex; int32_t start = RKL_ICU_FUNCTION_APPEND(uregex_start)(re, c, s); if(RKL_EXPECTED((*s > U_ZERO_ERROR), 0L) || (start == -1)) { *r = NSNotFoundRange; } else { r->location = (NSUInteger)start; r->length = (NSUInteger)RKL_ICU_FUNCTION_APPEND(uregex_end)(re, c, s) - r->location; r->location += cr->setToRange.location; } return(*s); } - -RKL_STATIC_INLINE RKLFindAll rkl_makeFindAll(RKL_STRONG_REF NSRange * RKL_GC_VOLATILE r, NSRange fir, NSInteger c, size_t s, size_t su, RKL_STRONG_REF void ** RKL_GC_VOLATILE rsb, RKL_STRONG_REF void ** RKL_GC_VOLATILE ssb, RKL_STRONG_REF void ** RKL_GC_VOLATILE asb, RKL_STRONG_REF void ** RKL_GC_VOLATILE dsb, RKL_STRONG_REF void ** RKL_GC_VOLATILE ksb, NSInteger f, NSInteger cap, NSInteger fut) RKL_WARN_UNUSED_CONST; -RKL_STATIC_INLINE RKLFindAll rkl_makeFindAll(RKL_STRONG_REF NSRange * RKL_GC_VOLATILE r, NSRange fir, NSInteger c, size_t s, size_t su, RKL_STRONG_REF void ** RKL_GC_VOLATILE rsb, RKL_STRONG_REF void ** RKL_GC_VOLATILE ssb, RKL_STRONG_REF void ** RKL_GC_VOLATILE asb, RKL_STRONG_REF void ** RKL_GC_VOLATILE dsb, RKL_STRONG_REF void ** RKL_GC_VOLATILE ksb, NSInteger f, NSInteger cap, NSInteger fut) { return(((RKLFindAll){ .ranges=r, .findInRange=fir, .remainingRange=fir, .capacity=c, .found=f, .findUpTo=fut, .capture=cap, .addedSplitRanges=0L, .size=s, .stackUsed=su, .rangesScratchBuffer=rsb, .stringsScratchBuffer=ssb, .arraysScratchBuffer=asb, .dictionariesScratchBuffer=dsb, .keysScratchBuffer=ksb})); } - -//////////// -#pragma mark - -#pragma mark RKL_FAST_MUTABLE_CHECK implementation - -#ifdef RKL_FAST_MUTABLE_CHECK -// We use a trampoline function pointer to check at run time if the function __CFStringIsMutable is available. -// If it is, the trampoline function pointer is replaced with the address of that function. -// Otherwise, we assume the worst case that every string is mutable. -// This hopefully helps to protect us since we're using an undocumented, non-public API call. -// We will keep on working if it ever does go away, just with a bit less performance due to the overhead of mutable checks. - -static BOOL rkl_CFStringIsMutable_first (CFStringRef str); -static BOOL rkl_CFStringIsMutable_yes (CFStringRef str RKL_UNUSED_ARG) { return(YES); } -static BOOL(*rkl_CFStringIsMutable) (CFStringRef str) = rkl_CFStringIsMutable_first; -static BOOL rkl_CFStringIsMutable_first (CFStringRef str) { if((rkl_CFStringIsMutable = (BOOL(*)(CFStringRef))dlsym(RTLD_DEFAULT, "__CFStringIsMutable")) == NULL) { rkl_CFStringIsMutable = rkl_CFStringIsMutable_yes; } return(rkl_CFStringIsMutable(str)); } -#else // RKL_FAST_MUTABLE_CHECK is not defined. Assume that all strings are potentially mutable. -#define rkl_CFStringIsMutable(s) (YES) -#endif // RKL_FAST_MUTABLE_CHECK - -//////////// -#pragma mark - -#pragma mark iPhone / iPod touch low memory notification handler - -#if defined(RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS) && (RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS == 1) - -// The next few lines are specifically for the iPhone to catch low memory conditions. -// The basic idea is that rkl_RegisterForLowMemoryNotifications() is set to be run once by the linker at load time via __attribute((constructor)). -// rkl_RegisterForLowMemoryNotifications() tries to find the iPhone low memory notification symbol. If it can find it, -// it registers with the default NSNotificationCenter to call the RKLLowMemoryWarningObserver class method +lowMemoryWarning:. -// rkl_RegisterForLowMemoryNotifications() uses an atomic compare and swap to guarantee that it initializes exactly once. -// +lowMemoryWarning tries to acquire the cache lock. If it gets the lock, it clears the cache. If it can't, it calls performSelector: -// with a delay of half a second to try again. This will hopefully prevent any deadlocks, such as a RegexKitLite request for -// memory triggering a notification while the lock is held. - -static void rkl_RegisterForLowMemoryNotifications(void) RKL_ATTRIBUTES(used); - -@interface RKLLowMemoryWarningObserver : NSObject +(void)lowMemoryWarning:(id)notification; @end -@implementation RKLLowMemoryWarningObserver -+(void)lowMemoryWarning:(id)notification { - if(OSSpinLockTry(&rkl_cacheSpinLock)) { rkl_clearStringCache(); OSSpinLockUnlock(&rkl_cacheSpinLock); } - else { [[RKLLowMemoryWarningObserver class] performSelector:@selector(lowMemoryWarning:) withObject:notification afterDelay:(NSTimeInterval)0.1]; } -} -@end - -static volatile int rkl_HaveRegisteredForLowMemoryNotifications = 0; - -__attribute__((constructor)) static void rkl_RegisterForLowMemoryNotifications(void) { - _Bool didSwap = false; - void **memoryWarningNotification = NULL; - - while((rkl_HaveRegisteredForLowMemoryNotifications == 0) && ((didSwap = OSAtomicCompareAndSwapIntBarrier(0, 1, &rkl_HaveRegisteredForLowMemoryNotifications)) == false)) { /* Allows for spurious CAS failures. */ } - if(didSwap == true) { - if((memoryWarningNotification = (void **)dlsym(RTLD_DEFAULT, "UIApplicationDidReceiveMemoryWarningNotification")) != NULL) { - [[NSNotificationCenter defaultCenter] addObserver:[RKLLowMemoryWarningObserver class] selector:@selector(lowMemoryWarning:) name:(NSString *)*memoryWarningNotification object:NULL]; - } - } -} - -#endif // defined(RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS) && (RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS == 1) - -//////////// -#pragma mark - -#pragma mark DTrace functionality - -#ifdef _RKL_DTRACE_ENABLED - -// compiledRegexCache(unsigned long eventID, const char *regexUTF8, int options, int captures, int hitMiss, int icuStatusCode, const char *icuErrorMessage, double *hitRate); -// utf16ConversionCache(unsigned long eventID, unsigned int lookupResultFlags, double *hitRate, const void *string, unsigned long NSRange.location, unsigned long NSRange.length, long length); - -/* -provider RegexKitLite { - probe compiledRegexCache(unsigned long, const char *, unsigned int, int, int, int, const char *, double *); - probe utf16ConversionCache(unsigned long, unsigned int, double *, const void *, unsigned long, unsigned long, long); -}; - -#pragma D attributes Unstable/Unstable/Common provider RegexKitLite provider -#pragma D attributes Private/Private/Common provider RegexKitLite module -#pragma D attributes Private/Private/Common provider RegexKitLite function -#pragma D attributes Unstable/Unstable/Common provider RegexKitLite name -#pragma D attributes Unstable/Unstable/Common provider RegexKitLite args -*/ - -#define REGEXKITLITE_STABILITY "___dtrace_stability$RegexKitLite$v1$4_4_5_1_1_5_1_1_5_4_4_5_4_4_5" -#define REGEXKITLITE_TYPEDEFS "___dtrace_typedefs$RegexKitLite$v1" -#define REGEXKITLITE_COMPILEDREGEXCACHE(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) { __asm__ volatile(".reference " REGEXKITLITE_TYPEDEFS); __dtrace_probe$RegexKitLite$compiledRegexCache$v1$756e7369676e6564206c6f6e67$63686172202a$756e7369676e656420696e74$696e74$696e74$696e74$63686172202a$646f75626c65202a(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); __asm__ volatile(".reference " REGEXKITLITE_STABILITY); } -#define REGEXKITLITE_COMPILEDREGEXCACHE_ENABLED() __dtrace_isenabled$RegexKitLite$compiledRegexCache$v1() -#define REGEXKITLITE_CONVERTEDSTRINGU16CACHE(arg0, arg1, arg2, arg3, arg4, arg5, arg6) { __asm__ volatile(".reference " REGEXKITLITE_TYPEDEFS); __dtrace_probe$RegexKitLite$utf16ConversionCache$v1$756e7369676e6564206c6f6e67$756e7369676e656420696e74$646f75626c65202a$766f6964202a$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67$6c6f6e67(arg0, arg1, arg2, arg3, arg4, arg5, arg6); __asm__ volatile(".reference " REGEXKITLITE_STABILITY); } -#define REGEXKITLITE_CONVERTEDSTRINGU16CACHE_ENABLED() __dtrace_isenabled$RegexKitLite$utf16ConversionCache$v1() - -extern void __dtrace_probe$RegexKitLite$compiledRegexCache$v1$756e7369676e6564206c6f6e67$63686172202a$756e7369676e656420696e74$696e74$696e74$696e74$63686172202a$646f75626c65202a(unsigned long, const char *, unsigned int, int, int, int, const char *, double *); -extern int __dtrace_isenabled$RegexKitLite$compiledRegexCache$v1(void); -extern void __dtrace_probe$RegexKitLite$utf16ConversionCache$v1$756e7369676e6564206c6f6e67$756e7369676e656420696e74$646f75626c65202a$766f6964202a$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67$6c6f6e67(unsigned long, unsigned int, double *, const void *, unsigned long, unsigned long, long); -extern int __dtrace_isenabled$RegexKitLite$utf16ConversionCache$v1(void); - -//////////////////////////// - -enum { - RKLCacheHitLookupFlag = 1 << 0, - RKLConversionRequiredLookupFlag = 1 << 1, - RKLSetTextLookupFlag = 1 << 2, - RKLDynamicBufferLookupFlag = 1 << 3, - RKLErrorLookupFlag = 1 << 4, - RKLEnumerationBufferLookupFlag = 1 << 5, -}; - -#define rkl_dtrace_addLookupFlag(a,b) do { a |= (unsigned int)(b); } while(0) - -static char rkl_dtrace_regexUTF8[_RKL_REGEX_CACHE_LINES + 1UL][_RKL_DTRACE_REGEXUTF8_SIZE]; -static NSUInteger rkl_dtrace_eventID, rkl_dtrace_compiledCacheLookups, rkl_dtrace_compiledCacheHits, rkl_dtrace_conversionBufferLookups, rkl_dtrace_conversionBufferHits; - -#define rkl_dtrace_incrementEventID() do { rkl_dtrace_eventID++; } while(0) -#define rkl_dtrace_incrementAndGetEventID(v) do { rkl_dtrace_eventID++; v = rkl_dtrace_eventID; } while(0) -#define rkl_dtrace_compiledRegexCache(a0, a1, a2, a3, a4, a5) do { int _a3 = (a3); rkl_dtrace_compiledCacheLookups++; if(_a3 == 1) { rkl_dtrace_compiledCacheHits++; } if(RKL_EXPECTED(REGEXKITLITE_COMPILEDREGEXCACHE_ENABLED(), 0L)) { double hitRate = 0.0; if(rkl_dtrace_compiledCacheLookups > 0UL) { hitRate = ((double)rkl_dtrace_compiledCacheHits / (double)rkl_dtrace_compiledCacheLookups) * 100.0; } REGEXKITLITE_COMPILEDREGEXCACHE(rkl_dtrace_eventID, a0, a1, a2, _a3, a4, a5, &hitRate); } } while(0) -#define rkl_dtrace_utf16ConversionCache(a0, a1, a2, a3, a4) do { unsigned int _a0 = (a0); if((_a0 & RKLConversionRequiredLookupFlag) != 0U) { rkl_dtrace_conversionBufferLookups++; if((_a0 & RKLCacheHitLookupFlag) != 0U) { rkl_dtrace_conversionBufferHits++; } } if(RKL_EXPECTED(REGEXKITLITE_CONVERTEDSTRINGU16CACHE_ENABLED(), 0L)) { double hitRate = 0.0; if(rkl_dtrace_conversionBufferLookups > 0UL) { hitRate = ((double)rkl_dtrace_conversionBufferHits / (double)rkl_dtrace_conversionBufferLookups) * 100.0; } REGEXKITLITE_CONVERTEDSTRINGU16CACHE(rkl_dtrace_eventID, _a0, &hitRate, a1, a2, a3, a4); } } while(0) -#define rkl_dtrace_utf16ConversionCacheWithEventID(c0, a0, a1, a2, a3, a4) do { unsigned int _a0 = (a0); if((_a0 & RKLConversionRequiredLookupFlag) != 0U) { rkl_dtrace_conversionBufferLookups++; if((_a0 & RKLCacheHitLookupFlag) != 0U) { rkl_dtrace_conversionBufferHits++; } } if(RKL_EXPECTED(REGEXKITLITE_CONVERTEDSTRINGU16CACHE_ENABLED(), 0L)) { double hitRate = 0.0; if(rkl_dtrace_conversionBufferLookups > 0UL) { hitRate = ((double)rkl_dtrace_conversionBufferHits / (double)rkl_dtrace_conversionBufferLookups) * 100.0; } REGEXKITLITE_CONVERTEDSTRINGU16CACHE(c0, _a0, &hitRate, a1, a2, a3, a4); } } while(0) - - -// \342\200\246 == UTF8 for HORIZONTAL ELLIPSIS, aka triple dots '...' -#define RKL_UTF8_ELLIPSE "\342\200\246" - -// rkl_dtrace_getRegexUTF8 will copy the str argument to utf8Buffer using UTF8 as the string encoding. -// If the utf8 encoding would take up more bytes than the utf8Buffers length, then the unicode character 'HORIZONTAL ELLIPSIS' ('...') is appended to indicate truncation occurred. -static void rkl_dtrace_getRegexUTF8(CFStringRef str, char *utf8Buffer) RKL_NONNULL_ARGS(2); -static void rkl_dtrace_getRegexUTF8(CFStringRef str, char *utf8Buffer) { - if((str == NULL) || (utf8Buffer == NULL)) { return; } - CFIndex maxLength = ((CFIndex)_RKL_DTRACE_REGEXUTF8_SIZE - 2L), maxBytes = (maxLength - (CFIndex)sizeof(RKL_UTF8_ELLIPSE) - 1L), stringU16Length = CFStringGetLength(str), usedBytes = 0L; - CFStringGetBytes(str, CFMakeRange(0L, ((stringU16Length < maxLength) ? stringU16Length : maxLength)), kCFStringEncodingUTF8, (UInt8)'?', (Boolean)0, (UInt8 *)utf8Buffer, maxBytes, &usedBytes); - if(usedBytes == maxBytes) { strncpy(utf8Buffer + usedBytes, RKL_UTF8_ELLIPSE, ((size_t)_RKL_DTRACE_REGEXUTF8_SIZE - (size_t)usedBytes) - 2UL); } else { utf8Buffer[usedBytes] = (char)0; } -} - -#else // _RKL_DTRACE_ENABLED - -#define rkl_dtrace_incrementEventID() -#define rkl_dtrace_incrementAndGetEventID(v) -#define rkl_dtrace_compiledRegexCache(a0, a1, a2, a3, a4, a5) -#define rkl_dtrace_utf16ConversionCache(a0, a1, a2, a3, a4) -#define rkl_dtrace_utf16ConversionCacheWithEventID(c0, a0, a1, a2, a3, a4) -#define rkl_dtrace_getRegexUTF8(str, buf) -#define rkl_dtrace_addLookupFlag(a,b) - -#endif // _RKL_DTRACE_ENABLED - -//////////// -#pragma mark - -#pragma mark RegexKitLite low-level internal functions -#pragma mark - - -// The 4-way set associative LRU logic comes from Henry S. Warren Jr.'s Hacker's Delight, "revisions", 7-7 An LRU Algorithm: -// http://www.hackersdelight.org/revisions.pdf -// The functions rkl_leastRecentlyUsedWayInSet() and rkl_accessCacheSetWay() implement the cache functionality and are used -// from a number of different places that need to perform caching (i.e., cached regex, cached UTF16 conversions, etc) - -#pragma mark 4-way set associative LRU functions - -RKL_STATIC_INLINE NSUInteger rkl_leastRecentlyUsedWayInSet(NSUInteger cacheSetsCount, const RKLLRUCacheSet_t cacheSetsArray[cacheSetsCount], NSUInteger set) { - RKLCAbortAssert((cacheSetsArray != NULL) && ((NSInteger)cacheSetsCount > 0L) && (set < cacheSetsCount) && ((cacheSetsArray == rkl_cachedRegexCacheSets) ? set < _RKL_REGEX_LRU_CACHE_SETS : 1) && (((sizeof(unsigned int) - sizeof(RKLLRUCacheSet_t)) * 8) < (sizeof(unsigned int) * 8))); - unsigned int cacheSet = (((unsigned int)cacheSetsArray[set]) << ((sizeof(unsigned int) - sizeof(RKLLRUCacheSet_t)) * 8)); // __builtin_clz takes an 'unsigned int' argument. The rest is to ensure bit alignment regardless of 32/64/whatever. - NSUInteger leastRecentlyUsed = ((NSUInteger)(3LU - (NSUInteger)((__builtin_clz((~(((cacheSet & 0x77777777U) + 0x77777777U) | cacheSet | 0x77777777U))) ) >> 2))); - RKLCAbortAssert(leastRecentlyUsed < _RKL_LRU_CACHE_SET_WAYS); - return(leastRecentlyUsed); -} - -RKL_STATIC_INLINE void rkl_accessCacheSetWay(NSUInteger cacheSetsCount, RKLLRUCacheSet_t cacheSetsArray[cacheSetsCount], NSUInteger cacheSet, NSUInteger cacheWay) { - RKLCAbortAssert((cacheSetsArray != NULL) && ((NSInteger)cacheSetsCount > 0L) && (cacheSet < cacheSetsCount) && (cacheWay < _RKL_LRU_CACHE_SET_WAYS) && ((cacheSetsArray == rkl_cachedRegexCacheSets) ? cacheSet < _RKL_REGEX_LRU_CACHE_SETS : 1)); - cacheSetsArray[cacheSet] = (RKLLRUCacheSet_t)(((cacheSetsArray[cacheSet] & (RKLLRUCacheSet_t)0xFFFFU) | (((RKLLRUCacheSet_t)0xFU) << (cacheWay * 4U))) & (~(((RKLLRUCacheSet_t)0x1111U) << (3U - cacheWay)))); -} - -#pragma mark Common, macro'ish compiled regular expression cache logic - -// These functions consolidate bits and pieces of code used to maintain, update, and access the 4-way set associative LRU cache and Regex Lookaside Cache. -RKL_STATIC_INLINE NSUInteger rkl_regexLookasideCacheIndexForPointerAndOptions (const void *ptr, RKLRegexOptions options) { return(((((NSUInteger)(ptr)) >> 4) + options + (options >> 4)) & _RKL_REGEX_LOOKASIDE_CACHE_MASK); } -RKL_STATIC_INLINE void rkl_setRegexLookasideCacheToCachedRegexForPointer (const RKLCachedRegex *cachedRegex, const void *ptr) { rkl_regexLookasideCache[rkl_regexLookasideCacheIndexForPointerAndOptions(ptr, cachedRegex->options)] = (cachedRegex - rkl_cachedRegexes); } -RKL_STATIC_INLINE RKLCachedRegex *rkl_cachedRegexFromRegexLookasideCacheForString (const void *ptr, RKLRegexOptions options) { return(&rkl_cachedRegexes[rkl_regexLookasideCache[rkl_regexLookasideCacheIndexForPointerAndOptions(ptr, options)]]); } -RKL_STATIC_INLINE NSUInteger rkl_makeCacheSetHash ( CFHashCode regexHash, RKLRegexOptions options) { return((NSUInteger)regexHash ^ (NSUInteger)options); } -RKL_STATIC_INLINE NSUInteger rkl_cacheSetForRegexHashAndOptions ( CFHashCode regexHash, RKLRegexOptions options) { return((rkl_makeCacheSetHash(regexHash, options) % _RKL_REGEX_LRU_CACHE_SETS)); } -RKL_STATIC_INLINE NSUInteger rkl_cacheWayForCachedRegex (const RKLCachedRegex *cachedRegex) { return((cachedRegex - rkl_cachedRegexes) % _RKL_LRU_CACHE_SET_WAYS); } -RKL_STATIC_INLINE NSUInteger rkl_cacheSetForCachedRegex (const RKLCachedRegex *cachedRegex) { return(rkl_cacheSetForRegexHashAndOptions(cachedRegex->regexHash, cachedRegex->options)); } -RKL_STATIC_INLINE RKLCachedRegex *rkl_cachedRegexForCacheSetAndWay ( NSUInteger cacheSet, NSUInteger cacheWay) { return(&rkl_cachedRegexes[((cacheSet * _RKL_LRU_CACHE_SET_WAYS) + cacheWay)]); } -RKL_STATIC_INLINE RKLCachedRegex *rkl_cachedRegexForRegexHashAndOptionsAndWay ( CFHashCode regexHash, RKLRegexOptions options, NSUInteger cacheWay) { return(rkl_cachedRegexForCacheSetAndWay(rkl_cacheSetForRegexHashAndOptions(regexHash, options), cacheWay)); } - -RKL_STATIC_INLINE void rkl_updateCachesWithCachedRegex(RKLCachedRegex *cachedRegex, const void *ptr, int hitOrMiss RKL_UNUSED_DTRACE_ARG, int status RKL_UNUSED_DTRACE_ARG) { - rkl_lastCachedRegex = cachedRegex; - rkl_setRegexLookasideCacheToCachedRegexForPointer(cachedRegex, ptr); - rkl_accessCacheSetWay(_RKL_REGEX_LRU_CACHE_SETS, rkl_cachedRegexCacheSets, rkl_cacheSetForCachedRegex(cachedRegex), rkl_cacheWayForCachedRegex(cachedRegex)); // Set the matching line as the most recently used. - rkl_dtrace_compiledRegexCache(&rkl_dtrace_regexUTF8[(cachedRegex - rkl_cachedRegexes)][0], cachedRegex->options, (int)cachedRegex->captureCount, hitOrMiss, status, NULL); -} - -RKL_STATIC_INLINE RKLCachedRegex *rkl_leastRecentlyUsedCachedRegexForRegexHashAndOptions(CFHashCode regexHash, RKLRegexOptions options) { - NSUInteger cacheSet = rkl_cacheSetForRegexHashAndOptions(regexHash, options); - return(rkl_cachedRegexForCacheSetAndWay(cacheSet, rkl_leastRecentlyUsedWayInSet(_RKL_REGEX_LRU_CACHE_SETS, rkl_cachedRegexCacheSets, cacheSet))); -} - -#pragma mark Regular expression lookup function - -// IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. -// IMPORTANT! Should only be called with rkl_cacheSpinLock already locked! -// ---------- - -static RKLCachedRegex *rkl_getCachedRegex(NSString *regexString, RKLRegexOptions options, NSError **error, id *exception) { - // ---------- vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - // IMPORTANT! This section of code is called almost every single time that any RegexKitLite functionality is used! It /MUST/ be very fast! - // ---------- vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - - RKLCachedRegex *cachedRegex = NULL; - CFHashCode regexHash = 0UL; - int32_t status = 0; - - RKLCDelayedAssert((rkl_cacheSpinLock != (OSSpinLock)0) && (regexString != NULL), exception, exitNow); - - // Fast path the common case where this regex is exactly the same one used last time. - // The pointer equality test is valid under these circumstances since the cachedRegex->regexString is an immutable copy. - // If the regexString argument is mutable, this test will fail, and we'll use the the slow path cache check below. - if(RKL_EXPECTED(rkl_lastCachedRegex != NULL, 1L) && RKL_EXPECTED(rkl_lastCachedRegex->regexString == (CFStringRef)regexString, 1L) && RKL_EXPECTED(rkl_lastCachedRegex->options == options, 1L) && RKL_EXPECTED(rkl_lastCachedRegex->icu_regex != NULL, 1L)) { - rkl_dtrace_compiledRegexCache(&rkl_dtrace_regexUTF8[(rkl_lastCachedRegex - rkl_cachedRegexes)][0], rkl_lastCachedRegex->options, (int)rkl_lastCachedRegex->captureCount, 1, 0, NULL); - return(rkl_lastCachedRegex); - } - - rkl_lastCachedRegex = NULL; // Make sure that rkl_lastCachedRegex is NULL in case there is some kind of error. - cachedRegex = rkl_cachedRegexFromRegexLookasideCacheForString(regexString, options); // Check the Regex Lookaside Cache to see if we can quickly find the correct Cached Regex for this regexString pointer + options. - if((RKL_EXPECTED(cachedRegex->regexString == (CFStringRef)regexString, 1L) || (RKL_EXPECTED(cachedRegex->regexString != NULL, 1L) && RKL_EXPECTED(CFEqual((CFTypeRef)regexString, (CFTypeRef)cachedRegex->regexString) == YES, 1L))) && RKL_EXPECTED(cachedRegex->options == options, 1L) && RKL_EXPECTED(cachedRegex->icu_regex != NULL, 1L)) { goto foundMatch; } // There was a Regex Lookaside Cache hit, jump to foundMatch: to quickly return the result. A Regex Lookaside Cache hit allows us to bypass calling CFHash(), which is a decent performance win. - else { cachedRegex = NULL; regexHash = CFHash((CFTypeRef)regexString); } // Regex Lookaside Cache miss. We need to call CFHash() to determine the cache set for this regex. - - NSInteger cacheWay = 0L; // Check each way of the set that this regex belongs to. - for(cacheWay = ((NSInteger)_RKL_LRU_CACHE_SET_WAYS - 1L); cacheWay > 0L; cacheWay--) { // Checking the ways in reverse (3, 2, 1, 0) finds a match "sooner" on average. - cachedRegex = rkl_cachedRegexForRegexHashAndOptionsAndWay(regexHash, options, (NSUInteger)cacheWay); - // Return the cached entry if it's a match. If regexString is mutable, the pointer equality test will fail, and CFEqual() is used to determine true equality with the immutable cachedRegex copy. CFEqual() performs a slow character by character check. - if(RKL_EXPECTED(cachedRegex->regexHash == regexHash, 0UL) && ((cachedRegex->regexString == (CFStringRef)regexString) || (RKL_EXPECTED(cachedRegex->regexString != NULL, 1L) && RKL_EXPECTED(CFEqual((CFTypeRef)regexString, (CFTypeRef)cachedRegex->regexString) == YES, 1L))) && RKL_EXPECTED(cachedRegex->options == options, 1L) && RKL_EXPECTED(cachedRegex->icu_regex != NULL, 1L)) { - foundMatch: // Control can transfer here (from above) via a Regex Lookaside Cache hit. - rkl_updateCachesWithCachedRegex(cachedRegex, regexString, 1, 0); - return(cachedRegex); - } - } - - // ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - // IMPORTANT! This section of code is called almost every single time that any RegexKitLite functionality is used! It /MUST/ be very fast! - // ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - // Code below this point is not as sensitive to speed since compiling a regular expression is an extremely expensive operation. - // The regex was not found in the cache. Get the cached regex for the least recently used line in the set, then clear the cached regex and create a new ICU regex in its place. - cachedRegex = rkl_leastRecentlyUsedCachedRegexForRegexHashAndOptions(regexHash, options); - rkl_clearCachedRegex(cachedRegex); - - if(RKL_EXPECTED((cachedRegex->regexString = CFStringCreateCopy(NULL, (CFStringRef)regexString)) == NULL, 0L)) { goto exitNow; } ; // Get a cheap immutable copy. - rkl_dtrace_getRegexUTF8(cachedRegex->regexString, &rkl_dtrace_regexUTF8[(cachedRegex - rkl_cachedRegexes)][0]); - cachedRegex->regexHash = regexHash; - cachedRegex->options = options; - - CFIndex regexStringU16Length = CFStringGetLength(cachedRegex->regexString); // In UTF16 code units. - UParseError parseError = (UParseError){-1, -1, {0}, {0}}; - RKL_STRONG_REF const UniChar * RKL_GC_VOLATILE regexUniChar = NULL; - - if(RKL_EXPECTED(regexStringU16Length >= (CFIndex)INT_MAX, 0L)) { *exception = [NSException exceptionWithName:NSRangeException reason:@"Regex string length exceeds INT_MAX" userInfo:NULL]; goto exitNow; } - - // Try to quickly obtain regexString in UTF16 format. - if((regexUniChar = CFStringGetCharactersPtr(cachedRegex->regexString)) == NULL) { // We didn't get the UTF16 pointer quickly and need to perform a full conversion in a temp buffer. - RKL_STRONG_REF UniChar * RKL_GC_VOLATILE uniCharBuffer = NULL; - if(((size_t)regexStringU16Length * sizeof(UniChar)) < (size_t)_RKL_STACK_LIMIT) { if(RKL_EXPECTED((uniCharBuffer = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)alloca( (size_t)regexStringU16Length * sizeof(UniChar) )) == NULL, 0L)) { goto exitNow; } } // Try to use the stack. - else { if(RKL_EXPECTED((uniCharBuffer = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)rkl_realloc(&rkl_scratchBuffer[0], (size_t)regexStringU16Length * sizeof(UniChar), 0UL)) == NULL, 0L)) { goto exitNow; } } // Otherwise use the heap. - CFStringGetCharacters(cachedRegex->regexString, CFMakeRange(0L, regexStringU16Length), uniCharBuffer); // Convert regexString to UTF16. - regexUniChar = uniCharBuffer; - } - - // Create the ICU regex. - if(RKL_EXPECTED((cachedRegex->icu_regex = RKL_ICU_FUNCTION_APPEND(uregex_open)(regexUniChar, (int32_t)regexStringU16Length, options, &parseError, &status)) == NULL, 0L)) { goto exitNow; } - if(RKL_EXPECTED(status <= U_ZERO_ERROR, 1L)) { cachedRegex->captureCount = (NSInteger)RKL_ICU_FUNCTION_APPEND(uregex_groupCount)(cachedRegex->icu_regex, &status); } - if(RKL_EXPECTED(status <= U_ZERO_ERROR, 1L)) { rkl_updateCachesWithCachedRegex(cachedRegex, regexString, 0, status); } - -exitNow: - if(RKL_EXPECTED(rkl_scratchBuffer[0] != NULL, 0L)) { rkl_scratchBuffer[0] = rkl_free(&rkl_scratchBuffer[0]); } - if(RKL_EXPECTED(status > U_ZERO_ERROR, 0L)) { rkl_clearCachedRegex(cachedRegex); cachedRegex = rkl_lastCachedRegex = NULL; if(error != NULL) { *error = rkl_makeNSError((RKLUserInfoOptions)RKLUserInfoNone, regexString, options, &parseError, status, NULL, NSNotFoundRange, NULL, NULL, 0L, (RKLRegexEnumerationOptions)RKLRegexEnumerationNoOptions, @"There was an error compiling the regular expression."); } } - -#ifdef _RKL_DTRACE_ENABLED - if(RKL_EXPECTED(cachedRegex == NULL, 1L)) { char regexUTF8[_RKL_DTRACE_REGEXUTF8_SIZE]; const char *err = NULL; if(status != U_ZERO_ERROR) { err = RKL_ICU_FUNCTION_APPEND(u_errorName)(status); } rkl_dtrace_getRegexUTF8((CFStringRef)regexString, regexUTF8); rkl_dtrace_compiledRegexCache(regexUTF8, options, -1, -1, status, err); } -#endif // _RKL_DTRACE_ENABLED - - return(cachedRegex); -} - -// IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. -// IMPORTANT! Should only be called with rkl_cacheSpinLock already locked! -// ---------- - -#pragma mark Set a cached regular expression to a NSStrings UTF-16 text - -static NSUInteger rkl_setCachedRegexToString(RKLCachedRegex *cachedRegex, const NSRange *range, int32_t *status, id *exception RKL_UNUSED_ASSERTION_ARG) { - // ---------- vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - // IMPORTANT! This section of code is called almost every single time that any RegexKitLite functionality is used! It /MUST/ be very fast! - // ---------- vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - - RKLCDelayedAssert((cachedRegex != NULL) && (cachedRegex->setToString != NULL) && ((range != NULL) && (NSEqualRanges(*range, NSNotFoundRange) == NO)) && (status != NULL), exception, exitNow); - RKL_STRONG_REF const UniChar * RKL_GC_VOLATILE stringUniChar = NULL; -#ifdef _RKL_DTRACE_ENABLED - unsigned int lookupResultFlags = 0U; -#endif - - NSUInteger useFixedBuffer = (cachedRegex->setToLength < (CFIndex)_RKL_FIXED_LENGTH) ? 1UL : 0UL; - RKLBuffer *buffer = NULL; - - if(cachedRegex->setToNeedsConversion == 0U) { - RKLCDelayedAssert((cachedRegex->setToUniChar != NULL) && (cachedRegex->buffer == NULL), exception, exitNow); - if(RKL_EXPECTED((stringUniChar = (RKL_STRONG_REF const UniChar * RKL_GC_VOLATILE)CFStringGetCharactersPtr(cachedRegex->setToString)) == NULL, 0L)) { cachedRegex->setToUniChar = NULL; cachedRegex->setToRange = NSNotFoundRange; cachedRegex->setToNeedsConversion = 1U; } - else { if(RKL_EXPECTED(cachedRegex->setToUniChar != stringUniChar, 0L)) { cachedRegex->setToRange = NSNotFoundRange; cachedRegex->setToUniChar = stringUniChar; } goto setRegexText; } - } - - buffer = cachedRegex->buffer; - - RKLCDelayedAssert((buffer == NULL) ? 1 : (((buffer == &rkl_lruFixedBuffer[0]) || (buffer == &rkl_lruFixedBuffer[1]) || (buffer == &rkl_lruFixedBuffer[2]) || (buffer == &rkl_lruFixedBuffer[3])) || - ((buffer == &rkl_lruDynamicBuffer[0]) || (buffer == &rkl_lruDynamicBuffer[1]) || (buffer == &rkl_lruDynamicBuffer[2]) || (buffer == &rkl_lruDynamicBuffer[3]))), exception, exitNow); - - if((buffer != NULL) && RKL_EXPECTED(cachedRegex->setToString == buffer->string, 1L) && RKL_EXPECTED(cachedRegex->setToHash == buffer->hash, 1L) && RKL_EXPECTED(cachedRegex->setToLength == buffer->length, 1L)) { - RKLCDelayedAssert((buffer->uniChar != NULL), exception, exitNow); - rkl_dtrace_addLookupFlag(lookupResultFlags, RKLCacheHitLookupFlag | RKLConversionRequiredLookupFlag | (useFixedBuffer ? 0U : RKLDynamicBufferLookupFlag)); - if(cachedRegex->setToUniChar != buffer->uniChar) { cachedRegex->setToRange = NSNotFoundRange; cachedRegex->setToUniChar = buffer->uniChar; } - goto setRegexText; - } - - buffer = NULL; - cachedRegex->buffer = NULL; - - NSInteger cacheWay = 0L; - for(cacheWay = ((NSInteger)_RKL_LRU_CACHE_SET_WAYS - 1L); cacheWay > 0L; cacheWay--) { - if(useFixedBuffer) { buffer = &rkl_lruFixedBuffer[cacheWay]; } else { buffer = &rkl_lruDynamicBuffer[cacheWay]; } - if(RKL_EXPECTED(cachedRegex->setToString == buffer->string, 1L) && RKL_EXPECTED(cachedRegex->setToHash == buffer->hash, 1L) && RKL_EXPECTED(cachedRegex->setToLength == buffer->length, 1L)) { - RKLCDelayedAssert((buffer->uniChar != NULL), exception, exitNow); - rkl_dtrace_addLookupFlag(lookupResultFlags, RKLCacheHitLookupFlag | RKLConversionRequiredLookupFlag | (useFixedBuffer ? 0U : RKLDynamicBufferLookupFlag)); - if(cachedRegex->setToUniChar != buffer->uniChar) { cachedRegex->setToRange = NSNotFoundRange; cachedRegex->setToUniChar = buffer->uniChar; } - cachedRegex->buffer = buffer; - goto setRegexText; - } - } - - buffer = NULL; - cachedRegex->setToUniChar = NULL; - cachedRegex->setToRange = NSNotFoundRange; - cachedRegex->buffer = NULL; - - RKLCDelayedAssert((cachedRegex->setToNeedsConversion == 1U) && (cachedRegex->buffer == NULL), exception, exitNow); - if(RKL_EXPECTED(cachedRegex->setToNeedsConversion == 1U, 1L) && RKL_EXPECTED((cachedRegex->setToUniChar = (RKL_STRONG_REF const UniChar * RKL_GC_VOLATILE)CFStringGetCharactersPtr(cachedRegex->setToString)) != NULL, 0L)) { cachedRegex->setToNeedsConversion = 0U; cachedRegex->setToRange = NSNotFoundRange; goto setRegexText; } - - rkl_dtrace_addLookupFlag(lookupResultFlags, RKLConversionRequiredLookupFlag | (useFixedBuffer ? 0U : RKLDynamicBufferLookupFlag)); - - if(useFixedBuffer) { buffer = &rkl_lruFixedBuffer [rkl_leastRecentlyUsedWayInSet(1UL, &rkl_lruFixedBufferCacheSet, 0UL)]; } - else { buffer = &rkl_lruDynamicBuffer[rkl_leastRecentlyUsedWayInSet(1UL, &rkl_lruDynamicBufferCacheSet, 0UL)]; } - - RKLCDelayedAssert((useFixedBuffer) ? ((buffer == &rkl_lruFixedBuffer[0]) || (buffer == &rkl_lruFixedBuffer[1]) || (buffer == &rkl_lruFixedBuffer[2]) || (buffer == &rkl_lruFixedBuffer[3])) : - ((buffer == &rkl_lruDynamicBuffer[0]) || (buffer == &rkl_lruDynamicBuffer[1]) || (buffer == &rkl_lruDynamicBuffer[2]) || (buffer == &rkl_lruDynamicBuffer[3])), exception, exitNow); - - rkl_clearBuffer(buffer, 0UL); - - RKLCDelayedAssert((buffer->string == NULL) && (cachedRegex->setToString != NULL), exception, exitNow); - if(RKL_EXPECTED((buffer->string = (CFStringRef)CFRetain((CFTypeRef)cachedRegex->setToString)) == NULL, 0L)) { goto exitNow; } - buffer->hash = cachedRegex->setToHash; - buffer->length = cachedRegex->setToLength; - - if(useFixedBuffer == 0UL) { - RKL_STRONG_REF void * RKL_GC_VOLATILE p = (RKL_STRONG_REF void * RKL_GC_VOLATILE)buffer->uniChar; - if(RKL_EXPECTED((buffer->uniChar = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)rkl_realloc(&p, ((size_t)buffer->length * sizeof(UniChar)), 0UL)) == NULL, 0L)) { goto exitNow; } // Resize the buffer. - } - - RKLCDelayedAssert((buffer->string != NULL) && (buffer->uniChar != NULL), exception, exitNow); - CFStringGetCharacters(buffer->string, CFMakeRange(0L, buffer->length), (UniChar *)buffer->uniChar); // Convert to a UTF16 string. - - cachedRegex->setToUniChar = buffer->uniChar; - cachedRegex->setToRange = NSNotFoundRange; - cachedRegex->buffer = buffer; - -setRegexText: - if(buffer != NULL) { if(useFixedBuffer == 1UL) { rkl_accessCacheSetWay(1UL, &rkl_lruFixedBufferCacheSet, 0UL, (NSUInteger)(buffer - rkl_lruFixedBuffer)); } else { rkl_accessCacheSetWay(1UL, &rkl_lruDynamicBufferCacheSet, 0UL, (NSUInteger)(buffer - rkl_lruDynamicBuffer)); } } - - if(NSEqualRanges(cachedRegex->setToRange, *range) == NO) { - RKLCDelayedAssert((cachedRegex->icu_regex != NULL) && (cachedRegex->setToUniChar != NULL) && (NSMaxRange(*range) <= (NSUInteger)cachedRegex->setToLength) && (cachedRegex->setToRange.length <= INT_MAX), exception, exitNow); - cachedRegex->lastFindRange = cachedRegex->lastMatchRange = NSNotFoundRange; - cachedRegex->setToRange = *range; - RKL_ICU_FUNCTION_APPEND(uregex_setText)(cachedRegex->icu_regex, cachedRegex->setToUniChar + cachedRegex->setToRange.location, (int32_t)cachedRegex->setToRange.length, status); - rkl_dtrace_addLookupFlag(lookupResultFlags, RKLSetTextLookupFlag); - if(RKL_EXPECTED(*status > U_ZERO_ERROR, 0L)) { rkl_dtrace_addLookupFlag(lookupResultFlags, RKLErrorLookupFlag); goto exitNow; } - } - - rkl_dtrace_utf16ConversionCache(lookupResultFlags, cachedRegex->setToString, cachedRegex->setToRange.location, cachedRegex->setToRange.length, cachedRegex->setToLength); - - return(1UL); - - // ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - // IMPORTANT! This section of code is called almost every single time that any RegexKitLite functionality is used! It /MUST/ be very fast! - // ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -exitNow: -#ifdef _RKL_DTRACE_ENABLED - rkl_dtrace_addLookupFlag(lookupResultFlags, RKLErrorLookupFlag); - if(cachedRegex != NULL) { rkl_dtrace_utf16ConversionCache(lookupResultFlags, cachedRegex->setToString, cachedRegex->setToRange.location, cachedRegex->setToRange.length, cachedRegex->setToLength); } -#endif // _RKL_DTRACE_ENABLED - if(cachedRegex != NULL) { cachedRegex->buffer = NULL; cachedRegex->setToRange = NSNotFoundRange; cachedRegex->lastFindRange = NSNotFoundRange; cachedRegex->lastMatchRange = NSNotFoundRange; } - return(0UL); -} - -// IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. -// IMPORTANT! Should only be called with rkl_cacheSpinLock already locked! -// ---------- - -#pragma mark Get a regular expression and set it to a NSStrings UTF-16 text - -static RKLCachedRegex *rkl_getCachedRegexSetToString(NSString *regexString, RKLRegexOptions options, NSString *matchString, NSUInteger *matchLengthPtr, NSRange *matchRange, NSError **error, id *exception, int32_t *status) { - // ---------- vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - // IMPORTANT! This section of code is called almost every single time that any RegexKitLite functionality is used! It /MUST/ be very fast! - // ---------- vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - - RKLCachedRegex *cachedRegex = NULL; - RKLCDelayedAssert((regexString != NULL) && (matchString != NULL) && (exception != NULL) && (status != NULL) && (matchLengthPtr != NULL), exception, exitNow); - - if(RKL_EXPECTED((cachedRegex = rkl_getCachedRegex(regexString, options, error, exception)) == NULL, 0L)) { goto exitNow; } - RKLCDelayedAssert(((cachedRegex >= rkl_cachedRegexes) && ((cachedRegex - rkl_cachedRegexes) < (ssize_t)_RKL_REGEX_CACHE_LINES)) && (cachedRegex != NULL) && (cachedRegex->icu_regex != NULL) && (cachedRegex->regexString != NULL) && (cachedRegex->captureCount >= 0L) && (cachedRegex == rkl_lastCachedRegex), exception, exitNow); - - // Optimize the case where the string to search (matchString) is immutable and the setToString immutable copy is the same string with its reference count incremented. - NSUInteger isSetTo = ((cachedRegex->setToString == (CFStringRef)matchString)) ? 1UL : 0UL; - CFIndex matchLength = ((cachedRegex->setToIsImmutable == 1U) && (isSetTo == 1UL)) ? cachedRegex->setToLength : CFStringGetLength((CFStringRef)matchString); - - *matchLengthPtr = (NSUInteger)matchLength; - if(matchRange->length == NSUIntegerMax) { matchRange->length = (NSUInteger)matchLength; } // For convenience, allow NSUIntegerMax == string length. - - if(RKL_EXPECTED((NSUInteger)matchLength < NSMaxRange(*matchRange), 0L)) { goto exitNow; } // The match range is out of bounds for the string. performRegexOp will catch and report the problem. - - RKLCDelayedAssert((isSetTo == 1UL) ? (cachedRegex->setToString != NULL) : 1, exception, exitNow); - - if(((cachedRegex->setToIsImmutable == 1U) ? isSetTo : (NSUInteger)((isSetTo == 1UL) && (cachedRegex->setToLength == matchLength) && (cachedRegex->setToHash == CFHash((CFTypeRef)matchString)))) == 0UL) { - if(cachedRegex->setToString != NULL) { rkl_clearCachedRegexSetTo(cachedRegex); } - - cachedRegex->setToString = (CFStringRef)CFRetain((CFTypeRef)matchString); - RKLCDelayedAssert(cachedRegex->setToString != NULL, exception, exitNow); - cachedRegex->setToUniChar = CFStringGetCharactersPtr(cachedRegex->setToString); - cachedRegex->setToNeedsConversion = (cachedRegex->setToUniChar == NULL) ? 1U : 0U; - cachedRegex->setToIsImmutable = (rkl_CFStringIsMutable(cachedRegex->setToString) == YES) ? 0U : 1U; // If RKL_FAST_MUTABLE_CHECK is not defined then setToIsImmutable will always be set to '0', or in other words mutable.. - cachedRegex->setToHash = CFHash((CFTypeRef)cachedRegex->setToString); - cachedRegex->setToRange = NSNotFoundRange; - cachedRegex->setToLength = matchLength; - - } - - if(RKL_EXPECTED(rkl_setCachedRegexToString(cachedRegex, matchRange, status, exception) == 0UL, 0L)) { cachedRegex = NULL; if(*exception == NULL) { *exception = (id)RKLCAssertDictionary(@"Failed to set up UTF16 buffer."); } goto exitNow; } - -exitNow: - return(cachedRegex); - // ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - // IMPORTANT! This section of code is called almost every single time that any RegexKitLite functionality is used! It /MUST/ be very fast! - // ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -} - -#pragma mark GCC cleanup __attribute__ functions that ensure the global rkl_cacheSpinLock is properly unlocked -#ifdef RKL_HAVE_CLEANUP - -// rkl_cleanup_cacheSpinLockStatus takes advantage of GCC's 'cleanup' variable attribute. When an 'auto' variable with the 'cleanup' attribute goes out of scope, -// GCC arranges to have the designated function called. In this case, we make sure that if rkl_cacheSpinLock was locked that it was also unlocked. -// If rkl_cacheSpinLock was locked, but the rkl_cacheSpinLockStatus unlocked flag was not set, we force rkl_cacheSpinLock unlocked with a call to OSSpinLockUnlock. -// This is not a panacea for preventing mutex usage errors. Old style ObjC exceptions will bypass the cleanup call, but newer C++ style ObjC exceptions should cause the cleanup function to be called during the stack unwind. - -// We do not depend on this cleanup function being called. It is used only as an extra safety net. It is probably a bug in RegexKitLite if it is ever invoked and forced to take some kind of protective action. - -volatile NSUInteger rkl_debugCacheSpinLockCount = 0UL; - -void rkl_debugCacheSpinLock (void) RKL_ATTRIBUTES(used, noinline, visibility("default")); -static void rkl_cleanup_cacheSpinLockStatus (volatile NSUInteger *rkl_cacheSpinLockStatusPtr) RKL_ATTRIBUTES(used); - -void rkl_debugCacheSpinLock(void) { - rkl_debugCacheSpinLockCount++; // This is here primarily to prevent the optimizer from optimizing away the function. -} - -static void rkl_cleanup_cacheSpinLockStatus(volatile NSUInteger *rkl_cacheSpinLockStatusPtr) { - static NSUInteger didPrintForcedUnlockWarning = 0UL, didPrintNotLockedWarning = 0UL; - NSUInteger rkl_cacheSpinLockStatus = *rkl_cacheSpinLockStatusPtr; - - if(RKL_EXPECTED((rkl_cacheSpinLockStatus & RKLUnlockedCacheSpinLock) == 0UL, 0L) && RKL_EXPECTED((rkl_cacheSpinLockStatus & RKLLockedCacheSpinLock) != 0UL, 1L)) { - if(rkl_cacheSpinLock != (OSSpinLock)0) { - if(didPrintForcedUnlockWarning == 0UL) { didPrintForcedUnlockWarning = 1UL; NSLog(@"[RegexKitLite] Unusual condition detected: Recorded that rkl_cacheSpinLock was locked, but for some reason it was not unlocked. Forcibly unlocking rkl_cacheSpinLock. Set a breakpoint at rkl_debugCacheSpinLock to debug. This warning is only printed once."); } - rkl_debugCacheSpinLock(); // Since this is an unusual condition, offer an attempt to catch it before we unlock. - OSSpinLockUnlock(&rkl_cacheSpinLock); - } else { - if(didPrintNotLockedWarning == 0UL) { didPrintNotLockedWarning = 1UL; NSLog(@"[RegexKitLite] Unusual condition detected: Recorded that rkl_cacheSpinLock was locked, but for some reason it was not unlocked, yet rkl_cacheSpinLock is currently not locked? Set a breakpoint at rkl_debugCacheSpinLock to debug. This warning is only printed once."); } - rkl_debugCacheSpinLock(); - } - } -} - -#endif // RKL_HAVE_CLEANUP - -// rkl_performDictionaryVarArgsOp is a front end to rkl_performRegexOp which converts a ', ...' varargs key/captures list and converts it in to a form that rkl_performRegexOp can use. -// All error checking of arguments is handled by rkl_performRegexOp. - -#pragma mark Front end function that handles varargs and calls rkl_performRegexOp with the marshaled results - -static id rkl_performDictionaryVarArgsOp(id self, SEL _cmd, RKLRegexOp regexOp, NSString *regexString, RKLRegexOptions options, NSInteger capture, id matchString, NSRange *matchRange, NSString *replacementString, NSError **error, void *result, id firstKey, va_list varArgsList) { - id captureKeys[64]; - int captureKeyIndexes[64]; - NSUInteger captureKeysCount = 0UL; - - if(varArgsList != NULL) { - while(captureKeysCount < 62UL) { - id thisCaptureKey = (captureKeysCount == 0) ? firstKey : va_arg(varArgsList, id); - if(RKL_EXPECTED(thisCaptureKey == NULL, 0L)) { break; } - int thisCaptureKeyIndex = va_arg(varArgsList, int); - captureKeys[captureKeysCount] = thisCaptureKey; - captureKeyIndexes[captureKeysCount] = thisCaptureKeyIndex; - captureKeysCount++; - } - } - - return(rkl_performRegexOp(self, _cmd, regexOp, regexString, options, capture, matchString, matchRange, replacementString, error, result, captureKeysCount, captureKeys, captureKeyIndexes)); -} - -// IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. -// ---------- - -#pragma mark Primary internal function that Objective-C methods call to perform regular expression operations - -static id rkl_performRegexOp(id self, SEL _cmd, RKLRegexOp regexOp, NSString *regexString, RKLRegexOptions options, NSInteger capture, id matchString, NSRange *matchRange, NSString *replacementString, NSError **error, void *result, NSUInteger captureKeysCount, id captureKeys[captureKeysCount], const int captureKeyIndexes[captureKeysCount]) { - volatile NSUInteger RKL_CLEANUP(rkl_cleanup_cacheSpinLockStatus) rkl_cacheSpinLockStatus = 0UL; - - NSUInteger replaceMutable = 0UL; - RKLRegexOp maskedRegexOp = (regexOp & RKLMaskOp); - BOOL dictionaryOp = ((maskedRegexOp == RKLDictionaryOfCapturesOp) || (maskedRegexOp == RKLArrayOfDictionariesOfCapturesOp)) ? YES : NO; - - if((error != NULL) && (*error != NULL)) { *error = NULL; } - - if(RKL_EXPECTED(regexString == NULL, 0L)) { RKL_RAISE_EXCEPTION(NSInvalidArgumentException, @"The regular expression argument is NULL."); } - if(RKL_EXPECTED(matchString == NULL, 0L)) { RKL_RAISE_EXCEPTION(NSInternalInconsistencyException, @"The match string argument is NULL."); } - if(RKL_EXPECTED(matchRange == NULL, 0L)) { RKL_RAISE_EXCEPTION(NSInternalInconsistencyException, @"The match range argument is NULL."); } - if((maskedRegexOp == RKLReplaceOp) && RKL_EXPECTED(replacementString == NULL, 0L)) { RKL_RAISE_EXCEPTION(NSInvalidArgumentException, @"The replacement string argument is NULL."); } - if((dictionaryOp == YES) && RKL_EXPECTED(captureKeys == NULL, 0L)) { RKL_RAISE_EXCEPTION(NSInvalidArgumentException, @"The keys argument is NULL."); } - if((dictionaryOp == YES) && RKL_EXPECTED(captureKeyIndexes == NULL, 0L)) { RKL_RAISE_EXCEPTION(NSInvalidArgumentException, @"The captures argument is NULL."); } - - id resultObject = NULL, exception = NULL; - int32_t status = U_ZERO_ERROR; - RKLCachedRegex *cachedRegex = NULL; - NSUInteger stringU16Length = 0UL, tmpIdx = 0UL; - NSRange stackRanges[2048]; - RKLFindAll findAll; - - // IMPORTANT! Once we have obtained the lock, code MUST exit via 'goto exitNow;' to unlock the lock! NO EXCEPTIONS! - // ---------- - OSSpinLockLock(&rkl_cacheSpinLock); // Grab the lock and get cache entry. - rkl_cacheSpinLockStatus |= RKLLockedCacheSpinLock; - rkl_dtrace_incrementEventID(); - - if(RKL_EXPECTED((cachedRegex = rkl_getCachedRegexSetToString(regexString, options, matchString, &stringU16Length, matchRange, error, &exception, &status)) == NULL, 0L)) { stringU16Length = (NSUInteger)CFStringGetLength((CFStringRef)matchString); } - if(RKL_EXPECTED(matchRange->length == NSUIntegerMax, 0L)) { matchRange->length = stringU16Length; } // For convenience. - if(RKL_EXPECTED(stringU16Length < NSMaxRange(*matchRange), 0L) && RKL_EXPECTED(exception == NULL, 1L)) { exception = (id)RKL_EXCEPTION(NSRangeException, @"Range or index out of bounds."); goto exitNow; } - if(RKL_EXPECTED(stringU16Length >= (NSUInteger)INT_MAX, 0L) && RKL_EXPECTED(exception == NULL, 1L)) { exception = (id)RKL_EXCEPTION(NSRangeException, @"String length exceeds INT_MAX."); goto exitNow; } - if(((maskedRegexOp == RKLRangeOp) || (maskedRegexOp == RKLArrayOfStringsOp)) && RKL_EXPECTED(cachedRegex != NULL, 1L) && (RKL_EXPECTED(capture < 0L, 0L) || RKL_EXPECTED(capture > cachedRegex->captureCount, 0L)) && RKL_EXPECTED(exception == NULL, 1L)) { exception = (id)RKL_EXCEPTION(NSInvalidArgumentException, @"The capture argument is not valid."); goto exitNow; } - - if((dictionaryOp == YES) && RKL_EXPECTED(cachedRegex != NULL, 1L) && RKL_EXPECTED(exception == NULL, 1L)) { - for(tmpIdx = 0UL; tmpIdx < captureKeysCount; tmpIdx++) { - if(RKL_EXPECTED(captureKeys[tmpIdx] == NULL, 0L)) { exception = (id)RKL_EXCEPTION(NSInvalidArgumentException, @"The capture key (key %lu of %lu) is NULL.", (unsigned long)(tmpIdx + 1UL), (unsigned long)captureKeysCount); break; } - if((RKL_EXPECTED(captureKeyIndexes[tmpIdx] < 0, 0L) || RKL_EXPECTED(captureKeyIndexes[tmpIdx] > cachedRegex->captureCount, 0L))) { exception = (id)RKL_EXCEPTION(NSInvalidArgumentException, @"The capture argument %d (capture %lu of %lu) for key '%@' is not valid.", captureKeyIndexes[tmpIdx], (unsigned long)(tmpIdx + 1UL), (unsigned long)captureKeysCount, captureKeys[tmpIdx]); break; } - } - } - - if(RKL_EXPECTED(cachedRegex == NULL, 0L) || RKL_EXPECTED(status > U_ZERO_ERROR, 0L) || RKL_EXPECTED(exception != NULL, 0L)) { goto exitNow; } - - RKLCDelayedAssert(((cachedRegex >= rkl_cachedRegexes) && ((cachedRegex - rkl_cachedRegexes) < (ssize_t)_RKL_REGEX_CACHE_LINES)) && (cachedRegex != NULL) && (cachedRegex->icu_regex != NULL) && (cachedRegex->regexString != NULL) && (cachedRegex->captureCount >= 0L) && (cachedRegex->setToString != NULL) && (cachedRegex->setToLength >= 0L) && (cachedRegex->setToUniChar != NULL) && ((CFIndex)NSMaxRange(cachedRegex->setToRange) <= cachedRegex->setToLength), &exception, exitNow); - RKLCDelayedAssert((cachedRegex->setToNeedsConversion == 0U) ? ((cachedRegex->setToNeedsConversion == 0U) && (cachedRegex->setToUniChar == CFStringGetCharactersPtr(cachedRegex->setToString))) : ((cachedRegex->buffer != NULL) && (cachedRegex->setToHash == cachedRegex->buffer->hash) && (cachedRegex->setToLength == cachedRegex->buffer->length) && (cachedRegex->setToUniChar == cachedRegex->buffer->uniChar)), &exception, exitNow); - - switch(maskedRegexOp) { - case RKLRangeOp: - if((RKL_EXPECTED(rkl_search(cachedRegex, matchRange, 0UL, &exception, &status) == NO, 0L)) || (RKL_EXPECTED(status > U_ZERO_ERROR, 0L))) { *(NSRange *)result = NSNotFoundRange; goto exitNow; } - if(RKL_EXPECTED(capture == 0L, 1L)) { *(NSRange *)result = cachedRegex->lastMatchRange; } else { if(RKL_EXPECTED(rkl_getRangeForCapture(cachedRegex, &status, (int32_t)capture, (NSRange *)result) > U_ZERO_ERROR, 0L)) { goto exitNow; } } - break; - - case RKLSplitOp: // Fall-thru... - case RKLArrayOfStringsOp: // Fall-thru... - case RKLCapturesArrayOp: // Fall-thru... - case RKLArrayOfCapturesOp: // Fall-thru... - case RKLDictionaryOfCapturesOp: // Fall-thru... - case RKLArrayOfDictionariesOfCapturesOp: - findAll = rkl_makeFindAll(stackRanges, *matchRange, 2048L, (2048UL * sizeof(NSRange)), 0UL, &rkl_scratchBuffer[0], &rkl_scratchBuffer[1], &rkl_scratchBuffer[2], &rkl_scratchBuffer[3], &rkl_scratchBuffer[4], 0L, capture, (((maskedRegexOp == RKLCapturesArrayOp) || (maskedRegexOp == RKLDictionaryOfCapturesOp)) ? 1L : NSIntegerMax)); - - if(RKL_EXPECTED(rkl_findRanges(cachedRegex, regexOp, &findAll, &exception, &status) == NO, 1L)) { - if(RKL_EXPECTED(findAll.found == 0L, 0L)) { resultObject = (maskedRegexOp == RKLDictionaryOfCapturesOp) ? [NSDictionary dictionary] : [NSArray array]; } - else { - if(dictionaryOp == YES) { resultObject = rkl_makeDictionary (cachedRegex, regexOp, &findAll, captureKeysCount, captureKeys, captureKeyIndexes, &exception); } - else { resultObject = rkl_makeArray (cachedRegex, regexOp, &findAll, &exception); } - } - } - - for(tmpIdx = 0UL; tmpIdx < _RKL_SCRATCH_BUFFERS; tmpIdx++) { if(RKL_EXPECTED(rkl_scratchBuffer[tmpIdx] != NULL, 0L)) { rkl_scratchBuffer[tmpIdx] = rkl_free(&rkl_scratchBuffer[tmpIdx]); } } - - break; - - case RKLReplaceOp: resultObject = rkl_replaceString(cachedRegex, matchString, stringU16Length, replacementString, (NSUInteger)CFStringGetLength((CFStringRef)replacementString), (NSInteger *)result, (replaceMutable = (((regexOp & RKLReplaceMutable) != 0) ? 1UL : 0UL)), &exception, &status); break; - - default: exception = RKLCAssertDictionary(@"Unknown regexOp code."); break; - } - -exitNow: - OSSpinLockUnlock(&rkl_cacheSpinLock); - rkl_cacheSpinLockStatus |= RKLUnlockedCacheSpinLock; // Warning about rkl_cacheSpinLockStatus never being read can be safely ignored. - - if(RKL_EXPECTED(status > U_ZERO_ERROR, 0L) && RKL_EXPECTED(exception == NULL, 0L)) { exception = rkl_NSExceptionForRegex(regexString, options, NULL, status); } // If we had a problem, prepare an exception to be thrown. - if(RKL_EXPECTED(exception != NULL, 0L)) { rkl_handleDelayedAssert(self, _cmd, exception); } // If there is an exception, throw it at this point. - // If we're working on a mutable string and there were successful matches/replacements, then we still have work to do. - // This is done outside the cache lock and with the objc replaceCharactersInRange:withString: method because Core Foundation - // does not assert that the string we are attempting to update is actually a mutable string, whereas Foundation ensures - // the object receiving the message is a mutable string and throws an exception if we're attempting to modify an immutable string. - if(RKL_EXPECTED(replaceMutable == 1UL, 0L) && RKL_EXPECTED(*((NSInteger *)result) > 0L, 1L) && RKL_EXPECTED(status == U_ZERO_ERROR, 1L) && RKL_EXPECTED(resultObject != NULL, 1L)) { [matchString replaceCharactersInRange:*matchRange withString:resultObject]; } - // If status < U_ZERO_ERROR, consider it an error, even though status < U_ZERO_ERROR is a 'warning' in ICU nomenclature. - // status > U_ZERO_ERROR are an exception and handled above. - // http://sourceforge.net/tracker/?func=detail&atid=990188&aid=2890810&group_id=204582 - if(RKL_EXPECTED(status < U_ZERO_ERROR, 0L) && RKL_EXPECTED(resultObject == NULL, 0L) && (error != NULL)) { - NSString *replacedString = NULL; - NSInteger replacedCount = 0L; - RKLUserInfoOptions userInfoOptions = RKLUserInfoNone; - if((maskedRegexOp == RKLReplaceOp) && (result != NULL)) { userInfoOptions |= RKLUserInfoReplacedCount; replacedString = resultObject; replacedCount = *((NSInteger *)result); } - if(matchRange != NULL) { userInfoOptions |= RKLUserInfoSubjectRange; } - *error = rkl_makeNSError(userInfoOptions, regexString, options, NULL, status, matchString, (matchRange != NULL) ? *matchRange : NSNotFoundRange, replacementString, replacedString, replacedCount, (RKLRegexEnumerationOptions)RKLRegexEnumerationNoOptions, @"The ICU library returned an unexpected error."); - } - return(resultObject); -} - -static void rkl_handleDelayedAssert(id self, SEL _cmd, id exception) { - if(RKL_EXPECTED(exception != NULL, 1L)) { - if([exception isKindOfClass:[NSException class]]) { [[NSException exceptionWithName:[exception name] reason:rkl_stringFromClassAndMethod(self, _cmd, [exception reason]) userInfo:[exception userInfo]] raise]; } - else { - id functionString = [exception objectForKey:@"function"], fileString = [exception objectForKey:@"file"], descriptionString = [exception objectForKey:@"description"], lineNumber = [exception objectForKey:@"line"]; - RKLCHardAbortAssert((functionString != NULL) && (fileString != NULL) && (descriptionString != NULL) && (lineNumber != NULL)); - [[NSAssertionHandler currentHandler] handleFailureInFunction:functionString file:fileString lineNumber:(NSInteger)[lineNumber longValue] description:descriptionString]; - } - } -} - -// IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. -// IMPORTANT! Should only be called from rkl_performRegexOp() or rkl_findRanges(). -// ---------- - -#pragma mark Primary means of performing a search with a regular expression - -static NSUInteger rkl_search(RKLCachedRegex *cachedRegex, NSRange *searchRange, NSUInteger updateSearchRange, id *exception RKL_UNUSED_ASSERTION_ARG, int32_t *status) { - NSUInteger foundMatch = 0UL; - - if((NSEqualRanges(*searchRange, cachedRegex->lastFindRange) == YES) && ((cachedRegex->lastMatchRange.length > 0UL) || (cachedRegex->lastMatchRange.location == (NSUInteger)NSNotFound))) { foundMatch = ((cachedRegex->lastMatchRange.location == (NSUInteger)NSNotFound) ? 0UL : 1UL);} - else { // Only perform an expensive 'find' operation iff the current find range is different than the last find range. - NSUInteger findLocation = (searchRange->location - cachedRegex->setToRange.location); - RKLCDelayedAssert(((searchRange->location >= cachedRegex->setToRange.location)) && (NSRangeInsideRange(*searchRange, cachedRegex->setToRange) == YES) && (findLocation < INT_MAX) && (findLocation <= cachedRegex->setToRange.length), exception, exitNow); - - RKL_PREFETCH_UNICHAR(cachedRegex->setToUniChar, searchRange->location); // Spool up the CPU caches. - - // Using uregex_findNext can be a slight performance win. - NSUInteger useFindNext = (RKL_EXPECTED(searchRange->location == (NSMaxRange(cachedRegex->lastMatchRange) + ((RKL_EXPECTED(cachedRegex->lastMatchRange.length == 0UL, 0L) && RKL_EXPECTED(cachedRegex->lastMatchRange.location < NSMaxRange(cachedRegex->setToRange), 0L)) ? 1UL : 0UL)), 1L) ? 1UL : 0UL); - - cachedRegex->lastFindRange = *searchRange; - if(RKL_EXPECTED(useFindNext == 0UL, 0L)) { if(RKL_EXPECTED((RKL_ICU_FUNCTION_APPEND(uregex_find) (cachedRegex->icu_regex, (int32_t)findLocation, status) == NO), 0L) || RKL_EXPECTED(*status > U_ZERO_ERROR, 0L)) { goto finishedFind; } } - else { if(RKL_EXPECTED((RKL_ICU_FUNCTION_APPEND(uregex_findNext)(cachedRegex->icu_regex, status) == NO), 0L) || RKL_EXPECTED(*status > U_ZERO_ERROR, 0L)) { goto finishedFind; } } - foundMatch = 1UL; - - if(RKL_EXPECTED(rkl_getRangeForCapture(cachedRegex, status, 0, &cachedRegex->lastMatchRange) > U_ZERO_ERROR, 0L)) { goto finishedFind; } - RKLCDelayedAssert(NSRangeInsideRange(cachedRegex->lastMatchRange, *searchRange) == YES, exception, exitNow); - } - -finishedFind: - if(RKL_EXPECTED(*status > U_ZERO_ERROR, 0L)) { foundMatch = 0UL; cachedRegex->lastFindRange = NSNotFoundRange; } - - if(RKL_EXPECTED(foundMatch == 0UL, 0L)) { cachedRegex->lastFindRange = NSNotFoundRange; cachedRegex->lastMatchRange = NSNotFoundRange; if(RKL_EXPECTED(updateSearchRange == 1UL, 1L)) { *searchRange = NSMakeRange(NSMaxRange(*searchRange), 0UL); } } - else { - RKLCDelayedAssert(NSRangeInsideRange(cachedRegex->lastMatchRange, *searchRange) == YES, exception, exitNow); - if(RKL_EXPECTED(updateSearchRange == 1UL, 1L)) { - NSUInteger nextLocation = (NSMaxRange(cachedRegex->lastMatchRange) + ((RKL_EXPECTED(cachedRegex->lastMatchRange.length == 0UL, 0L) && RKL_EXPECTED(cachedRegex->lastMatchRange.location < NSMaxRange(cachedRegex->setToRange), 1L)) ? 1UL : 0UL)), locationDiff = nextLocation - searchRange->location; - RKLCDelayedAssert((((locationDiff > 0UL) || ((locationDiff == 0UL) && (cachedRegex->lastMatchRange.location == NSMaxRange(cachedRegex->setToRange)))) && (locationDiff <= searchRange->length)), exception, exitNow); - searchRange->location = nextLocation; - searchRange->length -= locationDiff; - } - } - -#ifndef NS_BLOCK_ASSERTIONS -exitNow: -#endif - return(foundMatch); -} - -// IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. -// IMPORTANT! Should only be called from rkl_performRegexOp() or rkl_performEnumerationUsingBlock(). -// ---------- - -#pragma mark Used to perform multiple searches at once and return the NSRange results in bulk - -static BOOL rkl_findRanges(RKLCachedRegex *cachedRegex, RKLRegexOp regexOp, RKLFindAll *findAll, id *exception, int32_t *status) { - BOOL returnWithError = YES; - RKLCDelayedAssert((((cachedRegex != NULL) && (cachedRegex->icu_regex != NULL) && (cachedRegex->setToUniChar != NULL) && (cachedRegex->captureCount >= 0L) && (cachedRegex->setToRange.location != (NSUInteger)NSNotFound)) && (status != NULL) && ((findAll != NULL) && (findAll->found == 0L) && (findAll->addedSplitRanges == 0L) && ((findAll->capacity >= 0L) && (((findAll->capacity > 0L) || (findAll->size > 0UL)) ? ((findAll->ranges != NULL) && (findAll->capacity > 0L) && (findAll->size > 0UL)) : 1)) && ((findAll->capture >= 0L) && (findAll->capture <= cachedRegex->captureCount)))), exception, exitNow); - - NSInteger captureCount = cachedRegex->captureCount, findAllRangeIndexOfLastNonZeroLength = 0L; - NSUInteger lastLocation = findAll->findInRange.location; - RKLRegexOp maskedRegexOp = (regexOp & RKLMaskOp); - NSRange searchRange = findAll->findInRange; - - for(findAll->found = 0L; (findAll->found < findAll->findUpTo) && ((findAll->found < findAll->capacity) || (findAll->found == 0L)); findAll->found++) { - NSInteger loopCapture, shouldBreak = 0L; - - if(RKL_EXPECTED(findAll->found >= ((findAll->capacity - ((captureCount + 2L) * 4L)) - 4L), 0L)) { if(RKL_EXPECTED(rkl_growFindRanges(cachedRegex, lastLocation, findAll, exception) == 0UL, 0L)) { goto exitNow; } } - - RKLCDelayedAssert((searchRange.location != (NSUInteger)NSNotFound) && (NSRangeInsideRange(searchRange, cachedRegex->setToRange) == YES) && (NSRangeInsideRange(findAll->findInRange, cachedRegex->setToRange) == YES), exception, exitNow); - - // This fixes a 'bug' that is also present in ICU's uregex_split(). 'Bug', in this case, means that the results of a split operation can differ from those that perl's split() creates for the same input. - // "I|at|ice I eat rice" split using the regex "\b\s*" demonstrates the problem. ICU bug http://bugs.icu-project.org/trac/ticket/6826 - // ICU : "", "I", "|", "at", "|", "ice", "", "I", "", "eat", "", "rice" <- Results that RegexKitLite used to produce. - // PERL: "I", "|", "at", "|", "ice", "I", "eat", "rice" <- Results that RegexKitLite now produces. - do { if((rkl_search(cachedRegex, &searchRange, 1UL, exception, status) == NO) || (RKL_EXPECTED(*status > U_ZERO_ERROR, 0L))) { shouldBreak = 1L; } findAll->remainingRange = searchRange; } - while(RKL_EXPECTED((cachedRegex->lastMatchRange.location - lastLocation) == 0UL, 0L) && RKL_EXPECTED(cachedRegex->lastMatchRange.length == 0UL, 0L) && (maskedRegexOp == RKLSplitOp) && RKL_EXPECTED(shouldBreak == 0L, 1L)); - if(RKL_EXPECTED(shouldBreak == 1L, 0L)) { break; } - - RKLCDelayedAssert((searchRange.location != (NSUInteger)NSNotFound) && (NSRangeInsideRange(searchRange, cachedRegex->setToRange) == YES) && (NSRangeInsideRange(findAll->findInRange, cachedRegex->setToRange) == YES) && (NSRangeInsideRange(searchRange, findAll->findInRange) == YES), exception, exitNow); - RKLCDelayedAssert((NSRangeInsideRange(cachedRegex->lastFindRange, cachedRegex->setToRange) == YES) && (NSRangeInsideRange(cachedRegex->lastMatchRange, cachedRegex->setToRange) == YES) && (NSRangeInsideRange(cachedRegex->lastMatchRange, findAll->findInRange) == YES), exception, exitNow); - RKLCDelayedAssert((findAll->ranges != NULL) && (findAll->found >= 0L) && (findAll->capacity >= 0L) && ((findAll->found + (captureCount + 3L) + 1L) < (findAll->capacity - 2L)), exception, exitNow); - - NSInteger findAllRangesIndexForCapture0 = findAll->found; - switch(maskedRegexOp) { - case RKLArrayOfStringsOp: - if(findAll->capture == 0L) { findAll->ranges[findAll->found] = cachedRegex->lastMatchRange; } else { if(RKL_EXPECTED(rkl_getRangeForCapture(cachedRegex, status, (int32_t)findAll->capture, &findAll->ranges[findAll->found]) > U_ZERO_ERROR, 0L)) { goto exitNow; } } - break; - - case RKLSplitOp: // Fall-thru... - case RKLCapturesArrayOp: // Fall-thru... - case RKLDictionaryOfCapturesOp: // Fall-thru... - case RKLArrayOfDictionariesOfCapturesOp: // Fall-thru... - case RKLArrayOfCapturesOp: - findAll->ranges[findAll->found] = ((maskedRegexOp == RKLSplitOp) ? NSMakeRange(lastLocation, cachedRegex->lastMatchRange.location - lastLocation) : cachedRegex->lastMatchRange); - - for(loopCapture = 1L; loopCapture <= captureCount; loopCapture++) { - RKLCDelayedAssert((findAll->found >= 0L) && (findAll->found < (findAll->capacity - 2L)) && (loopCapture < INT_MAX), exception, exitNow); - if(RKL_EXPECTED(rkl_getRangeForCapture(cachedRegex, status, (int32_t)loopCapture, &findAll->ranges[++findAll->found]) > U_ZERO_ERROR, 0L)) { goto exitNow; } - } - break; - - default: if(*exception == NULL) { *exception = RKLCAssertDictionary(@"Unknown regexOp."); } goto exitNow; break; - } - - if(findAll->ranges[findAllRangesIndexForCapture0].length > 0UL) { findAllRangeIndexOfLastNonZeroLength = findAll->found + 1UL; } - lastLocation = NSMaxRange(cachedRegex->lastMatchRange); - } - - if(RKL_EXPECTED(*status > U_ZERO_ERROR, 0L)) { goto exitNow; } - - RKLCDelayedAssert((findAll->ranges != NULL) && (findAll->found >= 0L) && (findAll->found < (findAll->capacity - 2L)), exception, exitNow); - if(maskedRegexOp == RKLSplitOp) { - if(lastLocation != NSMaxRange(findAll->findInRange)) { findAll->addedSplitRanges++; findAll->ranges[findAll->found++] = NSMakeRange(lastLocation, NSMaxRange(findAll->findInRange) - lastLocation); findAllRangeIndexOfLastNonZeroLength = findAll->found; } - findAll->found = findAllRangeIndexOfLastNonZeroLength; - } - - RKLCDelayedAssert((findAll->ranges != NULL) && (findAll->found >= 0L) && (findAll->found < (findAll->capacity - 2L)), exception, exitNow); - returnWithError = NO; - -exitNow: - return(returnWithError); -} - -// IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. -// IMPORTANT! Should only be called from rkl_findRanges(). -// ---------- - -static NSUInteger rkl_growFindRanges(RKLCachedRegex *cachedRegex, NSUInteger lastLocation, RKLFindAll *findAll, id *exception RKL_UNUSED_ASSERTION_ARG) { - NSUInteger didGrowRanges = 0UL; - RKLCDelayedAssert((((cachedRegex != NULL) && (cachedRegex->captureCount >= 0L)) && ((findAll != NULL) && (findAll->capacity >= 0L) && (findAll->rangesScratchBuffer != NULL) && (findAll->found >= 0L) && (((findAll->capacity > 0L) || (findAll->size > 0UL) || (findAll->ranges != NULL)) ? ((findAll->capacity > 0L) && (findAll->size > 0UL) && (findAll->ranges != NULL) && (((size_t)findAll->capacity * sizeof(NSRange)) == findAll->size)) : 1))), exception, exitNow); - - // Attempt to guesstimate the required capacity based on: the total length needed to search / (length we've searched so far / ranges found so far). - NSInteger newCapacity = (findAll->capacity + (findAll->capacity / 2L)), estimate = (NSInteger)((float)cachedRegex->setToLength / (((float)lastLocation + 1.0f) / ((float)findAll->found + 1.0f))); - newCapacity = (((newCapacity + ((estimate > newCapacity) ? estimate : newCapacity)) / 2L) + ((cachedRegex->captureCount + 2L) * 4L) + 4L); - - NSUInteger needToCopy = ((*findAll->rangesScratchBuffer != findAll->ranges) && (findAll->ranges != NULL)) ? 1UL : 0UL; // If findAll->ranges is set to a stack allocation then we need to manually copy the data from the stack to the new heap allocation. - size_t newSize = ((size_t)newCapacity * sizeof(NSRange)); - RKL_STRONG_REF NSRange * RKL_GC_VOLATILE newRanges = NULL; - - if(RKL_EXPECTED((newRanges = (RKL_STRONG_REF NSRange * RKL_GC_VOLATILE)rkl_realloc((RKL_STRONG_REF void ** RKL_GC_VOLATILE)findAll->rangesScratchBuffer, newSize, 0UL)) == NULL, 0L)) { findAll->capacity = 0L; findAll->size = 0UL; findAll->ranges = NULL; *findAll->rangesScratchBuffer = rkl_free((RKL_STRONG_REF void ** RKL_GC_VOLATILE)findAll->rangesScratchBuffer); goto exitNow; } else { didGrowRanges = 1UL; } - if(needToCopy == 1UL) { memcpy(newRanges, findAll->ranges, findAll->size); } // If necessary, copy the existing data to the new heap allocation. - - findAll->capacity = newCapacity; - findAll->size = newSize; - findAll->ranges = newRanges; - -exitNow: - return(didGrowRanges); -} - -// IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. -// IMPORTANT! Should only be called from rkl_performRegexOp(). -// ---------- - -#pragma mark Convert bulk results from rkl_findRanges in to various NSArray types - -static NSArray *rkl_makeArray(RKLCachedRegex *cachedRegex, RKLRegexOp regexOp, RKLFindAll *findAll, id *exception RKL_UNUSED_ASSERTION_ARG) { - NSUInteger createdStringsCount = 0UL, createdArraysCount = 0UL, transferredStringsCount = 0UL; - id * RKL_GC_VOLATILE matchedStrings = NULL, * RKL_GC_VOLATILE subcaptureArrays = NULL, emptyString = @""; - NSArray * RKL_GC_VOLATILE resultArray = NULL; - - RKLCDelayedAssert((cachedRegex != NULL) && ((findAll != NULL) && (findAll->found >= 0L) && (findAll->stringsScratchBuffer != NULL) && (findAll->arraysScratchBuffer != NULL)), exception, exitNow); - - size_t matchedStringsSize = ((size_t)findAll->found * sizeof(id)); - CFStringRef setToString = cachedRegex->setToString; - - if((findAll->stackUsed + matchedStringsSize) < (size_t)_RKL_STACK_LIMIT) { if(RKL_EXPECTED((matchedStrings = (id * RKL_GC_VOLATILE)alloca(matchedStringsSize)) == NULL, 0L)) { goto exitNow; } findAll->stackUsed += matchedStringsSize; } - else { if(RKL_EXPECTED((matchedStrings = (id * RKL_GC_VOLATILE)rkl_realloc(findAll->stringsScratchBuffer, matchedStringsSize, (NSUInteger)RKLScannedOption)) == NULL, 0L)) { goto exitNow; } } - - { // This sub-block (and its local variables) is here for the benefit of the optimizer. - NSUInteger found = (NSUInteger)findAll->found; - const NSRange *rangePtr = findAll->ranges; - id *matchedStringsPtr = matchedStrings; - - for(createdStringsCount = 0UL; createdStringsCount < found; createdStringsCount++) { - NSRange range = *rangePtr++; - if(RKL_EXPECTED(((*matchedStringsPtr++ = RKL_EXPECTED(range.length == 0UL, 0L) ? emptyString : rkl_CreateStringWithSubstring((id)setToString, range)) == NULL), 0L)) { goto exitNow; } - } - } - - NSUInteger arrayCount = createdStringsCount; - id * RKL_GC_VOLATILE arrayObjects = matchedStrings; - - if((regexOp & RKLSubcapturesArray) != 0UL) { - RKLCDelayedAssert(((createdStringsCount % ((NSUInteger)cachedRegex->captureCount + 1UL)) == 0UL) && (createdArraysCount == 0UL), exception, exitNow); - - NSUInteger captureCount = ((NSUInteger)cachedRegex->captureCount + 1UL); - NSUInteger subcaptureArraysCount = (createdStringsCount / captureCount); - size_t subcaptureArraysSize = ((size_t)subcaptureArraysCount * sizeof(id)); - - if((findAll->stackUsed + subcaptureArraysSize) < (size_t)_RKL_STACK_LIMIT) { if(RKL_EXPECTED((subcaptureArrays = (id * RKL_GC_VOLATILE)alloca(subcaptureArraysSize)) == NULL, 0L)) { goto exitNow; } findAll->stackUsed += subcaptureArraysSize; } - else { if(RKL_EXPECTED((subcaptureArrays = (id * RKL_GC_VOLATILE)rkl_realloc(findAll->arraysScratchBuffer, subcaptureArraysSize, (NSUInteger)RKLScannedOption)) == NULL, 0L)) { goto exitNow; } } - - { // This sub-block (and its local variables) is here for the benefit of the optimizer. - id *subcaptureArraysPtr = subcaptureArrays; - id *matchedStringsPtr = matchedStrings; - - for(createdArraysCount = 0UL; createdArraysCount < subcaptureArraysCount; createdArraysCount++) { - if(RKL_EXPECTED((*subcaptureArraysPtr++ = rkl_CreateArrayWithObjects((void **)matchedStringsPtr, captureCount)) == NULL, 0L)) { goto exitNow; } - matchedStringsPtr += captureCount; - transferredStringsCount += captureCount; - } - } - - RKLCDelayedAssert((transferredStringsCount == createdStringsCount), exception, exitNow); - arrayCount = createdArraysCount; - arrayObjects = subcaptureArrays; - } - - RKLCDelayedAssert((arrayObjects != NULL), exception, exitNow); - resultArray = rkl_CreateAutoreleasedArray((void **)arrayObjects, (NSUInteger)arrayCount); - -exitNow: - if(RKL_EXPECTED(resultArray == NULL, 0L) && (rkl_collectingEnabled() == NO)) { // If we did not create an array then we need to make sure that we release any objects we created. - NSUInteger x; - if(matchedStrings != NULL) { for(x = transferredStringsCount; x < createdStringsCount; x++) { if((matchedStrings[x] != NULL) && (matchedStrings[x] != emptyString)) { matchedStrings[x] = rkl_ReleaseObject(matchedStrings[x]); } } } - if(subcaptureArrays != NULL) { for(x = 0UL; x < createdArraysCount; x++) { if(subcaptureArrays[x] != NULL) { subcaptureArrays[x] = rkl_ReleaseObject(subcaptureArrays[x]); } } } - } - - return(resultArray); -} - -// IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. -// IMPORTANT! Should only be called from rkl_performRegexOp(). -// ---------- - -#pragma mark Convert bulk results from rkl_findRanges in to various NSDictionary types - -static id rkl_makeDictionary(RKLCachedRegex *cachedRegex, RKLRegexOp regexOp, RKLFindAll *findAll, NSUInteger captureKeysCount, id captureKeys[captureKeysCount], const int captureKeyIndexes[captureKeysCount], id *exception RKL_UNUSED_ASSERTION_ARG) { - NSUInteger matchedStringIndex = 0UL, createdStringsCount = 0UL, createdDictionariesCount = 0UL, matchedDictionariesCount = (findAll->found / (cachedRegex->captureCount + 1UL)), transferredDictionariesCount = 0UL; - id * RKL_GC_VOLATILE matchedStrings = NULL, * RKL_GC_VOLATILE matchedKeys = NULL, emptyString = @""; - id RKL_GC_VOLATILE returnObject = NULL; - NSDictionary ** RKL_GC_VOLATILE matchedDictionaries = NULL; - - RKLCDelayedAssert((cachedRegex != NULL) && ((findAll != NULL) && (findAll->found >= 0L) && (findAll->stringsScratchBuffer != NULL) && (findAll->dictionariesScratchBuffer != NULL) && (findAll->keysScratchBuffer != NULL) && (captureKeyIndexes != NULL)), exception, exitNow); - - CFStringRef setToString = cachedRegex->setToString; - - size_t matchedStringsSize = ((size_t)captureKeysCount * sizeof(void *)); - if((findAll->stackUsed + matchedStringsSize) < (size_t)_RKL_STACK_LIMIT) { if(RKL_EXPECTED((matchedStrings = (id * RKL_GC_VOLATILE)alloca(matchedStringsSize)) == NULL, 0L)) { goto exitNow; } findAll->stackUsed += matchedStringsSize; } - else { if(RKL_EXPECTED((matchedStrings = (id * RKL_GC_VOLATILE)rkl_realloc(findAll->stringsScratchBuffer, matchedStringsSize, (NSUInteger)RKLScannedOption)) == NULL, 0L)) { goto exitNow; } } - - size_t matchedKeysSize = ((size_t)captureKeysCount * sizeof(void *)); - if((findAll->stackUsed + matchedKeysSize) < (size_t)_RKL_STACK_LIMIT) { if(RKL_EXPECTED((matchedKeys = (id * RKL_GC_VOLATILE)alloca(matchedKeysSize)) == NULL, 0L)) { goto exitNow; } findAll->stackUsed += matchedKeysSize; } - else { if(RKL_EXPECTED((matchedKeys = (id * RKL_GC_VOLATILE)rkl_realloc(findAll->keysScratchBuffer, matchedKeysSize, (NSUInteger)RKLScannedOption)) == NULL, 0L)) { goto exitNow; } } - - size_t matchedDictionariesSize = ((size_t)matchedDictionariesCount * sizeof(NSDictionary *)); - if((findAll->stackUsed + matchedDictionariesSize) < (size_t)_RKL_STACK_LIMIT) { if(RKL_EXPECTED((matchedDictionaries = (NSDictionary ** RKL_GC_VOLATILE)alloca(matchedDictionariesSize)) == NULL, 0L)) { goto exitNow; } findAll->stackUsed += matchedDictionariesSize; } - else { if(RKL_EXPECTED((matchedDictionaries = (NSDictionary ** RKL_GC_VOLATILE)rkl_realloc(findAll->dictionariesScratchBuffer, matchedDictionariesSize, (NSUInteger)RKLScannedOption)) == NULL, 0L)) { goto exitNow; } } - - { // This sub-block (and its local variables) is here for the benefit of the optimizer. - NSUInteger captureCount = cachedRegex->captureCount; - NSDictionary **matchedDictionariesPtr = matchedDictionaries; - - for(createdDictionariesCount = 0UL; createdDictionariesCount < matchedDictionariesCount; createdDictionariesCount++) { - RKLCDelayedAssert(((createdDictionariesCount * captureCount) < (NSUInteger)findAll->found), exception, exitNow); - RKL_STRONG_REF const NSRange * RKL_GC_VOLATILE rangePtr = &findAll->ranges[(createdDictionariesCount * (captureCount + 1UL))]; - for(matchedStringIndex = 0UL; matchedStringIndex < captureKeysCount; matchedStringIndex++) { - NSRange range = rangePtr[captureKeyIndexes[matchedStringIndex]]; - if(RKL_EXPECTED(range.location != NSNotFound, 0L)) { - if(RKL_EXPECTED(((matchedStrings[createdStringsCount] = RKL_EXPECTED(range.length == 0UL, 0L) ? emptyString : rkl_CreateStringWithSubstring((id)setToString, range)) == NULL), 0L)) { goto exitNow; } - matchedKeys[createdStringsCount] = captureKeys[createdStringsCount]; - createdStringsCount++; - } - } - RKLCDelayedAssert((matchedStringIndex <= captureCount), exception, exitNow); - if(RKL_EXPECTED(((*matchedDictionariesPtr++ = (NSDictionary * RKL_GC_VOLATILE)CFDictionaryCreate(NULL, (const void **)matchedKeys, (const void **)matchedStrings, (CFIndex)createdStringsCount, &rkl_transferOwnershipDictionaryKeyCallBacks, &rkl_transferOwnershipDictionaryValueCallBacks)) == NULL), 0L)) { goto exitNow; } - createdStringsCount = 0UL; - } - } - - if(createdDictionariesCount > 0UL) { - if((regexOp & RKLMaskOp) == RKLArrayOfDictionariesOfCapturesOp) { - RKLCDelayedAssert((matchedDictionaries != NULL) && (createdDictionariesCount > 0UL), exception, exitNow); - if((returnObject = rkl_CreateAutoreleasedArray((void **)matchedDictionaries, createdDictionariesCount)) == NULL) { goto exitNow; } - transferredDictionariesCount = createdDictionariesCount; - } else { - RKLCDelayedAssert((matchedDictionaries != NULL) && (createdDictionariesCount == 1UL), exception, exitNow); - if((returnObject = rkl_CFAutorelease(matchedDictionaries[0])) == NULL) { goto exitNow; } - transferredDictionariesCount = 1UL; - } - } - -exitNow: - RKLCDelayedAssert((createdDictionariesCount <= transferredDictionariesCount) && ((transferredDictionariesCount > 0UL) ? (createdStringsCount == 0UL) : 1), exception, exitNow2); -#ifndef NS_BLOCK_ASSERTIONS -exitNow2: -#endif - - if(rkl_collectingEnabled() == NO) { // Release any objects, if necessary. - NSUInteger x; - if(matchedStrings != NULL) { for(x = 0UL; x < createdStringsCount; x++) { if((matchedStrings[x] != NULL) && (matchedStrings[x] != emptyString)) { matchedStrings[x] = rkl_ReleaseObject(matchedStrings[x]); } } } - if(matchedDictionaries != NULL) { for(x = transferredDictionariesCount; x < createdDictionariesCount; x++) { if((matchedDictionaries[x] != NULL)) { matchedDictionaries[x] = rkl_ReleaseObject(matchedDictionaries[x]); } } } - } - - return(returnObject); -} - -// IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. -// IMPORTANT! Should only be called from rkl_performRegexOp(). -// ---------- - -#pragma mark Perform "search and replace" operations on strings using ICUs uregex_*replace* functions - -static NSString *rkl_replaceString(RKLCachedRegex *cachedRegex, id searchString, NSUInteger searchU16Length, NSString *replacementString, NSUInteger replacementU16Length, NSInteger *replacedCountPtr, NSUInteger replaceMutable, id *exception, int32_t *status) { - RKL_STRONG_REF UniChar * RKL_GC_VOLATILE tempUniCharBuffer = NULL; - RKL_STRONG_REF const UniChar * RKL_GC_VOLATILE replacementUniChar = NULL; - uint64_t searchU16Length64 = (uint64_t)searchU16Length, replacementU16Length64 = (uint64_t)replacementU16Length; - int32_t resultU16Length = 0, tempUniCharBufferU16Capacity = 0, needU16Capacity = 0; - id RKL_GC_VOLATILE resultObject = NULL; - NSInteger replacedCount = -1L; - - if((RKL_EXPECTED(replacementU16Length64 >= (uint64_t)INT_MAX, 0L) || RKL_EXPECTED(((searchU16Length64 / 2ULL) + (replacementU16Length64 * 2ULL)) >= (uint64_t)INT_MAX, 0L))) { *exception = [NSException exceptionWithName:NSRangeException reason:@"Replacement string length exceeds INT_MAX." userInfo:NULL]; goto exitNow; } - - RKLCDelayedAssert((searchU16Length64 < (uint64_t)INT_MAX) && (replacementU16Length64 < (uint64_t)INT_MAX) && (((searchU16Length64 / 2ULL) + (replacementU16Length64 * 2ULL)) < (uint64_t)INT_MAX), exception, exitNow); - - // Zero order approximation of the buffer sizes for holding the replaced string or split strings and split strings pointer offsets. As UTF16 code units. - tempUniCharBufferU16Capacity = (int32_t)(16UL + (searchU16Length + (searchU16Length / 2UL)) + (replacementU16Length * 2UL)); - RKLCDelayedAssert((tempUniCharBufferU16Capacity < INT_MAX) && (tempUniCharBufferU16Capacity > 0), exception, exitNow); - - // Buffer sizes converted from native units to bytes. - size_t stackSize = 0UL, replacementSize = ((size_t)replacementU16Length * sizeof(UniChar)), tempUniCharBufferSize = ((size_t)tempUniCharBufferU16Capacity * sizeof(UniChar)); - - // For the various buffers we require, we first try to allocate from the stack if we're not over the RKL_STACK_LIMIT. If we are, switch to using the heap for the buffer. - if((stackSize + tempUniCharBufferSize) < (size_t)_RKL_STACK_LIMIT) { if(RKL_EXPECTED((tempUniCharBuffer = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)alloca(tempUniCharBufferSize)) == NULL, 0L)) { goto exitNow; } stackSize += tempUniCharBufferSize; } - else { if(RKL_EXPECTED((tempUniCharBuffer = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)rkl_realloc(&rkl_scratchBuffer[0], tempUniCharBufferSize, 0UL)) == NULL, 0L)) { goto exitNow; } } - - // Try to get the pointer to the replacement strings UTF16 data. If we can't, allocate some buffer space, then covert to UTF16. - if((replacementUniChar = CFStringGetCharactersPtr((CFStringRef)replacementString)) == NULL) { - RKL_STRONG_REF UniChar * RKL_GC_VOLATILE uniCharBuffer = NULL; - if((stackSize + replacementSize) < (size_t)_RKL_STACK_LIMIT) { if(RKL_EXPECTED((uniCharBuffer = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)alloca(replacementSize)) == NULL, 0L)) { goto exitNow; } stackSize += replacementSize; } - else { if(RKL_EXPECTED((uniCharBuffer = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)rkl_realloc(&rkl_scratchBuffer[1], replacementSize, 0UL)) == NULL, 0L)) { goto exitNow; } } - CFStringGetCharacters((CFStringRef)replacementString, CFMakeRange(0L, replacementU16Length), uniCharBuffer); // Convert to a UTF16 string. - replacementUniChar = uniCharBuffer; - } - - resultU16Length = rkl_replaceAll(cachedRegex, replacementUniChar, (int32_t)replacementU16Length, tempUniCharBuffer, tempUniCharBufferU16Capacity, &replacedCount, &needU16Capacity, exception, status); - RKLCDelayedAssert((resultU16Length <= tempUniCharBufferU16Capacity) && (needU16Capacity >= resultU16Length) && (needU16Capacity >= 0), exception, exitNow); - if(RKL_EXPECTED((needU16Capacity + 4) >= INT_MAX, 0L)) { *exception = [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Replaced string length exceeds INT_MAX." userInfo:NULL]; goto exitNow; } - - if(RKL_EXPECTED(*status == U_BUFFER_OVERFLOW_ERROR, 0L)) { // Our buffer guess(es) were too small. Resize the buffers and try again. - // rkl_replaceAll will turn a status of U_STRING_NOT_TERMINATED_WARNING in to a U_BUFFER_OVERFLOW_ERROR. - // As an extra precaution, we pad out the amount needed by an extra four characters "just in case". - // http://lists.apple.com/archives/Cocoa-dev/2010/Jan/msg01011.html - needU16Capacity += 4; - tempUniCharBufferSize = ((size_t)(tempUniCharBufferU16Capacity = needU16Capacity + 4) * sizeof(UniChar)); // Use needU16Capacity. Bug 2890810. - if((stackSize + tempUniCharBufferSize) < (size_t)_RKL_STACK_LIMIT) { if(RKL_EXPECTED((tempUniCharBuffer = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)alloca(tempUniCharBufferSize)) == NULL, 0L)) { goto exitNow; } stackSize += tempUniCharBufferSize; } // Warning about stackSize can be safely ignored. - else { if(RKL_EXPECTED((tempUniCharBuffer = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)rkl_realloc(&rkl_scratchBuffer[0], tempUniCharBufferSize, 0UL)) == NULL, 0L)) { goto exitNow; } } - - *status = U_ZERO_ERROR; // Make sure the status var is cleared and try again. - resultU16Length = rkl_replaceAll(cachedRegex, replacementUniChar, (int32_t)replacementU16Length, tempUniCharBuffer, tempUniCharBufferU16Capacity, &replacedCount, &needU16Capacity, exception, status); - RKLCDelayedAssert((resultU16Length <= tempUniCharBufferU16Capacity) && (needU16Capacity >= resultU16Length) && (needU16Capacity >= 0), exception, exitNow); - } - - // If status != U_ZERO_ERROR, consider it an error, even though status < U_ZERO_ERROR is a 'warning' in ICU nomenclature. - // http://sourceforge.net/tracker/?func=detail&atid=990188&aid=2890810&group_id=204582 - if(RKL_EXPECTED(*status != U_ZERO_ERROR, 0L)) { goto exitNow; } // Something went wrong. - - RKLCDelayedAssert((replacedCount >= 0L), exception, exitNow); - if(RKL_EXPECTED(resultU16Length == 0, 0L)) { resultObject = @""; } // Optimize the case where the replaced text length == 0 with a @"" string. - else if(RKL_EXPECTED((NSUInteger)resultU16Length == searchU16Length, 0L) && RKL_EXPECTED(replacedCount == 0L, 1L)) { // Optimize the case where the replacement == original by creating a copy. Very fast if self is immutable. - if(replaceMutable == 0UL) { resultObject = rkl_CFAutorelease(CFStringCreateCopy(NULL, (CFStringRef)searchString)); } // .. but only if this is not replacing a mutable self. Warning about potential leak can be safely ignored. - } else { resultObject = rkl_CFAutorelease(CFStringCreateWithCharacters(NULL, tempUniCharBuffer, (CFIndex)resultU16Length)); } // otherwise, create a new string. Warning about potential leak can be safely ignored. - - // If replaceMutable == 1UL, we don't do the replacement here. We wait until after we return and unlock the cache lock. - // This is because we may be trying to mutate an immutable string object. - if((replaceMutable == 1UL) && RKL_EXPECTED(replacedCount > 0L, 1L)) { // We're working on a mutable string and there were successful matches with replaced text, so there's work to do. - if(cachedRegex->buffer != NULL) { rkl_clearBuffer(cachedRegex->buffer, 0UL); cachedRegex->buffer = NULL; } - NSUInteger idx = 0UL; - for(idx = 0UL; idx < _RKL_LRU_CACHE_SET_WAYS; idx++) { - RKLBuffer *buffer = ((NSUInteger)cachedRegex->setToLength < _RKL_FIXED_LENGTH) ? &rkl_lruFixedBuffer[idx] : &rkl_lruDynamicBuffer[idx]; - if(RKL_EXPECTED(cachedRegex->setToString == buffer->string, 0L) && (cachedRegex->setToLength == buffer->length) && (cachedRegex->setToHash == buffer->hash)) { rkl_clearBuffer(buffer, 0UL); } - } - rkl_clearCachedRegexSetTo(cachedRegex); // Flush any cached information about this string since it will mutate. - } - -exitNow: - if(RKL_EXPECTED(status == NULL, 0L) || RKL_EXPECTED(*status != U_ZERO_ERROR, 0L) || RKL_EXPECTED(exception == NULL, 0L) || RKL_EXPECTED(*exception != NULL, 0L)) { replacedCount = -1L; } - if(rkl_scratchBuffer[0] != NULL) { rkl_scratchBuffer[0] = rkl_free(&rkl_scratchBuffer[0]); } - if(rkl_scratchBuffer[1] != NULL) { rkl_scratchBuffer[1] = rkl_free(&rkl_scratchBuffer[1]); } - if(replacedCountPtr != NULL) { *replacedCountPtr = replacedCount; } - return(resultObject); -} // The two warnings about potential leaks can be safely ignored. - -// IMPORTANT! Should only be called from rkl_replaceString(). -// ---------- -// Modified version of the ICU libraries uregex_replaceAll() that keeps count of the number of replacements made. - -static int32_t rkl_replaceAll(RKLCachedRegex *cachedRegex, RKL_STRONG_REF const UniChar * RKL_GC_VOLATILE replacementUniChar, int32_t replacementU16Length, UniChar *replacedUniChar, int32_t replacedU16Capacity, NSInteger *replacedCount, int32_t *needU16Capacity, id *exception RKL_UNUSED_ASSERTION_ARG, int32_t *status) { - int32_t u16Length = 0, initialReplacedU16Capacity = replacedU16Capacity; - NSUInteger bufferOverflowed = 0UL; - NSInteger replaced = -1L; - RKLCDelayedAssert((cachedRegex != NULL) && (replacementUniChar != NULL) && (replacedUniChar != NULL) && (replacedCount != NULL) && (needU16Capacity != NULL) && (status != NULL) && (replacementU16Length >= 0) && (replacedU16Capacity >= 0), exception, exitNow); - - cachedRegex->lastFindRange = cachedRegex->lastMatchRange = NSNotFoundRange; // Clear the cached find information for this regex so a subsequent find works correctly. - RKL_ICU_FUNCTION_APPEND(uregex_reset)(cachedRegex->icu_regex, 0, status); - - // Work around for ICU uregex_reset() bug, see http://bugs.icu-project.org/trac/ticket/6545 - // http://sourceforge.net/tracker/index.php?func=detail&aid=2105213&group_id=204582&atid=990188 - if(RKL_EXPECTED(cachedRegex->setToRange.length == 0UL, 0L) && (*status == U_INDEX_OUTOFBOUNDS_ERROR)) { *status = U_ZERO_ERROR; } - replaced = 0L; - // This loop originally came from ICU source/i18n/uregex.cpp, uregex_replaceAll. - // There is a bug in that code which causes the size of the buffer required for the replaced text to not be calculated correctly. - // This contains a work around using the variable bufferOverflowed. - // ICU bug: http://bugs.icu-project.org/trac/ticket/6656 - // http://sourceforge.net/tracker/index.php?func=detail&aid=2408447&group_id=204582&atid=990188 - while(RKL_ICU_FUNCTION_APPEND(uregex_findNext)(cachedRegex->icu_regex, status)) { - replaced++; - u16Length += RKL_ICU_FUNCTION_APPEND(uregex_appendReplacement)(cachedRegex->icu_regex, replacementUniChar, replacementU16Length, &replacedUniChar, &replacedU16Capacity, status); - if(RKL_EXPECTED(*status == U_BUFFER_OVERFLOW_ERROR, 0L)) { bufferOverflowed = 1UL; *status = U_ZERO_ERROR; } - } - if(RKL_EXPECTED(*status == U_BUFFER_OVERFLOW_ERROR, 0L)) { bufferOverflowed = 1UL; *status = U_ZERO_ERROR; } - if(RKL_EXPECTED(*status <= U_ZERO_ERROR, 1L)) { u16Length += RKL_ICU_FUNCTION_APPEND(uregex_appendTail)(cachedRegex->icu_regex, &replacedUniChar, &replacedU16Capacity, status); } - - // Try to work around a status of U_STRING_NOT_TERMINATED_WARNING. For now, we treat it as a "Buffer Overflow" error. - // As an extra precaution, in rkl_replaceString, we pad out the amount needed by an extra four characters "just in case". - // http://lists.apple.com/archives/Cocoa-dev/2010/Jan/msg01011.html - if(RKL_EXPECTED(*status == U_STRING_NOT_TERMINATED_WARNING, 0L)) { *status = U_BUFFER_OVERFLOW_ERROR; } - - // Check for status <= U_ZERO_ERROR (a 'warning' in ICU nomenclature) rather than just status == U_ZERO_ERROR. - // Under just the right circumstances, status might be equal to U_STRING_NOT_TERMINATED_WARNING. When this occurred, - // rkl_replaceString would never get the U_BUFFER_OVERFLOW_ERROR status, and thus never grow the buffer to the size needed. - // http://sourceforge.net/tracker/?func=detail&atid=990188&aid=2890810&group_id=204582 - if(RKL_EXPECTED(bufferOverflowed == 1UL, 0L) && RKL_EXPECTED(*status <= U_ZERO_ERROR, 1L)) { *status = U_BUFFER_OVERFLOW_ERROR; } - -#ifndef NS_BLOCK_ASSERTIONS -exitNow: -#endif // NS_BLOCK_ASSERTIONS - if(RKL_EXPECTED(replacedCount != NULL, 1L)) { *replacedCount = replaced; } - if(RKL_EXPECTED(needU16Capacity != NULL, 1L)) { *needU16Capacity = u16Length; } // Use needU16Capacity to return the number of characters that are needed for the completely replaced string. Bug 2890810. - return(initialReplacedU16Capacity - replacedU16Capacity); // Return the number of characters of replacedUniChar that were used. -} - -#pragma mark Internal function used to check if a regular expression is valid. - -static NSUInteger rkl_isRegexValid(id self, SEL _cmd, NSString *regex, RKLRegexOptions options, NSInteger *captureCountPtr, NSError **error) { - volatile NSUInteger RKL_CLEANUP(rkl_cleanup_cacheSpinLockStatus) rkl_cacheSpinLockStatus = 0UL; - - RKLCachedRegex *cachedRegex = NULL; - NSUInteger gotCachedRegex = 0UL; - NSInteger captureCount = -1L; - id exception = NULL; - - if((error != NULL) && (*error != NULL)) { *error = NULL; } - if(RKL_EXPECTED(regex == NULL, 0L)) { RKL_RAISE_EXCEPTION(NSInvalidArgumentException, @"The regular expression argument is NULL."); } - - OSSpinLockLock(&rkl_cacheSpinLock); - rkl_cacheSpinLockStatus |= RKLLockedCacheSpinLock; - rkl_dtrace_incrementEventID(); - if(RKL_EXPECTED((cachedRegex = rkl_getCachedRegex(regex, options, error, &exception)) != NULL, 1L)) { gotCachedRegex = 1UL; captureCount = cachedRegex->captureCount; } - cachedRegex = NULL; - OSSpinLockUnlock(&rkl_cacheSpinLock); - rkl_cacheSpinLockStatus |= RKLUnlockedCacheSpinLock; // Warning about rkl_cacheSpinLockStatus never being read can be safely ignored. - - if(captureCountPtr != NULL) { *captureCountPtr = captureCount; } - if(RKL_EXPECTED(exception != NULL, 0L)) { rkl_handleDelayedAssert(self, _cmd, exception); } - return(gotCachedRegex); -} - -#pragma mark Functions used for clearing and releasing resources for various internal data structures - -static void rkl_clearStringCache(void) { - RKLCAbortAssert(rkl_cacheSpinLock != (OSSpinLock)0); - rkl_lastCachedRegex = NULL; - NSUInteger x = 0UL; - for(x = 0UL; x < _RKL_SCRATCH_BUFFERS; x++) { if(rkl_scratchBuffer[x] != NULL) { rkl_scratchBuffer[x] = rkl_free(&rkl_scratchBuffer[x]); } } - for(x = 0UL; x < _RKL_REGEX_CACHE_LINES; x++) { rkl_clearCachedRegex(&rkl_cachedRegexes[x]); } - for(x = 0UL; x < _RKL_LRU_CACHE_SET_WAYS; x++) { rkl_clearBuffer(&rkl_lruFixedBuffer[x], 0UL); rkl_clearBuffer(&rkl_lruDynamicBuffer[x], 1UL); } -} - -static void rkl_clearBuffer(RKLBuffer *buffer, NSUInteger freeDynamicBuffer) { - RKLCAbortAssert(buffer != NULL); - if(RKL_EXPECTED(buffer == NULL, 0L)) { return; } - if(RKL_EXPECTED(freeDynamicBuffer == 1UL, 0L) && RKL_EXPECTED(buffer->uniChar != NULL, 1L)) { RKL_STRONG_REF void * RKL_GC_VOLATILE p = (RKL_STRONG_REF void * RKL_GC_VOLATILE)buffer->uniChar; buffer->uniChar = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)rkl_free(&p); } - if(RKL_EXPECTED(buffer->string != NULL, 1L)) { CFRelease((CFTypeRef)buffer->string); buffer->string = NULL; } - buffer->length = 0L; - buffer->hash = 0UL; -} - -static void rkl_clearCachedRegex(RKLCachedRegex *cachedRegex) { - RKLCAbortAssert(cachedRegex != NULL); - if(RKL_EXPECTED(cachedRegex == NULL, 0L)) { return; } - rkl_clearCachedRegexSetTo(cachedRegex); - if(rkl_lastCachedRegex == cachedRegex) { rkl_lastCachedRegex = NULL; } - if(cachedRegex->icu_regex != NULL) { RKL_ICU_FUNCTION_APPEND(uregex_close)(cachedRegex->icu_regex); cachedRegex->icu_regex = NULL; cachedRegex->captureCount = -1L; } - if(cachedRegex->regexString != NULL) { CFRelease((CFTypeRef)cachedRegex->regexString); cachedRegex->regexString = NULL; cachedRegex->options = 0U; cachedRegex->regexHash = 0UL; } -} - -static void rkl_clearCachedRegexSetTo(RKLCachedRegex *cachedRegex) { - RKLCAbortAssert(cachedRegex != NULL); - if(RKL_EXPECTED(cachedRegex == NULL, 0L)) { return; } - if(RKL_EXPECTED(cachedRegex->icu_regex != NULL, 1L)) { int32_t status = 0; RKL_ICU_FUNCTION_APPEND(uregex_setText)(cachedRegex->icu_regex, &rkl_emptyUniCharString[0], 0, &status); } - if(RKL_EXPECTED(cachedRegex->setToString != NULL, 1L)) { CFRelease((CFTypeRef)cachedRegex->setToString); cachedRegex->setToString = NULL; } - cachedRegex->lastFindRange = cachedRegex->lastMatchRange = cachedRegex->setToRange = NSNotFoundRange; - cachedRegex->setToIsImmutable = cachedRegex->setToNeedsConversion = 0U; - cachedRegex->setToUniChar = NULL; - cachedRegex->setToHash = 0UL; - cachedRegex->setToLength = 0L; - cachedRegex->buffer = NULL; -} - -#pragma mark Internal functions used to implement NSException and NSError functionality and userInfo NSDictionaries - -// Helps to keep things tidy. -#define addKeyAndObject(objs, keys, i, k, o) ({id _o=(o), _k=(k); if((_o != NULL) && (_k != NULL)) { objs[i] = _o; keys[i] = _k; i++; } }) - -static NSDictionary *rkl_userInfoDictionary(RKLUserInfoOptions userInfoOptions, NSString *regexString, RKLRegexOptions options, const UParseError *parseError, int32_t status, NSString *matchString, NSRange matchRange, NSString *replacementString, NSString *replacedString, NSInteger replacedCount, RKLRegexEnumerationOptions enumerationOptions, ...) { - va_list varArgsList; - va_start(varArgsList, enumerationOptions); - if(regexString == NULL) { regexString = @""; } - - id objects[64], keys[64]; - NSUInteger count = 0UL; - - NSString * RKL_GC_VOLATILE errorNameString = [NSString stringWithUTF8String:RKL_ICU_FUNCTION_APPEND(u_errorName)(status)]; - - addKeyAndObject(objects, keys, count, RKLICURegexRegexErrorKey, regexString); - addKeyAndObject(objects, keys, count, RKLICURegexRegexOptionsErrorKey, [NSNumber numberWithUnsignedInt:options]); - addKeyAndObject(objects, keys, count, RKLICURegexErrorCodeErrorKey, [NSNumber numberWithInt:status]); - addKeyAndObject(objects, keys, count, RKLICURegexErrorNameErrorKey, errorNameString); - - if(matchString != NULL) { addKeyAndObject(objects, keys, count, RKLICURegexSubjectStringErrorKey, matchString); } - if((userInfoOptions & RKLUserInfoSubjectRange) != 0UL) { addKeyAndObject(objects, keys, count, RKLICURegexSubjectRangeErrorKey, [NSValue valueWithRange:matchRange]); } - if(replacementString != NULL) { addKeyAndObject(objects, keys, count, RKLICURegexReplacementStringErrorKey, replacementString); } - if(replacedString != NULL) { addKeyAndObject(objects, keys, count, RKLICURegexReplacedStringErrorKey, replacedString); } - if((userInfoOptions & RKLUserInfoReplacedCount) != 0UL) { addKeyAndObject(objects, keys, count, RKLICURegexReplacedCountErrorKey, [NSNumber numberWithInteger:replacedCount]); } - if((userInfoOptions & RKLUserInfoRegexEnumerationOptions) != 0UL) { addKeyAndObject(objects, keys, count, RKLICURegexEnumerationOptionsErrorKey, [NSNumber numberWithUnsignedInteger:enumerationOptions]); } - - if((parseError != NULL) && (parseError->line != -1)) { - NSString *preContextString = [NSString stringWithCharacters:&parseError->preContext[0] length:(NSUInteger)RKL_ICU_FUNCTION_APPEND(u_strlen)(&parseError->preContext[0])]; - NSString *postContextString = [NSString stringWithCharacters:&parseError->postContext[0] length:(NSUInteger)RKL_ICU_FUNCTION_APPEND(u_strlen)(&parseError->postContext[0])]; - - addKeyAndObject(objects, keys, count, RKLICURegexLineErrorKey, [NSNumber numberWithInt:parseError->line]); - addKeyAndObject(objects, keys, count, RKLICURegexOffsetErrorKey, [NSNumber numberWithInt:parseError->offset]); - addKeyAndObject(objects, keys, count, RKLICURegexPreContextErrorKey, preContextString); - addKeyAndObject(objects, keys, count, RKLICURegexPostContextErrorKey, postContextString); - addKeyAndObject(objects, keys, count, @"NSLocalizedFailureReason", ([NSString stringWithFormat:@"The error %@ occurred at line %d, column %d: %@<>%@", errorNameString, parseError->line, parseError->offset, preContextString, postContextString])); - } else { - addKeyAndObject(objects, keys, count, @"NSLocalizedFailureReason", ([NSString stringWithFormat:@"The error %@ occurred.", errorNameString])); - } - - while(count < 62UL) { id obj = va_arg(varArgsList, id), key = va_arg(varArgsList, id); if((obj != NULL) && (key != NULL)) { addKeyAndObject(objects, keys, count, key, obj); } else { break; } } - va_end(varArgsList); - - return([NSDictionary dictionaryWithObjects:&objects[0] forKeys:&keys[0] count:count]); -} - -static NSError *rkl_makeNSError(RKLUserInfoOptions userInfoOptions, NSString *regexString, RKLRegexOptions options, const UParseError *parseError, int32_t status, NSString *matchString, NSRange matchRange, NSString *replacementString, NSString *replacedString, NSInteger replacedCount, RKLRegexEnumerationOptions enumerationOptions, NSString *errorDescription) { - if(errorDescription == NULL) { errorDescription = (status == U_ZERO_ERROR) ? @"No description of this error is available." : [NSString stringWithFormat:@"ICU regular expression error #%d, %s.", status, RKL_ICU_FUNCTION_APPEND(u_errorName)(status)]; } - return([NSError errorWithDomain:RKLICURegexErrorDomain code:(NSInteger)status userInfo:rkl_userInfoDictionary(userInfoOptions, regexString, options, parseError, status, matchString, matchRange, replacementString, replacedString, replacedCount, enumerationOptions, errorDescription, @"NSLocalizedDescription", NULL)]); -} - -static NSException *rkl_NSExceptionForRegex(NSString *regexString, RKLRegexOptions options, const UParseError *parseError, int32_t status) { - return([NSException exceptionWithName:RKLICURegexException reason:[NSString stringWithFormat:@"ICU regular expression error #%d, %s.", status, RKL_ICU_FUNCTION_APPEND(u_errorName)(status)] userInfo:rkl_userInfoDictionary((RKLUserInfoOptions)RKLUserInfoNone, regexString, options, parseError, status, NULL, NSNotFoundRange, NULL, NULL, 0L, (RKLRegexEnumerationOptions)RKLRegexEnumerationNoOptions, NULL)]); -} - -static NSDictionary *rkl_makeAssertDictionary(const char *function, const char *file, int line, NSString *format, ...) { - va_list varArgsList; - va_start(varArgsList, format); - NSString * RKL_GC_VOLATILE formatString = [[[NSString alloc] initWithFormat:format arguments:varArgsList] autorelease]; - va_end(varArgsList); - NSString * RKL_GC_VOLATILE functionString = [NSString stringWithUTF8String:function], *fileString = [NSString stringWithUTF8String:file]; - return([NSDictionary dictionaryWithObjectsAndKeys:formatString, @"description", functionString, @"function", fileString, @"file", [NSNumber numberWithInt:line], @"line", NSInternalInconsistencyException, @"exceptionName", NULL]); -} - -static NSString *rkl_stringFromClassAndMethod(id object, SEL selector, NSString *format, ...) { - va_list varArgsList; - va_start(varArgsList, format); - NSString * RKL_GC_VOLATILE formatString = [[[NSString alloc] initWithFormat:format arguments:varArgsList] autorelease]; - va_end(varArgsList); - Class objectsClass = (object == NULL) ? NULL : [object class]; - return([NSString stringWithFormat:@"*** %c[%@ %@]: %@", (object == objectsClass) ? '+' : '-', (objectsClass == NULL) ? @"" : NSStringFromClass(objectsClass), (selector == NULL) ? @":NULL:" : NSStringFromSelector(selector), formatString]); -} - -#ifdef _RKL_BLOCKS_ENABLED - -//////////// -#pragma mark - -#pragma mark Objective-C ^Blocks Support -#pragma mark - -//////////// - -// Prototypes - -static id rkl_performEnumerationUsingBlock(id self, SEL _cmd, - RKLRegexOp regexOp, NSString *regexString, RKLRegexOptions options, - id matchString, NSRange matchRange, - RKLBlockEnumerationOp blockEnumerationOp, RKLRegexEnumerationOptions enumerationOptions, - NSInteger *replacedCountPtr, NSUInteger *errorFreePtr, - NSError **error, - void (^stringsAndRangesBlock)(NSInteger capturedCount, NSString * const capturedStrings[capturedCount], const NSRange capturedStringRanges[capturedCount], volatile BOOL * const stop), - NSString *(^replaceStringsAndRangesBlock)(NSInteger capturedCount, NSString * const capturedStrings[capturedCount], const NSRange capturedStringRanges[capturedCount], volatile BOOL * const stop) - ) RKL_NONNULL_ARGS(1,2,4,6); - -// This is an object meant for internal use only. It wraps and abstracts various functionality to simplify ^Blocks support. - -@interface RKLBlockEnumerationHelper : NSObject { - @public - RKLCachedRegex cachedRegex; - RKLBuffer buffer; - RKL_STRONG_REF void * RKL_GC_VOLATILE scratchBuffer[_RKL_SCRATCH_BUFFERS]; - NSUInteger needToFreeBufferUniChar:1; -} -- (id)initWithRegex:(NSString *)initRegexString options:(RKLRegexOptions)initOptions string:(NSString *)initString range:(NSRange)initRange error:(NSError **)initError; -@end - -@implementation RKLBlockEnumerationHelper - -- (id)initWithRegex:(NSString *)initRegexString options:(RKLRegexOptions)initOptions string:(NSString *)initString range:(NSRange)initRange error:(NSError **)initError -{ - volatile NSUInteger RKL_CLEANUP(rkl_cleanup_cacheSpinLockStatus) rkl_cacheSpinLockStatus = 0UL; - - int32_t status = U_ZERO_ERROR; - id exception = NULL; - RKLCachedRegex *retrievedCachedRegex = NULL; - -#ifdef _RKL_DTRACE_ENABLED - NSUInteger thisDTraceEventID = 0UL; - unsigned int lookupResultFlags = 0U; -#endif - - if(RKL_EXPECTED((self = [super init]) == NULL, 0L)) { goto errorExit; } - - RKLCDelayedAssert((initRegexString != NULL) && (initString != NULL), &exception, errorExit); - - // IMPORTANT! Once we have obtained the lock, code MUST exit via 'goto exitNow;' to unlock the lock! NO EXCEPTIONS! - // ---------- - OSSpinLockLock(&rkl_cacheSpinLock); // Grab the lock and get cache entry. - rkl_cacheSpinLockStatus |= RKLLockedCacheSpinLock; - rkl_dtrace_incrementAndGetEventID(thisDTraceEventID); - - if(RKL_EXPECTED((retrievedCachedRegex = rkl_getCachedRegex(initRegexString, initOptions, initError, &exception)) == NULL, 0L)) { goto exitNow; } - RKLCDelayedAssert(((retrievedCachedRegex >= rkl_cachedRegexes) && ((retrievedCachedRegex - &rkl_cachedRegexes[0]) < (ssize_t)_RKL_REGEX_CACHE_LINES)) && (retrievedCachedRegex != NULL) && (retrievedCachedRegex->icu_regex != NULL) && (retrievedCachedRegex->regexString != NULL) && (retrievedCachedRegex->captureCount >= 0L) && (retrievedCachedRegex == rkl_lastCachedRegex), &exception, exitNow); - - if(RKL_EXPECTED(retrievedCachedRegex == NULL, 0L) || RKL_EXPECTED(status > U_ZERO_ERROR, 0L) || RKL_EXPECTED(exception != NULL, 0L)) { goto exitNow; } - - if(RKL_EXPECTED((cachedRegex.icu_regex = RKL_ICU_FUNCTION_APPEND(uregex_clone)(retrievedCachedRegex->icu_regex, &status)) == NULL, 0L) || RKL_EXPECTED(status != U_ZERO_ERROR, 0L)) { goto exitNow; } - if(RKL_EXPECTED((cachedRegex.regexString = (CFStringRef)CFRetain((CFTypeRef)retrievedCachedRegex->regexString)) == NULL, 0L)) { goto exitNow; } - cachedRegex.options = initOptions; - cachedRegex.captureCount = retrievedCachedRegex->captureCount; - cachedRegex.regexHash = retrievedCachedRegex->regexHash; - - RKLCDelayedAssert((cachedRegex.icu_regex != NULL) && (cachedRegex.regexString != NULL) && (cachedRegex.captureCount >= 0L), &exception, exitNow); - -exitNow: - if((rkl_cacheSpinLockStatus & RKLLockedCacheSpinLock) != 0UL) { // In case we arrive at exitNow: without obtaining the rkl_cacheSpinLock. - OSSpinLockUnlock(&rkl_cacheSpinLock); - rkl_cacheSpinLockStatus |= RKLUnlockedCacheSpinLock; // Warning about rkl_cacheSpinLockStatus never being read can be safely ignored. - } - - if(RKL_EXPECTED(self == NULL, 0L) || RKL_EXPECTED(retrievedCachedRegex == NULL, 0L) || RKL_EXPECTED(cachedRegex.icu_regex == NULL, 0L) || RKL_EXPECTED(status != U_ZERO_ERROR, 0L) || RKL_EXPECTED(exception != NULL, 0L)) { goto errorExit; } - retrievedCachedRegex = NULL; // Since we no longer hold the lock, ensure that nothing accesses the retrieved cache regex after this point. - - rkl_dtrace_addLookupFlag(lookupResultFlags, RKLEnumerationBufferLookupFlag); - - if(RKL_EXPECTED((buffer.string = CFStringCreateCopy(NULL, (CFStringRef)initString)) == NULL, 0L)) { goto errorExit; } - buffer.hash = CFHash((CFTypeRef)buffer.string); - buffer.length = CFStringGetLength(buffer.string); - - if((buffer.uniChar = (UniChar *)CFStringGetCharactersPtr(buffer.string)) == NULL) { - rkl_dtrace_addLookupFlag(lookupResultFlags, RKLConversionRequiredLookupFlag); - if(RKL_EXPECTED((buffer.uniChar = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)rkl_realloc((RKL_STRONG_REF void ** RKL_GC_VOLATILE)&buffer.uniChar, ((size_t)buffer.length * sizeof(UniChar)), 0UL)) == NULL, 0L)) { goto errorExit; } // Resize the buffer. - needToFreeBufferUniChar = rkl_collectingEnabled() ? 0U : 1U; - CFStringGetCharacters(buffer.string, CFMakeRange(0L, buffer.length), (UniChar *)buffer.uniChar); // Convert to a UTF16 string. - } - - if(RKL_EXPECTED((cachedRegex.setToString = (CFStringRef)CFRetain((CFTypeRef)buffer.string)) == NULL, 0L)) { goto errorExit; } - cachedRegex.setToHash = buffer.hash; - cachedRegex.setToLength = buffer.length; - cachedRegex.setToUniChar = buffer.uniChar; - cachedRegex.buffer = &buffer; - - RKLCDelayedAssert((cachedRegex.icu_regex != NULL) && (cachedRegex.setToUniChar != NULL) && (cachedRegex.setToLength < INT_MAX) && (NSMaxRange(initRange) <= (NSUInteger)cachedRegex.setToLength) && (NSMaxRange(initRange) < INT_MAX), &exception, errorExit); - cachedRegex.lastFindRange = cachedRegex.lastMatchRange = NSNotFoundRange; - cachedRegex.setToRange = initRange; - RKL_ICU_FUNCTION_APPEND(uregex_setText)(cachedRegex.icu_regex, cachedRegex.setToUniChar + cachedRegex.setToRange.location, (int32_t)cachedRegex.setToRange.length, &status); - if(RKL_EXPECTED(status > U_ZERO_ERROR, 0L)) { goto errorExit; } - - rkl_dtrace_addLookupFlag(lookupResultFlags, RKLSetTextLookupFlag); - rkl_dtrace_utf16ConversionCacheWithEventID(thisDTraceEventID, lookupResultFlags, initString, cachedRegex.setToRange.location, cachedRegex.setToRange.length, cachedRegex.setToLength); - - return(self); - -errorExit: - if(RKL_EXPECTED(self != NULL, 1L)) { [self autorelease]; } - if(RKL_EXPECTED(status > U_ZERO_ERROR, 0L) && RKL_EXPECTED(exception == NULL, 0L)) { exception = rkl_NSExceptionForRegex(initRegexString, initOptions, NULL, status); } // If we had a problem, prepare an exception to be thrown. - if(RKL_EXPECTED(status < U_ZERO_ERROR, 0L) && (initError != NULL)) { *initError = rkl_makeNSError((RKLUserInfoOptions)RKLUserInfoNone, initRegexString, initOptions, NULL, status, initString, initRange, NULL, NULL, 0L, (RKLRegexEnumerationOptions)RKLRegexEnumerationNoOptions, @"The ICU library returned an unexpected error."); } - if(RKL_EXPECTED(exception != NULL, 0L)) { rkl_handleDelayedAssert(self, _cmd, exception); } - - return(NULL); -} - -#ifdef __OBJC_GC__ -- (void)finalize -{ - rkl_clearCachedRegex(&cachedRegex); - rkl_clearBuffer(&buffer, (needToFreeBufferUniChar != 0U) ? 1LU : 0LU); - NSUInteger tmpIdx = 0UL; // The rkl_free() below is "probably" a no-op when GC is on, but better to be safe than sorry... - for(tmpIdx = 0UL; tmpIdx < _RKL_SCRATCH_BUFFERS; tmpIdx++) { if(RKL_EXPECTED(scratchBuffer[tmpIdx] != NULL, 0L)) { scratchBuffer[tmpIdx] = rkl_free(&scratchBuffer[tmpIdx]); } } - [super finalize]; -} -#endif // __OBJC_GC__ - -- (void)dealloc -{ - rkl_clearCachedRegex(&cachedRegex); - rkl_clearBuffer(&buffer, (needToFreeBufferUniChar != 0U) ? 1LU : 0LU); - NSUInteger tmpIdx = 0UL; - for(tmpIdx = 0UL; tmpIdx < _RKL_SCRATCH_BUFFERS; tmpIdx++) { if(RKL_EXPECTED(scratchBuffer[tmpIdx] != NULL, 0L)) { scratchBuffer[tmpIdx] = rkl_free(&scratchBuffer[tmpIdx]); } } - [super dealloc]; -} - -@end - -// IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. -// ---------- -// -// Return value: BOOL. Per "Error Handling Programming Guide" wrt/ NSError, return NO on error / failure, and set *error to an NSError object. -// -// rkl_performEnumerationUsingBlock reference counted / manual memory management notes: -// -// When running using reference counting, rkl_performEnumerationUsingBlock() creates a CFMutableArray called autoreleaseArray, which is -autoreleased. -// autoreleaseArray uses the rkl_transferOwnershipArrayCallBacks CFArray callbacks which do not perform a -retain/CFRetain() when objects are added, but do perform a -release/CFRelease() when an object is removed. -// -// A special class, RKLBlockEnumerationHelper, is used to manage the details of creating a private instantiation of the ICU regex (via uregex_clone()) and setting up the details of the UTF-16 buffer required by the ICU regex engine. -// The instantiated RKLBlockEnumerationHelper is not autoreleased, but added to autoreleaseArray. When rkl_performEnumerationUsingBlock() exits, it calls CFArrayRemoveAllValues(autoreleaseArray), which empties the array. -// This has the effect of immediately -releasing the instantiated RKLBlockEnumerationHelper object, and all the memory used to hold the ICU regex and UTF-16 conversion buffer. -// This means the memory is reclaimed immediately and we do not have to wait until the autorelease pool pops. -// -// If we are performing a "string replacement" operation, we create a temporary NSMutableString named mutableReplacementString to hold the replaced strings results. mutableReplacementString is also added to autoreleaseArray so that it -// can be properly released on an error. -// -// Temporary strings that are created during the enumeration of matches are added to autoreleaseArray. -// The strings are added by doing a CFArrayReplaceValues(), which simultaneously releases the previous iterations temporary strings while adding the current iterations temporary strings to the array. -// -// autoreleaseArray always has a reference to any "live" and in use objects. If anything "Goes Wrong", at any point, for any reason (ie, exception is thrown), autoreleaseArray is in the current NSAutoreleasePool -// and will automatically be released when that pool pops. This ensures that we don't leak anything even when things go seriously sideways. This also allows us to keep the total amount of memory in use -// down to a minimum, which can be substantial if the user is enumerating a large string, for example a regex of '\w+' on a 500K+ text file. -// -// The only 'caveat' is that the user needs to -retain any strings that they want to use past the point at which their ^block returns. Logically, it is as if the following takes place: -// -// for(eachMatchOfRegexInStringToSearch) { -// NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; -// callUsersBlock(capturedCount, capturedStrings, capturedStringRanges, stop); -// [pool release]; -// } -// -// But in reality, no NSAutoreleasePool is created, it's all slight of hand done via the CFMutableArray autoreleaseArray. -// -// rkl_performEnumerationUsingBlock garbage collected / automatic memory management notes: -// -// When RegexKitLite is built with -fobjc-gc or -fobjc-gc-only, and (in the case of -fobjc-gc) RegexKitLite determines that GC is active at execution time, then rkl_performEnumerationUsingBlock essentially -// skips all of the above reference counted autoreleaseArray stuff. -// -// rkl_performEnumerationUsingBlock and RKLRegexEnumerationReleaseStringReturnedByReplacementBlock notes -// -// Under reference counting, this enumeration option allows the user to return a non-autoreleased string, and then have RegexKitLite send the object a -release message once it's done with it. -// The primary reason to do this is to immediately reclaim the memory used by the string holding the replacement text. -// Just in case the user returns one of the strings we passed via capturedStrings[], we check to see if the string return by the block is any of the strings we created and passed via capturedStrings[]. -// If it is one of our strings, we do not send the string a -release since that would over release it. It is assumed that the user will /NOT/ add a -retain to our strings in this case. -// Under GC, RKLRegexEnumerationReleaseStringReturnedByReplacementBlock is ignored and no -release messages are sent. -// - -#pragma mark Primary internal function that Objective-C ^Blocks related methods call to perform regular expression operations - -static id rkl_performEnumerationUsingBlock(id self, SEL _cmd, - RKLRegexOp regexOp, NSString *regexString, RKLRegexOptions options, - id matchString, NSRange matchRange, - RKLBlockEnumerationOp blockEnumerationOp, RKLRegexEnumerationOptions enumerationOptions, - NSInteger *replacedCountPtr, NSUInteger *errorFreePtr, - NSError **error, - void (^stringsAndRangesBlock)(NSInteger capturedCount, NSString * const capturedStrings[capturedCount], const NSRange capturedStringRanges[capturedCount], volatile BOOL * const stop), - NSString *(^replaceStringsAndRangesBlock)(NSInteger capturedCount, NSString * const capturedStrings[capturedCount], const NSRange capturedStringRanges[capturedCount], volatile BOOL * const stop)) { - NSMutableArray * RKL_GC_VOLATILE autoreleaseArray = NULL; - RKLBlockEnumerationHelper * RKL_GC_VOLATILE blockEnumerationHelper = NULL; - NSMutableString * RKL_GC_VOLATILE mutableReplacementString = NULL; - RKL_STRONG_REF UniChar * RKL_GC_VOLATILE blockEnumerationHelperUniChar = NULL; - NSUInteger errorFree = NO; - id exception = NULL, returnObject = NULL; - CFRange autoreleaseReplaceRange = CFMakeRange(0L, 0L); - int32_t status = U_ZERO_ERROR; - RKLRegexOp maskedRegexOp = (regexOp & RKLMaskOp); - volatile BOOL shouldStop = NO; - NSInteger replacedCount = -1L; - NSRange lastMatchedRange = NSNotFoundRange; - NSUInteger stringU16Length = 0UL; - - BOOL performStringReplacement = (blockEnumerationOp == RKLBlockEnumerationReplaceOp) ? YES : NO; - - if((error != NULL) && (*error != NULL)) { *error = NULL; } - - if(RKL_EXPECTED(regexString == NULL, 0L)) { exception = (id)RKL_EXCEPTION(NSInvalidArgumentException, @"The regular expression argument is NULL."); goto exitNow; } - if(RKL_EXPECTED(matchString == NULL, 0L)) { exception = (id)RKL_EXCEPTION(NSInternalInconsistencyException, @"The match string argument is NULL."); goto exitNow; } - - if((((enumerationOptions & RKLRegexEnumerationCapturedStringsNotRequired) != 0UL) && ((enumerationOptions & RKLRegexEnumerationFastCapturedStringsXXX) != 0UL)) || - (((enumerationOptions & RKLRegexEnumerationReleaseStringReturnedByReplacementBlock) != 0UL) && (blockEnumerationOp != RKLBlockEnumerationReplaceOp)) || - ((enumerationOptions & (~((RKLRegexEnumerationOptions)(RKLRegexEnumerationCapturedStringsNotRequired | RKLRegexEnumerationReleaseStringReturnedByReplacementBlock | RKLRegexEnumerationFastCapturedStringsXXX)))) != 0UL)) { - exception = (id)RKL_EXCEPTION(NSInvalidArgumentException, @"The RKLRegexEnumerationOptions argument is not valid."); - goto exitNow; - } - - stringU16Length = (NSUInteger)CFStringGetLength((CFStringRef)matchString); - - if(RKL_EXPECTED(matchRange.length == NSUIntegerMax, 1L)) { matchRange.length = stringU16Length; } // For convenience. - if(RKL_EXPECTED(stringU16Length < NSMaxRange(matchRange), 0L)) { exception = (id)RKL_EXCEPTION(NSRangeException, @"Range or index out of bounds."); goto exitNow; } - if(RKL_EXPECTED(stringU16Length >= (NSUInteger)INT_MAX, 0L)) { exception = (id)RKL_EXCEPTION(NSRangeException, @"String length exceeds INT_MAX."); goto exitNow; } - - RKLCDelayedAssert((self != NULL) && (_cmd != NULL) && ((blockEnumerationOp == RKLBlockEnumerationMatchOp) ? (((regexOp == RKLCapturesArrayOp) || (regexOp == RKLSplitOp)) && (stringsAndRangesBlock != NULL) && (replaceStringsAndRangesBlock == NULL)) : 1) && ((blockEnumerationOp == RKLBlockEnumerationReplaceOp) ? ((regexOp == RKLCapturesArrayOp) && (stringsAndRangesBlock == NULL) && (replaceStringsAndRangesBlock != NULL)) : 1) , &exception, exitNow); - - if((rkl_collectingEnabled() == NO) && RKL_EXPECTED((autoreleaseArray = rkl_CFAutorelease(CFArrayCreateMutable(NULL, 0L, &rkl_transferOwnershipArrayCallBacks))) == NULL, 0L)) { goto exitNow; } // Warning about potential leak of Core Foundation object can be safely ignored. - if(RKL_EXPECTED((blockEnumerationHelper = [[RKLBlockEnumerationHelper alloc] initWithRegex:regexString options:options string:matchString range:matchRange error:error]) == NULL, 0L)) { goto exitNow; } // Warning about potential leak of blockEnumerationHelper can be safely ignored. - if(autoreleaseArray != NULL) { CFArrayAppendValue((CFMutableArrayRef)autoreleaseArray, blockEnumerationHelper); autoreleaseReplaceRange.location++; } // We do not autorelease blockEnumerationHelper, but instead add it to autoreleaseArray. - - if(performStringReplacement == YES) { - if(RKL_EXPECTED((mutableReplacementString = [[NSMutableString alloc] init]) == NULL, 0L)) { goto exitNow; } // Warning about potential leak of mutableReplacementString can be safely ignored. - if(autoreleaseArray != NULL) { CFArrayAppendValue((CFMutableArrayRef)autoreleaseArray, mutableReplacementString); autoreleaseReplaceRange.location++; } // We do not autorelease mutableReplacementString, but instead add it to autoreleaseArray. - } - - // RKLBlockEnumerationHelper creates an immutable copy of the string to match (matchString) which we reference via blockEnumerationHelperString. We use blockEnumerationHelperString when creating the captured strings from a match. - // This protects us against the user mutating matchString while we are in the middle of enumerating matches. - NSString * RKL_GC_VOLATILE blockEnumerationHelperString = (NSString *)blockEnumerationHelper->buffer.string, ** RKL_GC_VOLATILE capturedStrings = NULL, *emptyString = @""; - CFMutableStringRef * RKL_GC_VOLATILE fastCapturedStrings = NULL; - NSInteger captureCountBlockArgument = (blockEnumerationHelper->cachedRegex.captureCount + 1L); - size_t capturedStringsCapacity = ((size_t)captureCountBlockArgument + 4UL); - size_t capturedRangesCapacity = (((size_t)captureCountBlockArgument + 4UL) * 5UL); - NSRange *capturedRanges = NULL; - - lastMatchedRange = NSMakeRange(matchRange.location, 0UL); - blockEnumerationHelperUniChar = blockEnumerationHelper->buffer.uniChar; - - RKLCDelayedAssert((blockEnumerationHelperString != NULL) && (blockEnumerationHelperUniChar != NULL) && (captureCountBlockArgument > 0L) && (capturedStringsCapacity > 0UL) && (capturedRangesCapacity > 0UL), &exception, exitNow); - - if((capturedStrings = (NSString ** RKL_GC_VOLATILE)alloca(sizeof(NSString *) * capturedStringsCapacity)) == NULL) { goto exitNow; } // Space to hold the captured strings from a match. - if((capturedRanges = (NSRange *) alloca(sizeof(NSRange) * capturedRangesCapacity)) == NULL) { goto exitNow; } // Space to hold the NSRanges of the captured strings from a match. - -#ifdef NS_BLOCK_ASSERTIONS - { // Initialize the padded capturedStrings and capturedRanges to values that should tickle a fault if they are ever used. - size_t idx = 0UL; - for(idx = captureCountBlockArgument; idx < capturedStringsCapacity; idx++) { capturedStrings[idx] = (NSString *)RKLIllegalPointer; } - for(idx = captureCountBlockArgument; idx < capturedRangesCapacity; idx++) { capturedRanges[idx] = RKLIllegalRange; } - } -#else - { // Initialize all of the capturedStrings and capturedRanges to values that should tickle a fault if they are ever used. - size_t idx = 0UL; - for(idx = 0UL; idx < capturedStringsCapacity; idx++) { capturedStrings[idx] = (NSString *)RKLIllegalPointer; } - for(idx = 0UL; idx < capturedRangesCapacity; idx++) { capturedRanges[idx] = RKLIllegalRange; } - } -#endif - - if((enumerationOptions & RKLRegexEnumerationFastCapturedStringsXXX) != 0UL) { - RKLCDelayedAssert(((enumerationOptions & RKLRegexEnumerationCapturedStringsNotRequired) == 0UL), &exception, exitNow); - size_t idx = 0UL; - if((fastCapturedStrings = (CFMutableStringRef * RKL_GC_VOLATILE)alloca(sizeof(NSString *) * capturedStringsCapacity)) == NULL) { goto exitNow; } // Space to hold the "fast" captured strings from a match. - - for(idx = 0UL; idx < (size_t)captureCountBlockArgument; idx++) { - if((fastCapturedStrings[idx] = CFStringCreateMutableWithExternalCharactersNoCopy(NULL, NULL, 0L, 0L, kCFAllocatorNull)) == NULL) { goto exitNow; } - if(autoreleaseArray != NULL) { CFArrayAppendValue((CFMutableArrayRef)autoreleaseArray, fastCapturedStrings[idx]); autoreleaseReplaceRange.location++; } // We do not autorelease mutableReplacementString, but instead add it to autoreleaseArray. - capturedStrings[idx] = (NSString *)fastCapturedStrings[idx]; - } - } - - RKLFindAll findAll = rkl_makeFindAll(capturedRanges, matchRange, (NSInteger)capturedRangesCapacity, (capturedRangesCapacity * sizeof(NSRange)), 0UL, &blockEnumerationHelper->scratchBuffer[0], &blockEnumerationHelper->scratchBuffer[1], &blockEnumerationHelper->scratchBuffer[2], &blockEnumerationHelper->scratchBuffer[3], &blockEnumerationHelper->scratchBuffer[4], 0L, 0L, 1L); - - NSString ** RKL_GC_VOLATILE capturedStringsBlockArgument = NULL; // capturedStringsBlockArgument is what we pass to the 'capturedStrings[]' argument of the users ^block. Will pass NULL if the user doesn't want the captured strings created automatically. - if((enumerationOptions & RKLRegexEnumerationCapturedStringsNotRequired) == 0UL) { capturedStringsBlockArgument = capturedStrings; } // If the user wants the captured strings automatically created, set to capturedStrings. - - replacedCount = 0L; - while(RKL_EXPECTED(rkl_findRanges(&blockEnumerationHelper->cachedRegex, regexOp, &findAll, &exception, &status) == NO, 1L) && RKL_EXPECTED(findAll.found > 0L, 1L) && RKL_EXPECTED(exception == NULL, 1L) && RKL_EXPECTED(status == U_ZERO_ERROR, 1L)) { - if(performStringReplacement == YES) { - NSUInteger lastMatchedMaxLocation = (lastMatchedRange.location + lastMatchedRange.length); - NSRange previousUnmatchedRange = NSMakeRange(lastMatchedMaxLocation, findAll.ranges[0].location - lastMatchedMaxLocation); - RKLCDelayedAssert((NSMaxRange(previousUnmatchedRange) <= stringU16Length) && (NSRangeInsideRange(previousUnmatchedRange, matchRange) == YES), &exception, exitNow); - if(RKL_EXPECTED(previousUnmatchedRange.length > 0UL, 1L)) { CFStringAppendCharacters((CFMutableStringRef)mutableReplacementString, blockEnumerationHelperUniChar + previousUnmatchedRange.location, (CFIndex)previousUnmatchedRange.length); } - } - - findAll.found -= findAll.addedSplitRanges; - - NSInteger passCaptureCountBlockArgument = ((findAll.found == 0L) && (findAll.addedSplitRanges == 1L) && (maskedRegexOp == RKLSplitOp)) ? 1L : findAll.found, capturedStringsIdx = passCaptureCountBlockArgument; - RKLCDelayedHardAssert(passCaptureCountBlockArgument <= captureCountBlockArgument, &exception, exitNow); - if(capturedStringsBlockArgument != NULL) { // Only create the captured strings if the user has requested them. - BOOL hadError = NO; // Loop over all the strings rkl_findRanges found. If rkl_CreateStringWithSubstring() returns NULL due to an error, set returnBool to NO, and break out of the for() loop. - - for(capturedStringsIdx = 0L; capturedStringsIdx < passCaptureCountBlockArgument; capturedStringsIdx++) { - RKLCDelayedHardAssert(capturedStringsIdx < captureCountBlockArgument, &exception, exitNow); - if((enumerationOptions & RKLRegexEnumerationFastCapturedStringsXXX) != 0UL) { - // Analyzer report of "Dereference of null pointer" can be safely ignored for the next line. Bug filed: http://llvm.org/bugs/show_bug.cgi?id=6150 - CFStringSetExternalCharactersNoCopy(fastCapturedStrings[capturedStringsIdx], &blockEnumerationHelperUniChar[findAll.ranges[capturedStringsIdx].location], (CFIndex)findAll.ranges[capturedStringsIdx].length, (CFIndex)findAll.ranges[capturedStringsIdx].length); - } else { - if((capturedStrings[capturedStringsIdx] = (findAll.ranges[capturedStringsIdx].length == 0UL) ? emptyString : rkl_CreateStringWithSubstring(blockEnumerationHelperString, findAll.ranges[capturedStringsIdx])) == NULL) { hadError = YES; break; } - } - } - if(((enumerationOptions & RKLRegexEnumerationFastCapturedStringsXXX) == 0UL) && RKL_EXPECTED(autoreleaseArray != NULL, 1L)) { CFArrayReplaceValues((CFMutableArrayRef)autoreleaseArray, autoreleaseReplaceRange, (const void **)capturedStrings, capturedStringsIdx); autoreleaseReplaceRange.length = capturedStringsIdx; } // Add to autoreleaseArray all the strings the for() loop created. - if(RKL_EXPECTED(hadError == YES, 0L)) { goto exitNow; } // hadError == YES will be set if rkl_CreateStringWithSubstring() returned NULL. - } - // For safety, set any capturedRanges and capturedStrings up to captureCountBlockArgument + 1 to values that indicate that they are not valid. - // These values are chosen such that they should tickle any misuse by users. - // capturedStringsIdx is initialized to passCaptureCountBlockArgument, but if capturedStringsBlockArgument != NULL, it is reset to 0 by the loop that creates strings. - // If the loop that creates strings has an error, execution should transfer to exitNow and this will never get run. - // Again, this is for safety for users that do not check the passed block argument 'captureCount' and instead depend on something like [regex captureCount]. - for(; capturedStringsIdx < captureCountBlockArgument + 1L; capturedStringsIdx++) { RKLCDelayedAssert((capturedStringsIdx < (NSInteger)capturedStringsCapacity) && (capturedStringsIdx < (NSInteger)capturedRangesCapacity), &exception, exitNow); capturedRanges[capturedStringsIdx] = RKLIllegalRange; capturedStrings[capturedStringsIdx] = (NSString *)RKLIllegalPointer; } - - RKLCDelayedAssert((passCaptureCountBlockArgument > 0L) && (NSMaxRange(capturedRanges[0]) <= stringU16Length) && (capturedRanges[0].location < NSIntegerMax) && (capturedRanges[0].length < NSIntegerMax), &exception, exitNow); - - switch(blockEnumerationOp) { - case RKLBlockEnumerationMatchOp: stringsAndRangesBlock(passCaptureCountBlockArgument, capturedStringsBlockArgument, capturedRanges, &shouldStop); break; - - case RKLBlockEnumerationReplaceOp: { - NSString *blockReturnedReplacementString = replaceStringsAndRangesBlock(passCaptureCountBlockArgument, capturedStringsBlockArgument, capturedRanges, &shouldStop); - - if(RKL_EXPECTED(blockReturnedReplacementString != NULL, 1L)) { - CFStringAppend((CFMutableStringRef)mutableReplacementString, (CFStringRef)blockReturnedReplacementString); - BOOL shouldRelease = (((enumerationOptions & RKLRegexEnumerationReleaseStringReturnedByReplacementBlock) != 0UL) && (capturedStringsBlockArgument != NULL) && (rkl_collectingEnabled() == NO)) ? YES : NO; - if(shouldRelease == YES) { NSInteger idx = 0L; for(idx = 0L; idx < passCaptureCountBlockArgument; idx++) { if(capturedStrings[idx] == blockReturnedReplacementString) { shouldRelease = NO; break; } } } - if(shouldRelease == YES) { [blockReturnedReplacementString release]; } - } - } - break; - - default: exception = RKLCAssertDictionary(@"Unknown blockEnumerationOp code."); goto exitNow; break; - } - - replacedCount++; - findAll.addedSplitRanges = 0L; // rkl_findRanges() expects findAll.addedSplitRanges to be 0 on entry. - findAll.found = 0L; // rkl_findRanges() expects findAll.found to be 0 on entry. - findAll.findInRange = findAll.remainingRange; // Ask rkl_findRanges() to search the part of the string after the current match. - lastMatchedRange = findAll.ranges[0]; - - if(RKL_EXPECTED(shouldStop != NO, 0L)) { break; } - } - errorFree = YES; - -exitNow: - if(RKL_EXPECTED(errorFree == NO, 0L)) { replacedCount = -1L; } - if((blockEnumerationOp == RKLBlockEnumerationReplaceOp) && RKL_EXPECTED(errorFree == YES, 1L)) { - RKLCDelayedAssert(replacedCount >= 0L, &exception, exitNow2); - if(RKL_EXPECTED(replacedCount == 0UL, 0L)) { - RKLCDelayedAssert((blockEnumerationHelper != NULL) && (blockEnumerationHelper->buffer.string != NULL), &exception, exitNow2); - returnObject = rkl_CreateStringWithSubstring((id)blockEnumerationHelper->buffer.string, matchRange); - if(rkl_collectingEnabled() == NO) { returnObject = rkl_CFAutorelease(returnObject); } - } - else { - NSUInteger lastMatchedMaxLocation = (lastMatchedRange.location + lastMatchedRange.length); - NSRange previousUnmatchedRange = NSMakeRange(lastMatchedMaxLocation, NSMaxRange(matchRange) - lastMatchedMaxLocation); - RKLCDelayedAssert((NSMaxRange(previousUnmatchedRange) <= stringU16Length) && (NSRangeInsideRange(previousUnmatchedRange, matchRange) == YES), &exception, exitNow2); - - if(RKL_EXPECTED(previousUnmatchedRange.length > 0UL, 1L)) { CFStringAppendCharacters((CFMutableStringRef)mutableReplacementString, blockEnumerationHelperUniChar + previousUnmatchedRange.location, (CFIndex)previousUnmatchedRange.length); } - returnObject = rkl_CFAutorelease(CFStringCreateCopy(NULL, (CFStringRef)mutableReplacementString)); // Warning about potential leak of Core Foundation object can be safely ignored. - } - } - -#ifndef NS_BLOCK_ASSERTIONS -exitNow2: -#endif // NS_BLOCK_ASSERTIONS - if(RKL_EXPECTED(autoreleaseArray != NULL, 1L)) { CFArrayRemoveAllValues((CFMutableArrayRef)autoreleaseArray); } // Causes blockEnumerationHelper to be released immediately, freeing all of its resources (such as a large UTF-16 conversion buffer). - if(RKL_EXPECTED(exception != NULL, 0L)) { rkl_handleDelayedAssert(self, _cmd, exception); } // If there is an exception, throw it at this point. - if(((errorFree == NO) || ((errorFree == YES) && (returnObject == NULL))) && (error != NULL) && (*error == NULL)) { - RKLUserInfoOptions userInfoOptions = (RKLUserInfoSubjectRange | RKLUserInfoRegexEnumerationOptions); - NSString *replacedString = NULL; - if(blockEnumerationOp == RKLBlockEnumerationReplaceOp) { userInfoOptions |= RKLUserInfoReplacedCount; if(RKL_EXPECTED(errorFree == YES, 1L)) { replacedString = returnObject; } } - *error = rkl_makeNSError(userInfoOptions, regexString, options, NULL, status, (blockEnumerationHelper != NULL) ? (blockEnumerationHelper->buffer.string != NULL) ? (NSString *)blockEnumerationHelper->buffer.string : matchString : matchString, matchRange, NULL, replacedString, replacedCount, enumerationOptions, @"An unexpected error occurred."); - } - if(replacedCountPtr != NULL) { *replacedCountPtr = replacedCount; } - if(errorFreePtr != NULL) { *errorFreePtr = errorFree; } - return(returnObject); -} // The two warnings about potential leaks can be safely ignored. - -#endif // _RKL_BLOCKS_ENABLED - -//////////// -#pragma mark - -#pragma mark Objective-C Public Interface -#pragma mark - -//////////// - -@implementation NSString (RegexKitLiteAdditions) - -#pragma mark +clearStringCache - -+ (void)RKL_METHOD_PREPEND(clearStringCache) -{ - volatile NSUInteger RKL_CLEANUP(rkl_cleanup_cacheSpinLockStatus) rkl_cacheSpinLockStatus = 0UL; - OSSpinLockLock(&rkl_cacheSpinLock); - rkl_cacheSpinLockStatus |= RKLLockedCacheSpinLock; - rkl_clearStringCache(); - OSSpinLockUnlock(&rkl_cacheSpinLock); - rkl_cacheSpinLockStatus |= RKLUnlockedCacheSpinLock; // Warning about rkl_cacheSpinLockStatus never being read can be safely ignored. -} - -#pragma mark +captureCountForRegex: - -+ (NSInteger)RKL_METHOD_PREPEND(captureCountForRegex):(NSString *)regex -{ - NSInteger captureCount = -1L; - rkl_isRegexValid(self, _cmd, regex, RKLNoOptions, &captureCount, NULL); - return(captureCount); -} - -+ (NSInteger)RKL_METHOD_PREPEND(captureCountForRegex):(NSString *)regex options:(RKLRegexOptions)options error:(NSError **)error -{ - NSInteger captureCount = -1L; - rkl_isRegexValid(self, _cmd, regex, options, &captureCount, error); - return(captureCount); -} - -#pragma mark -captureCount: - -- (NSInteger)RKL_METHOD_PREPEND(captureCount) -{ - NSInteger captureCount = -1L; - rkl_isRegexValid(self, _cmd, self, RKLNoOptions, &captureCount, NULL); - return(captureCount); -} - -- (NSInteger)RKL_METHOD_PREPEND(captureCountWithOptions):(RKLRegexOptions)options error:(NSError **)error -{ - NSInteger captureCount = -1L; - rkl_isRegexValid(self, _cmd, self, options, &captureCount, error); - return(captureCount); -} - -#pragma mark -componentsSeparatedByRegex: - -- (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex -{ - NSRange range = NSMaxiumRange; - return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLSplitOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, NULL, 0UL, NULL, NULL)); -} - -- (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex range:(NSRange)range -{ - return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLSplitOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, NULL, 0UL, NULL, NULL)); -} - -- (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error -{ - return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLSplitOp, regex, options, 0L, self, &range, NULL, error, NULL, 0UL, NULL, NULL)); -} - -#pragma mark -isMatchedByRegex: - -- (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex -{ - NSRange result = NSNotFoundRange, range = NSMaxiumRange; - rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, &result, 0UL, NULL, NULL); - return((result.location == (NSUInteger)NSNotFound) ? NO : YES); -} - -- (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex inRange:(NSRange)range -{ - NSRange result = NSNotFoundRange; - rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, &result, 0UL, NULL, NULL); - return((result.location == (NSUInteger)NSNotFound) ? NO : YES); -} - -- (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error -{ - NSRange result = NSNotFoundRange; - rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, options, 0L, self, &range, NULL, error, &result, 0UL, NULL, NULL); - return((result.location == (NSUInteger)NSNotFound) ? NO : YES); -} - -#pragma mark -isRegexValid - -- (BOOL)RKL_METHOD_PREPEND(isRegexValid) -{ - return(rkl_isRegexValid(self, _cmd, self, RKLNoOptions, NULL, NULL) == 1UL ? YES : NO); -} - -- (BOOL)RKL_METHOD_PREPEND(isRegexValidWithOptions):(RKLRegexOptions)options error:(NSError **)error -{ - return(rkl_isRegexValid(self, _cmd, self, options, NULL, error) == 1UL ? YES : NO); -} - -#pragma mark -flushCachedRegexData - -- (void)RKL_METHOD_PREPEND(flushCachedRegexData) -{ - volatile NSUInteger RKL_CLEANUP(rkl_cleanup_cacheSpinLockStatus) rkl_cacheSpinLockStatus = 0UL; - - CFIndex selfLength = CFStringGetLength((CFStringRef)self); - CFHashCode selfHash = CFHash((CFTypeRef)self); - - OSSpinLockLock(&rkl_cacheSpinLock); - rkl_cacheSpinLockStatus |= RKLLockedCacheSpinLock; - rkl_dtrace_incrementEventID(); - - NSUInteger idx; - for(idx = 0UL; idx < _RKL_REGEX_CACHE_LINES; idx++) { - RKLCachedRegex *cachedRegex = &rkl_cachedRegexes[idx]; - if((cachedRegex->setToString != NULL) && ( (cachedRegex->setToString == (CFStringRef)self) || ((cachedRegex->setToLength == selfLength) && (cachedRegex->setToHash == selfHash)) ) ) { rkl_clearCachedRegexSetTo(cachedRegex); } - } - for(idx = 0UL; idx < _RKL_LRU_CACHE_SET_WAYS; idx++) { RKLBuffer *buffer = &rkl_lruFixedBuffer[idx]; if((buffer->string != NULL) && ((buffer->string == (CFStringRef)self) || ((buffer->length == selfLength) && (buffer->hash == selfHash)))) { rkl_clearBuffer(buffer, 0UL); } } - for(idx = 0UL; idx < _RKL_LRU_CACHE_SET_WAYS; idx++) { RKLBuffer *buffer = &rkl_lruDynamicBuffer[idx]; if((buffer->string != NULL) && ((buffer->string == (CFStringRef)self) || ((buffer->length == selfLength) && (buffer->hash == selfHash)))) { rkl_clearBuffer(buffer, 0UL); } } - - OSSpinLockUnlock(&rkl_cacheSpinLock); - rkl_cacheSpinLockStatus |= RKLUnlockedCacheSpinLock; // Warning about rkl_cacheSpinLockStatus never being read can be safely ignored. -} - -#pragma mark -rangeOfRegex: - -- (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex -{ - NSRange result = NSNotFoundRange, range = NSMaxiumRange; - rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, &result, 0UL, NULL, NULL); - return(result); -} - -- (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex capture:(NSInteger)capture -{ - NSRange result = NSNotFoundRange, range = NSMaxiumRange; - rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, RKLNoOptions, capture, self, &range, NULL, NULL, &result, 0UL, NULL, NULL); - return(result); -} - -- (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex inRange:(NSRange)range -{ - NSRange result = NSNotFoundRange; - rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, &result, 0UL, NULL, NULL); - return(result); -} - -- (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range capture:(NSInteger)capture error:(NSError **)error -{ - NSRange result = NSNotFoundRange; - rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, options, capture, self, &range, NULL, error, &result, 0UL, NULL, NULL); - return(result); -} - -#pragma mark -stringByMatching: - -- (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex -{ - NSRange matchedRange = NSNotFoundRange, range = NSMaxiumRange; - rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, &matchedRange, 0UL, NULL, NULL); - return((matchedRange.location == (NSUInteger)NSNotFound) ? NULL : rkl_CFAutorelease(CFStringCreateWithSubstring(NULL, (CFStringRef)self, CFMakeRange(matchedRange.location, matchedRange.length)))); // Warning about potential leak can be safely ignored. -} // Warning about potential leak can be safely ignored. - -- (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex capture:(NSInteger)capture -{ - NSRange matchedRange = NSNotFoundRange, range = NSMaxiumRange; - rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, RKLNoOptions, capture, self, &range, NULL, NULL, &matchedRange, 0UL, NULL, NULL); - return((matchedRange.location == (NSUInteger)NSNotFound) ? NULL : rkl_CFAutorelease(CFStringCreateWithSubstring(NULL, (CFStringRef)self, CFMakeRange(matchedRange.location, matchedRange.length)))); // Warning about potential leak can be safely ignored. -} // Warning about potential leak can be safely ignored. - -- (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex inRange:(NSRange)range -{ - NSRange matchedRange = NSNotFoundRange; - rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, &matchedRange, 0UL, NULL, NULL); - return((matchedRange.location == (NSUInteger)NSNotFound) ? NULL : rkl_CFAutorelease(CFStringCreateWithSubstring(NULL, (CFStringRef)self, CFMakeRange(matchedRange.location, matchedRange.length)))); // Warning about potential leak can be safely ignored. -} // Warning about potential leak can be safely ignored. - -- (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range capture:(NSInteger)capture error:(NSError **)error -{ - NSRange matchedRange = NSNotFoundRange; - rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, options, capture, self, &range, NULL, error, &matchedRange, 0UL, NULL, NULL); - return((matchedRange.location == (NSUInteger)NSNotFound) ? NULL : rkl_CFAutorelease(CFStringCreateWithSubstring(NULL, (CFStringRef)self, CFMakeRange(matchedRange.location, matchedRange.length)))); // Warning about potential leak can be safely ignored. -} // Warning about potential leak can be safely ignored. - -#pragma mark -stringByReplacingOccurrencesOfRegex: - -- (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement -{ - NSRange searchRange = NSMaxiumRange; - return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLReplaceOp, regex, RKLNoOptions, 0L, self, &searchRange, replacement, NULL, NULL, 0UL, NULL, NULL)); -} - -- (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement range:(NSRange)searchRange -{ - return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLReplaceOp, regex, RKLNoOptions, 0L, self, &searchRange, replacement, NULL, NULL, 0UL, NULL, NULL)); -} - -- (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement options:(RKLRegexOptions)options range:(NSRange)searchRange error:(NSError **)error -{ - return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLReplaceOp, regex, options, 0L, self, &searchRange, replacement, error, NULL, 0UL, NULL, NULL)); -} - -#pragma mark -componentsMatchedByRegex: - -- (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex -{ - NSRange searchRange = NSMaxiumRange; - return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLArrayOfStringsOp, regex, RKLNoOptions, 0L, self, &searchRange, NULL, NULL, NULL, 0UL, NULL, NULL)); -} - -- (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex capture:(NSInteger)capture -{ - NSRange searchRange = NSMaxiumRange; - return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLArrayOfStringsOp, regex, RKLNoOptions, capture, self, &searchRange, NULL, NULL, NULL, 0UL, NULL, NULL)); -} - -- (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex range:(NSRange)range -{ - return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLArrayOfStringsOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, NULL, 0UL, NULL, NULL)); -} - -- (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range capture:(NSInteger)capture error:(NSError **)error -{ - return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLArrayOfStringsOp, regex, options, capture, self, &range, NULL, error, NULL, 0UL, NULL, NULL)); -} - -#pragma mark -captureComponentsMatchedByRegex: - -- (NSArray *)RKL_METHOD_PREPEND(captureComponentsMatchedByRegex):(NSString *)regex -{ - NSRange searchRange = NSMaxiumRange; - return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLCapturesArrayOp, regex, RKLNoOptions, 0L, self, &searchRange, NULL, NULL, NULL, 0UL, NULL, NULL)); -} - -- (NSArray *)RKL_METHOD_PREPEND(captureComponentsMatchedByRegex):(NSString *)regex range:(NSRange)range -{ - return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLCapturesArrayOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, NULL, 0UL, NULL, NULL)); -} - -- (NSArray *)RKL_METHOD_PREPEND(captureComponentsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error -{ - return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLCapturesArrayOp, regex, options, 0L, self, &range, NULL, error, NULL, 0UL, NULL, NULL)); -} - -#pragma mark -arrayOfCaptureComponentsMatchedByRegex: - -- (NSArray *)RKL_METHOD_PREPEND(arrayOfCaptureComponentsMatchedByRegex):(NSString *)regex -{ - NSRange searchRange = NSMaxiumRange; - return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)(RKLArrayOfCapturesOp | RKLSubcapturesArray), regex, RKLNoOptions, 0L, self, &searchRange, NULL, NULL, NULL, 0UL, NULL, NULL)); -} - -- (NSArray *)RKL_METHOD_PREPEND(arrayOfCaptureComponentsMatchedByRegex):(NSString *)regex range:(NSRange)range -{ - return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)(RKLArrayOfCapturesOp | RKLSubcapturesArray), regex, RKLNoOptions, 0L, self, &range, NULL, NULL, NULL, 0UL, NULL, NULL)); -} - -- (NSArray *)RKL_METHOD_PREPEND(arrayOfCaptureComponentsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error -{ - return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)(RKLArrayOfCapturesOp | RKLSubcapturesArray), regex, options, 0L, self, &range, NULL, error, NULL, 0UL, NULL, NULL)); -} - -#pragma mark -dictionaryByMatchingRegex: - -- (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex withKeysAndCaptures:(id)firstKey, ... -{ - NSRange searchRange = NSMaxiumRange; - id returnObject = NULL; - va_list varArgsList; - va_start(varArgsList, firstKey); - returnObject = rkl_performDictionaryVarArgsOp(self, _cmd, (RKLRegexOp)RKLDictionaryOfCapturesOp, regex, (RKLRegexOptions)RKLNoOptions, 0L, self, &searchRange, NULL, NULL, NULL, firstKey, varArgsList); - va_end(varArgsList); - return(returnObject); -} - -- (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex range:(NSRange)range withKeysAndCaptures:(id)firstKey, ... -{ - id returnObject = NULL; - va_list varArgsList; - va_start(varArgsList, firstKey); - returnObject = rkl_performDictionaryVarArgsOp(self, _cmd, (RKLRegexOp)RKLDictionaryOfCapturesOp, regex, (RKLRegexOptions)RKLNoOptions, 0L, self, &range, NULL, NULL, NULL, firstKey, varArgsList); - va_end(varArgsList); - return(returnObject); -} - -- (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeysAndCaptures:(id)firstKey, ... -{ - id returnObject = NULL; - va_list varArgsList; - va_start(varArgsList, firstKey); - returnObject = rkl_performDictionaryVarArgsOp(self, _cmd, (RKLRegexOp)RKLDictionaryOfCapturesOp, regex, options, 0L, self, &range, NULL, error, NULL, firstKey, varArgsList); - va_end(varArgsList); - return(returnObject); -} - -- (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withFirstKey:(id)firstKey arguments:(va_list)varArgsList -{ - return(rkl_performDictionaryVarArgsOp(self, _cmd, (RKLRegexOp)RKLDictionaryOfCapturesOp, regex, options, 0L, self, &range, NULL, error, NULL, firstKey, varArgsList)); -} - -- (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeys:(id *)keys forCaptures:(int *)captures count:(NSUInteger)count -{ - return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLDictionaryOfCapturesOp, regex, options, 0L, self, &range, NULL, error, NULL, count, keys, captures)); -} - -#pragma mark -arrayOfDictionariesByMatchingRegex: - -- (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex withKeysAndCaptures:(id)firstKey, ... -{ - NSRange searchRange = NSMaxiumRange; - id returnObject = NULL; - va_list varArgsList; - va_start(varArgsList, firstKey); - returnObject = rkl_performDictionaryVarArgsOp(self, _cmd, (RKLRegexOp)RKLArrayOfDictionariesOfCapturesOp, regex, (RKLRegexOptions)RKLNoOptions, 0L, self, &searchRange, NULL, NULL, NULL, firstKey, varArgsList); - va_end(varArgsList); - return(returnObject); -} - -- (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex range:(NSRange)range withKeysAndCaptures:(id)firstKey, ... -{ - id returnObject = NULL; - va_list varArgsList; - va_start(varArgsList, firstKey); - returnObject = rkl_performDictionaryVarArgsOp(self, _cmd, (RKLRegexOp)RKLArrayOfDictionariesOfCapturesOp, regex, (RKLRegexOptions)RKLNoOptions, 0L, self, &range, NULL, NULL, NULL, firstKey, varArgsList); - va_end(varArgsList); - return(returnObject); -} - -- (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeysAndCaptures:(id)firstKey, ... -{ - id returnObject = NULL; - va_list varArgsList; - va_start(varArgsList, firstKey); - returnObject = rkl_performDictionaryVarArgsOp(self, _cmd, (RKLRegexOp)RKLArrayOfDictionariesOfCapturesOp, regex, options, 0L, self, &range, NULL, error, NULL, firstKey, varArgsList); - va_end(varArgsList); - return(returnObject); -} - -- (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withFirstKey:(id)firstKey arguments:(va_list)varArgsList -{ - return(rkl_performDictionaryVarArgsOp(self, _cmd, (RKLRegexOp)RKLArrayOfDictionariesOfCapturesOp, regex, options, 0L, self, &range, NULL, error, NULL, firstKey, varArgsList)); -} - -- (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeys:(id *)keys forCaptures:(int *)captures count:(NSUInteger)count -{ - return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLArrayOfDictionariesOfCapturesOp, regex, options, 0L, self, &range, NULL, error, NULL, count, keys, captures)); -} - -#ifdef _RKL_BLOCKS_ENABLED - -//////////// -#pragma mark - -#pragma mark ^Blocks Related NSString Methods - -#pragma mark -enumerateStringsMatchedByRegex:usingBlock: - -- (BOOL)RKL_METHOD_PREPEND(enumerateStringsMatchedByRegex):(NSString *)regex usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block -{ - NSUInteger errorFree = NO; - rkl_performEnumerationUsingBlock(self, _cmd, (RKLRegexOp)RKLCapturesArrayOp, regex, (RKLRegexOptions)RKLNoOptions, self, NSMaxiumRange, (RKLBlockEnumerationOp)RKLBlockEnumerationMatchOp, 0UL, NULL, &errorFree, NULL, block, NULL); - return(errorFree == NO ? NO : YES); -} - -- (BOOL)RKL_METHOD_PREPEND(enumerateStringsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block -{ - NSUInteger errorFree = NO; - rkl_performEnumerationUsingBlock(self, _cmd, (RKLRegexOp)RKLCapturesArrayOp, regex, options, self, range, (RKLBlockEnumerationOp)RKLBlockEnumerationMatchOp, enumerationOptions, NULL, &errorFree, error, block, NULL); - return(errorFree == NO ? NO : YES); -} - -#pragma mark -enumerateStringsSeparatedByRegex:usingBlock: - -- (BOOL)RKL_METHOD_PREPEND(enumerateStringsSeparatedByRegex):(NSString *)regex usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block -{ - NSUInteger errorFree = NO; - rkl_performEnumerationUsingBlock(self, _cmd, (RKLRegexOp)RKLSplitOp, regex, (RKLRegexOptions)RKLNoOptions, self, NSMaxiumRange, (RKLBlockEnumerationOp)RKLBlockEnumerationMatchOp, 0UL, NULL, &errorFree, NULL, block, NULL); - return(errorFree == NO ? NO : YES); -} - -- (BOOL)RKL_METHOD_PREPEND(enumerateStringsSeparatedByRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block -{ - NSUInteger errorFree = NO; - rkl_performEnumerationUsingBlock(self, _cmd, (RKLRegexOp)RKLSplitOp, regex, options, self, range, (RKLBlockEnumerationOp)RKLBlockEnumerationMatchOp, enumerationOptions, NULL, &errorFree, error, block, NULL); - return(errorFree == NO ? NO : YES); -} - -#pragma mark -stringByReplacingOccurrencesOfRegex:usingBlock: - -- (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block -{ - return(rkl_performEnumerationUsingBlock(self, _cmd, (RKLRegexOp)RKLCapturesArrayOp, regex, (RKLRegexOptions)RKLNoOptions, self, NSMaxiumRange, (RKLBlockEnumerationOp)RKLBlockEnumerationReplaceOp, 0UL, NULL, NULL, NULL, NULL, block)); -} - -- (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block -{ - return(rkl_performEnumerationUsingBlock(self, _cmd, (RKLRegexOp)RKLCapturesArrayOp, regex, options, self, range, (RKLBlockEnumerationOp)RKLBlockEnumerationReplaceOp, enumerationOptions, NULL, NULL, error, NULL, block)); -} - -#endif // _RKL_BLOCKS_ENABLED - -@end - -//////////// -#pragma mark - -@implementation NSMutableString (RegexKitLiteAdditions) - -#pragma mark -replaceOccurrencesOfRegex: - -- (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement -{ - NSRange searchRange = NSMaxiumRange; - NSInteger replacedCount = -1L; - rkl_performRegexOp(self, _cmd, (RKLRegexOp)(RKLReplaceOp | RKLReplaceMutable), regex, RKLNoOptions, 0L, self, &searchRange, replacement, NULL, (void **)((void *)&replacedCount), 0UL, NULL, NULL); - return(replacedCount); -} - -- (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement range:(NSRange)searchRange -{ - NSInteger replacedCount = -1L; - rkl_performRegexOp(self, _cmd, (RKLRegexOp)(RKLReplaceOp | RKLReplaceMutable), regex, RKLNoOptions, 0L, self, &searchRange, replacement, NULL, (void **)((void *)&replacedCount), 0UL, NULL, NULL); - return(replacedCount); -} - -- (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement options:(RKLRegexOptions)options range:(NSRange)searchRange error:(NSError **)error -{ - NSInteger replacedCount = -1L; - rkl_performRegexOp(self, _cmd, (RKLRegexOp)(RKLReplaceOp | RKLReplaceMutable), regex, options, 0L, self, &searchRange, replacement, error, (void **)((void *)&replacedCount), 0UL, NULL, NULL); - return(replacedCount); -} - -#ifdef _RKL_BLOCKS_ENABLED - -//////////// -#pragma mark - -#pragma mark ^Blocks Related NSMutableString Methods - -#pragma mark -replaceOccurrencesOfRegex:usingBlock: - -- (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block -{ - NSUInteger errorFree = 0UL; - NSInteger replacedCount = -1L; - NSString *replacedString = rkl_performEnumerationUsingBlock(self, _cmd, (RKLRegexOp)RKLCapturesArrayOp, regex, RKLNoOptions, self, NSMaxiumRange, (RKLBlockEnumerationOp)RKLBlockEnumerationReplaceOp, 0UL, &replacedCount, &errorFree, NULL, NULL, block); - if((errorFree == YES) && (replacedCount > 0L)) { [self replaceCharactersInRange:NSMakeRange(0UL, [self length]) withString:replacedString]; } - return(replacedCount); -} - -- (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block -{ - NSUInteger errorFree = 0UL; - NSInteger replacedCount = -1L; - NSString *replacedString = rkl_performEnumerationUsingBlock(self, _cmd, (RKLRegexOp)RKLCapturesArrayOp, regex, options, self, range, (RKLBlockEnumerationOp)RKLBlockEnumerationReplaceOp, enumerationOptions, &replacedCount, &errorFree, error, NULL, block); - if((errorFree == YES) && (replacedCount > 0L)) { [self replaceCharactersInRange:range withString:replacedString]; } - return(replacedCount); -} - -#endif // _RKL_BLOCKS_ENABLED - -@end diff --git a/SocketIO.h b/SocketIO.h deleted file mode 100644 index b5521a9..0000000 --- a/SocketIO.h +++ /dev/null @@ -1,107 +0,0 @@ -// -// SocketIO.h -// v.01 -// -// based on -// socketio-cocoa https://github.com/fpotter/socketio-cocoa -// by Fred Potter -// -// using -// https://github.com/erichocean/cocoa-websocket -// http://regexkit.sourceforge.net/RegexKitLite/ -// https://github.com/stig/json-framework/ -// http://allseeing-i.com/ASIHTTPRequest/ -// -// reusing some parts of -// /socket.io/socket.io.js -// -// Created by Philipp Kyeck http://beta_interactive.de -// - -#import - -@class WebSocket; -@class SocketIO; -@class SocketIOPacket; - - -@protocol SocketIODelegate -@optional -- (void) socketIODidConnect:(SocketIO *)socket; -- (void) socketIODidDisconnect:(SocketIO *)socket; -- (void) socketIO:(SocketIO *)socket didReceiveMessage:(SocketIOPacket *)packet; -- (void) socketIO:(SocketIO *)socket didReceiveJSON:(SocketIOPacket *)packet; -- (void) socketIO:(SocketIO *)socket didReceiveEvent:(SocketIOPacket *)packet; -- (void) socketIO:(SocketIO *)socket didSendMessage:(SocketIOPacket *)packet; -@end - - -@interface SocketIO : NSObject -{ - @private - NSString *_host; - NSInteger _port; - NSString *_sid; - - id _delegate; - - WebSocket *_webSocket; - - BOOL _isConnected; - BOOL _isConnecting; - - // heartbeat - NSTimeInterval _heartbeatTimeout; - NSTimer *_timeout; - - NSMutableArray *_queue; - - // acknowledge - NSMutableDictionary *_acks; - NSInteger _ackCount; -} - -- (id) initWithDelegate:(id)delegate; -- (void) connectToHost:(NSString *)host onPort:(NSInteger)port; -- (void) disconnect; - -- (void) sendMessage:(NSString *)data; -- (void) sendMessage:(NSString *)data withAcknowledge:(SEL)function; -- (void) sendJSON:(NSDictionary *)data; -- (void) sendJSON:(NSDictionary *)data withAcknowledge:(SEL)function; -- (void) sendEvent:(NSString *)eventName withData:(NSDictionary *)data; -- (void) sendEvent:(NSString *)eventName withData:(NSDictionary *)data andAcknowledge:(SEL)function; -- (void) sendAcknowledgement:(NSString*)pId withArgs:(NSArray *)data; - -@end - - -@interface SocketIOPacket : NSObject -{ - NSString *type; - NSString *pId; - NSString *ack; - NSString *name; - NSString *data; - NSArray *args; - NSString *endpoint; - - @private - NSArray *_types; -} - -@property (nonatomic, copy) NSString *type; -@property (nonatomic, copy) NSString *pId; -@property (nonatomic, copy) NSString *ack; -@property (nonatomic, copy) NSString *name; -@property (nonatomic, copy) NSString *data; -@property (nonatomic, copy) NSString *endpoint; -@property (nonatomic, copy) NSArray *args; - -- (id) initWithType:(NSString *)packetType; -- (id) initWithTypeIndex:(int)index; -- (id) dataAsJSON; -- (NSNumber *) typeAsNumber; -- (NSString *) typeForIndex:(int)index; - -@end \ No newline at end of file diff --git a/SocketIO.m b/SocketIO.m deleted file mode 100644 index e5da03f..0000000 --- a/SocketIO.m +++ /dev/null @@ -1,642 +0,0 @@ -// -// SocketIO.m -// v.01 -// -// based on -// socketio-cocoa https://github.com/fpotter/socketio-cocoa -// by Fred Potter -// -// using -// https://github.com/erichocean/cocoa-websocket -// http://regexkit.sourceforge.net/RegexKitLite/ -// https://github.com/stig/json-framework/ -// http://allseeing-i.com/ASIHTTPRequest/ -// -// reusing some parts of -// /socket.io/socket.io.js -// -// Created by Philipp Kyeck http://beta_interactive.de -// - -#import "SocketIO.h" - -#import "ASIHTTPRequest.h" -#import "WebSocket.h" -#import "RegexKitLite.h" -#import "SBJson.h" - -#define DEBUG_LOGS 1 -#define HANDSHAKE_URL @"http://%@:%d/socket.io/1/?t=%d" -#define SOCKET_URL @"ws://%@:%d/socket.io/1/websocket/%@" - - -# pragma mark - -# pragma mark SocketIO's private interface - -@interface SocketIO (FP_Private) - -- (void) log:(NSString *)message; - -- (void) setTimeout; -- (void) onTimeout; - -- (void) onConnect; -- (void) onDisconnect; - -- (void) sendDisconnect; -- (void) sendHearbeat; -- (void) send:(SocketIOPacket *)packet; - -- (NSString *) addAcknowledge:(SEL)function; -- (void) removeAcknowledgeForKey:(NSString *)key; - -@end - - -# pragma mark - -# pragma mark SocketIO implementation - -@implementation SocketIO - -- (id) initWithDelegate:(id)delegate -{ - self = [super init]; - if (self) - { - _delegate = delegate; - - _queue = [[NSMutableArray alloc] init]; - - _ackCount = 0; - _acks = [[NSMutableDictionary alloc] init]; - } - return self; -} - -- (void) connectToHost:(NSString *)host onPort:(NSInteger)port -{ - if (!_isConnected && !_isConnecting) - { - _isConnecting = YES; - - _host = [host retain]; - _port = port; - - // do handshake via HTTP request - NSString *s = [NSString stringWithFormat:HANDSHAKE_URL, _host, _port, rand()]; - NSURL *url = [NSURL URLWithString:s]; - - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; - [request setDelegate:self]; - [request startAsynchronous]; - } -} - -- (void) disconnect -{ - [self sendDisconnect]; -} - -- (void) sendMessage:(NSString *)data -{ - [self sendMessage:data withAcknowledge:nil]; -} - -- (void) sendMessage:(NSString *)data withAcknowledge:(SEL)function -{ - SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"message"]; - packet.data = data; - packet.pId = [self addAcknowledge:function]; - [self send:packet]; - [packet release]; -} - -- (void) sendJSON:(NSDictionary *)data -{ - [self sendJSON:data withAcknowledge:nil]; -} - -- (void) sendJSON:(NSDictionary *)data withAcknowledge:(SEL)function -{ - SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"json"]; - packet.data = [data JSONRepresentation]; - packet.pId = [self addAcknowledge:function]; - [self send:packet]; - [packet release]; -} - -- (void) sendEvent:(NSString *)eventName withData:(NSDictionary *)data -{ - [self sendEvent:eventName withData:data andAcknowledge:nil]; -} - -- (void) sendEvent:(NSString *)eventName withData:(NSDictionary *)data andAcknowledge:(SEL)function -{ - NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObject:eventName forKey:@"name"]; - [dict setObject:data forKey:@"args"]; - - SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"event"]; - packet.data = [dict JSONRepresentation]; - packet.pId = [self addAcknowledge:function]; - if (function) - { - packet.ack = @"data"; - } - [self send:packet]; - [packet release]; -} - -- (void)sendAcknowledgement:(NSString *)pId withArgs:(NSArray *)data { - SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"ack"]; - packet.data = [data JSONRepresentation]; - packet.pId = pId; - packet.ack = @"data"; - - [self send:packet]; - [packet release]; -} - -# pragma mark - -# pragma mark private methods - -- (void) openSocket -{ - NSString *url = [NSString stringWithFormat:SOCKET_URL, _host, _port, _sid]; - - [_webSocket release]; - _webSocket = nil; - - _webSocket = [[WebSocket alloc] initWithURLString:url delegate:self]; - [self log:[NSString stringWithFormat:@"Opening %@", url]]; - [_webSocket open]; -} - -- (void) sendDisconnect -{ - SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"disconnect"]; - [self send:packet]; - [packet release]; -} - -- (void) sendHeartbeat -{ - SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"heartbeat"]; - [self send:packet]; - [packet release]; -} - -- (void) send:(SocketIOPacket *)packet -{ - [self log:@"send()"]; - NSNumber *type = [packet typeAsNumber]; - NSMutableArray *encoded = [NSMutableArray arrayWithObject:type]; - - NSString *pId = packet.pId != nil ? packet.pId : @""; - if ([packet.ack isEqualToString:@"data"]) - { - pId = [pId stringByAppendingString:@"+"]; - } - - // Do not write pid for acknowledgements - if ([type intValue] != 6) { - [encoded addObject:pId]; - } - - // not yet sure what this is for - NSString *endPoint = @""; - [encoded addObject:endPoint]; - - - if (packet.data != nil) - { - NSString *ackpId = @""; - // This is an acknowledgement packet, so, prepend the ack pid to the data - if ([type intValue] == 6) { - ackpId = [NSString stringWithFormat:@":%@%@", packet.pId, @"+"]; - } - - [encoded addObject:[NSString stringWithFormat:@"%@%@", ackpId, packet.data]]; - } - - NSString *req = [encoded componentsJoinedByString:@":"]; - if (!_isConnected) - { - [self log:[NSString stringWithFormat:@"queue >>> %@", req]]; - [_queue addObject:packet]; - } - else - { - [self log:[NSString stringWithFormat:@"send() >>> %@", req]]; - [_webSocket send:req]; - - if ([_delegate respondsToSelector:@selector(socketIO:didSendMessage:)]) - { - [_delegate socketIO:self didSendMessage:packet]; - } - } -} - - - -- (void) onData:(NSString *)data -{ - [self log:[NSString stringWithFormat:@"onData %@", data]]; - - // data arrived -> reset timeout - [self setTimeout]; - - // check if data is valid (from socket.io.js) - NSString *regex = @"^([^:]+):([0-9]+)?(\\+)?:([^:]+)?:?(.*)?$"; - NSString *regexPieces = @"^([0-9]+)(\\+)?(.*)"; - NSArray *test = [data arrayOfCaptureComponentsMatchedByRegex:regex]; - - // valid data-string arrived - if ([test count] > 0) - { - NSArray *result = [test objectAtIndex:0]; - - int idx = [[result objectAtIndex:1] intValue]; - SocketIOPacket *packet = [[SocketIOPacket alloc] initWithTypeIndex:idx]; - - packet.pId = [result objectAtIndex:2]; - - packet.ack = [result objectAtIndex:3]; - packet.endpoint = [result objectAtIndex:4]; - packet.data = [result objectAtIndex:5]; - - // - switch (idx) - { - case 0: - [self log:@"disconnect"]; - [self onDisconnect]; - break; - - case 1: - [self log:@"connect"]; - // from socket.io.js ... not sure when data will contain sth?! - // packet.qs = data || ''; - [self onConnect]; - break; - - case 2: - [self log:@"heartbeat"]; - [self sendHeartbeat]; - break; - - case 3: - [self log:@"message"]; - if (packet.data && ![packet.data isEqualToString:@""]) - { - if ([_delegate respondsToSelector:@selector(socketIO:didReceiveMessage:)]) - { - [_delegate socketIO:self didReceiveMessage:packet]; - } - } - break; - - case 4: - [self log:@"json"]; - if (packet.data && ![packet.data isEqualToString:@""]) - { - if ([_delegate respondsToSelector:@selector(socketIO:didReceiveJSON:)]) - { - [_delegate socketIO:self didReceiveJSON:packet]; - } - } - break; - - case 5: - [self log:@"event"]; - if (packet.data && ![packet.data isEqualToString:@""]) - { - NSDictionary *json = [packet dataAsJSON]; - packet.name = [json objectForKey:@"name"]; - packet.args = [json objectForKey:@"args"]; - if ([_delegate respondsToSelector:@selector(socketIO:didReceiveEvent:)]) - { - [_delegate socketIO:self didReceiveEvent:packet]; - } - } - break; - - case 6: - [self log:@"ack"]; - NSArray *pieces = [packet.data arrayOfCaptureComponentsMatchedByRegex:regexPieces]; - - if ([pieces count] > 0) - { - NSArray *piece = [pieces objectAtIndex:0]; - int ackId = [[piece objectAtIndex:1] intValue]; - [self log:[NSString stringWithFormat:@"ack id found: %d", ackId]]; - - NSString *argsStr = [piece objectAtIndex:3]; - id argsData = nil; - if (argsStr && ![argsStr isEqualToString:@""]) - { - argsData = [argsStr JSONValue]; - if ([argsData count] > 0) - { - argsData = [argsData objectAtIndex:0]; - } - } - - // get selector for ackId - NSString *key = [NSString stringWithFormat:@"%d", ackId]; - SEL function = NSSelectorFromString([_acks objectForKey:key]); - if ([_delegate respondsToSelector:function]) - { - if (argsData != nil) - { - [_delegate performSelector:function withObject:argsData]; - } - else - { - [_delegate performSelector:function]; - } - [self removeAcknowledgeForKey:key]; - } - } - - break; - - case 7: - [self log:@"error"]; - break; - - case 8: - [self log:@"noop"]; - break; - - default: - [self log:@"command not found or not yet supported"]; - break; - } - - [packet release]; - } - else - { - [self log:@"ERROR: data that has arrived wasn't valid"]; - } -} - - -- (void) doQueue -{ - [self log:[NSString stringWithFormat:@"doQueue() >> %d", [_queue count]]]; - - // TODO send all packets at once ... not as seperate packets - while ([_queue count] > 0) - { - SocketIOPacket *packet = [_queue objectAtIndex:0]; - [self send:packet]; - [_queue removeObject:packet]; - } -} - -- (void) onConnect -{ - [self log:@"onConnect()"]; - - _isConnected = YES; - _isConnecting = NO; - - if ([_delegate respondsToSelector:@selector(socketIODidConnect:)]) - { - [_delegate socketIODidConnect:self]; - } - - // semd amy queued packets - [self doQueue]; - - [self setTimeout]; -} - -- (void) onDisconnect -{ - [self log:@"onDisconnect()"]; - BOOL wasConnected = _isConnected; - - _isConnected = NO; - _isConnecting = NO; - _sid = nil; - - [_queue removeAllObjects]; - - if (wasConnected && [_delegate respondsToSelector:@selector(socketIODidDisconnect:)]) - { - [_delegate socketIODidDisconnect:self]; - } -} - -# pragma mark - -# pragma mark Acknowledge methods - -- (NSString *) addAcknowledge:(SEL)function -{ - if (function) - { - ++_ackCount; - NSString *ac = [NSString stringWithFormat:@"%d", _ackCount]; - [_acks setObject:NSStringFromSelector(function) forKey:ac]; - return ac; - } - return nil; -} - -- (void) removeAcknowledgeForKey:(NSString *)key -{ - [_acks removeObjectForKey:key]; -} - -# pragma mark - -# pragma mark Heartbeat methods - -- (void) onTimeout -{ - [self log:@"Timed out waiting for heartbeat."]; - [self onDisconnect]; -} - -- (void) setTimeout -{ - [self log:@"setTimeout()"]; - if (_timeout != nil) - { - [_timeout invalidate]; - [_timeout release]; - _timeout = nil; - } - - _timeout = [[NSTimer scheduledTimerWithTimeInterval:_heartbeatTimeout - target:self - selector:@selector(onTimeout) - userInfo:nil - repeats:NO] retain]; -} - - -# pragma mark - -# pragma mark Handshake callbacks - -- (void) requestFinished:(ASIHTTPRequest *)request -{ - NSString *responseString = [request responseString]; - [self log:[NSString stringWithFormat:@"requestFinished() %@", responseString]]; - NSArray *data = [responseString componentsSeparatedByString:@":"]; - - _sid = [[data objectAtIndex:0] retain]; - [self log:[NSString stringWithFormat:@"sid: %@", _sid]]; - - // add small buffer of 7sec (magic xD) - _heartbeatTimeout = [[data objectAtIndex:1] floatValue] + 7.0; - [self log:[NSString stringWithFormat:@"heartbeatTimeout: %f", _heartbeatTimeout]]; - - // index 2 => connection timeout - - NSString *t = [data objectAtIndex:3]; - NSArray *transports = [t componentsSeparatedByString:@","]; - [self log:[NSString stringWithFormat:@"transports: %@", transports]]; - - [self openSocket]; -} - -- (void) requestFailed:(ASIHTTPRequest *)request -{ - NSError *error = [request error]; - NSLog(@"ERROR: handshake failed ... %@", [error localizedDescription]); -} - -# pragma mark - -# pragma mark WebSocket Delegate Methods - -- (void) webSocketDidClose:(WebSocket*)webSocket -{ - [self log:[NSString stringWithFormat:@"Connection closed."]]; - [self onDisconnect]; -} - -- (void) webSocketDidOpen:(WebSocket *)ws -{ - [self log:[NSString stringWithFormat:@"Connection opened."]]; -} - -- (void) webSocket:(WebSocket *)ws didFailWithError:(NSError *)error -{ - NSLog(@"ERROR: Connection failed with error ... %@", [error localizedDescription]); -} - -- (void) webSocket:(WebSocket *)ws didReceiveMessage:(NSString*)message -{ - [self onData:message]; -} - -# pragma mark - - -- (void) log:(NSString *)message -{ -#if DEBUG_LOGS - NSLog(@"%@", message); -#endif -} - -- (void) dealloc -{ - [_host release]; - [_sid release]; - - [_webSocket release]; - - [_timeout invalidate]; - [_timeout release]; - - [_queue release]; - [_acks release]; - - [super dealloc]; -} - - -@end - - -# pragma mark - -# pragma mark SocketIOPacket implementation - -@implementation SocketIOPacket - -@synthesize type, pId, name, ack, data, args, endpoint; - -- (id) init -{ - self = [super init]; - if (self) - { - _types = [[NSArray arrayWithObjects: @"disconnect", - @"connect", - @"heartbeat", - @"message", - @"json", - @"event", - @"ack", - @"error", - @"noop", - nil] retain]; - } - return self; -} - -- (id) initWithType:(NSString *)packetType -{ - self = [self init]; - if (self) - { - self.type = packetType; - } - return self; -} - -- (id) initWithTypeIndex:(int)index -{ - self = [self init]; - if (self) - { - self.type = [self typeForIndex:index]; - } - return self; -} - -- (id) dataAsJSON -{ - return [self.data JSONValue]; -} - -- (NSNumber *) typeAsNumber -{ - int index = [_types indexOfObject:self.type]; - NSNumber *num = [NSNumber numberWithInt:index]; - return num; -} - -- (NSString *) typeForIndex:(int)index -{ - return [_types objectAtIndex:index]; -} - -- (void) dealloc -{ - [_types release]; - - [type release]; - [pId release]; - [name release]; - [ack release]; - [data release]; - [args release]; - [endpoint release]; - - [super dealloc]; -} - -@end diff --git a/WebSocket.h b/WebSocket.h deleted file mode 100755 index 6f86f0f..0000000 --- a/WebSocket.h +++ /dev/null @@ -1,54 +0,0 @@ -// -// WebSocket.h -// Zimt -// -// Created by Esad Hajdarevic on 2/14/10. -// Copyright 2010 OpenResearch Software Development OG. All rights reserved. -// - -#import - -@class AsyncSocket; -@class WebSocket; - -@protocol WebSocketDelegate -@optional - - (void)webSocket:(WebSocket*)webSocket didFailWithError:(NSError*)error; - - (void)webSocketDidOpen:(WebSocket*)webSocket; - - (void)webSocketDidClose:(WebSocket*)webSocket; - - (void)webSocket:(WebSocket*)webSocket didReceiveMessage:(NSString*)message; - - (void)webSocketDidSendMessage:(WebSocket*)webSocket; -@end - -@interface WebSocket : NSObject { - id delegate; - NSURL* url; - AsyncSocket* socket; - BOOL connected; - NSString* origin; - - NSArray* runLoopModes; -} - -@property(nonatomic,assign) id delegate; -@property(nonatomic,readonly) NSURL* url; -@property(nonatomic,retain) NSString* origin; -@property(nonatomic,readonly) BOOL connected; -@property(nonatomic,retain) NSArray* runLoopModes; - -+ (id)webSocketWithURLString:(NSString*)urlString delegate:(id)delegate; -- (id)initWithURLString:(NSString*)urlString delegate:(id)delegate; - -- (void)open; -- (void)close; -- (void)send:(NSString*)message; - -@end - -enum { - WebSocketErrorConnectionFailed = 1, - WebSocketErrorHandshakeFailed = 2 -}; - -extern NSString *const WebSocketException; -extern NSString* const WebSocketErrorDomain; diff --git a/WebSocket.m b/WebSocket.m deleted file mode 100755 index 6ca37f7..0000000 --- a/WebSocket.m +++ /dev/null @@ -1,178 +0,0 @@ -// -// WebSocket.m -// Zimt -// -// Created by Esad Hajdarevic on 2/14/10. -// Copyright 2010 OpenResearch Software Development OG. All rights reserved. -// - -#import "WebSocket.h" -#import "AsyncSocket.h" - - -NSString* const WebSocketErrorDomain = @"WebSocketErrorDomain"; -NSString* const WebSocketException = @"WebSocketException"; - -enum { - WebSocketTagHandshake = 0, - WebSocketTagMessage = 1 -}; - -@implementation WebSocket - -@synthesize delegate, url, origin, connected, runLoopModes; - -#pragma mark Initializers - -+ (id)webSocketWithURLString:(NSString*)urlString delegate:(id)aDelegate { - return [[[WebSocket alloc] initWithURLString:urlString delegate:aDelegate] autorelease]; -} - --(id)initWithURLString:(NSString *)urlString delegate:(id)aDelegate { - self = [super init]; - if (self) { - self.delegate = aDelegate; - url = [[NSURL URLWithString:urlString] retain]; - if (![url.scheme isEqualToString:@"ws"]) { - [NSException raise:WebSocketException format:@"Unsupported protocol %@", url.scheme]; - } - socket = [[AsyncSocket alloc] initWithDelegate:self]; - self.runLoopModes = [NSArray arrayWithObjects:NSRunLoopCommonModes, nil]; - } - return self; -} - -#pragma mark Delegate dispatch methods - --(void)_dispatchFailure:(NSNumber*)code { - if(delegate && [delegate respondsToSelector:@selector(webSocket:didFailWithError:)]) { - [delegate webSocket:self didFailWithError:[NSError errorWithDomain:WebSocketErrorDomain code:[code intValue] userInfo:nil]]; - } -} - --(void)_dispatchClosed { - if (delegate && [delegate respondsToSelector:@selector(webSocketDidClose:)]) { - [delegate webSocketDidClose:self]; - } -} - --(void)_dispatchOpened { - if (delegate && [delegate respondsToSelector:@selector(webSocketDidOpen:)]) { - [delegate webSocketDidOpen:self]; - } -} - --(void)_dispatchMessageReceived:(NSString*)message { - if (delegate && [delegate respondsToSelector:@selector(webSocket:didReceiveMessage:)]) { - [delegate webSocket:self didReceiveMessage:message]; - } -} - --(void)_dispatchMessageSent { - if (delegate && [delegate respondsToSelector:@selector(webSocketDidSendMessage:)]) { - [delegate webSocketDidSendMessage:self]; - } -} - -#pragma mark Private - --(void)_readNextMessage { - [socket readDataToData:[NSData dataWithBytes:"\xFF" length:1] withTimeout:-1 tag:WebSocketTagMessage]; -} - -#pragma mark Public interface - --(void)close { - [socket disconnectAfterReadingAndWriting]; -} - --(void)open { - if (!connected) { - [socket connectToHost:url.host onPort:[url.port intValue] withTimeout:5 error:nil]; - if (runLoopModes) [socket setRunLoopModes:runLoopModes]; - } -} - --(void)send:(NSString*)message { - NSMutableData* data = [NSMutableData data]; - [data appendBytes:"\x00" length:1]; - [data appendData:[message dataUsingEncoding:NSUTF8StringEncoding]]; - [data appendBytes:"\xFF" length:1]; - [socket writeData:data withTimeout:-1 tag:WebSocketTagMessage]; -} - -#pragma mark AsyncSocket delegate methods - --(void)onSocketDidDisconnect:(AsyncSocket *)sock { - connected = NO; -} - --(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err { - if (!connected) { - [self _dispatchFailure:[NSNumber numberWithInt:WebSocketErrorConnectionFailed]]; - } else { - [self _dispatchClosed]; - } -} - -- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port { - NSString* requestOrigin = self.origin; - if (!requestOrigin) requestOrigin = [NSString stringWithFormat:@"http://%@",url.host]; - - NSString *requestPath = url.path; - if (url.query) { - requestPath = [requestPath stringByAppendingFormat:@"?%@", url.query]; - } - NSString* getRequest = [NSString stringWithFormat:@"GET %@ HTTP/1.1\r\n" - "Upgrade: WebSocket\r\n" - "Connection: Upgrade\r\n" - "Host: %@\r\n" - "Origin: %@\r\n" - "\r\n", - requestPath,url.host,requestOrigin]; - [socket writeData:[getRequest dataUsingEncoding:NSASCIIStringEncoding] withTimeout:-1 tag:WebSocketTagHandshake]; -} - --(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag { - if (tag == WebSocketTagHandshake) { - [sock readDataToData:[@"\r\n\r\n" dataUsingEncoding:NSASCIIStringEncoding] withTimeout:-1 tag:WebSocketTagHandshake]; - } else if (tag == WebSocketTagMessage) { - [self _dispatchMessageSent]; - } -} - --(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag { - if (tag == WebSocketTagHandshake) { - NSString* response = [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease]; - if ([response hasPrefix:@"HTTP/1.1 101 Web Socket Protocol Handshake\r\nUpgrade: WebSocket\r\nConnection: Upgrade\r\n"]) { - connected = YES; - [self _dispatchOpened]; - - [self _readNextMessage]; - } else { - [self _dispatchFailure:[NSNumber numberWithInt:WebSocketErrorHandshakeFailed]]; - } - } else if (tag == WebSocketTagMessage) { - char firstByte = 0xFF; - [data getBytes:&firstByte length:1]; - if (firstByte != 0x00) return; // Discard message - NSString* message = [[[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(1, [data length]-2)] encoding:NSUTF8StringEncoding] autorelease]; - - [self _dispatchMessageReceived:message]; - [self _readNextMessage]; - } -} - -#pragma mark Destructor - --(void)dealloc { - socket.delegate = nil; - [socket disconnect]; - [socket release]; - [runLoopModes release]; - [url release]; - [super dealloc]; -} - -@end - diff --git a/socketIOexample/socketIOexample.xcodeproj/project.pbxproj b/socketIOexample/socketIOexample.xcodeproj/project.pbxproj new file mode 100644 index 0000000..2304b24 --- /dev/null +++ b/socketIOexample/socketIOexample.xcodeproj/project.pbxproj @@ -0,0 +1,637 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + C56D4C2F1814E221007C35B3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C56D4C2E1814E221007C35B3 /* Foundation.framework */; }; + C56D4C311814E221007C35B3 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C56D4C301814E221007C35B3 /* CoreGraphics.framework */; }; + C56D4C331814E221007C35B3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C56D4C321814E221007C35B3 /* UIKit.framework */; }; + C56D4C391814E221007C35B3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C56D4C371814E221007C35B3 /* InfoPlist.strings */; }; + C56D4C3B1814E221007C35B3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4C3A1814E221007C35B3 /* main.m */; }; + C56D4C3F1814E221007C35B3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4C3E1814E221007C35B3 /* AppDelegate.m */; }; + C56D4C421814E221007C35B3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C56D4C401814E221007C35B3 /* Main.storyboard */; }; + C56D4C451814E221007C35B3 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4C441814E221007C35B3 /* ViewController.m */; }; + C56D4C471814E221007C35B3 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C56D4C461814E221007C35B3 /* Images.xcassets */; }; + C56D4C4E1814E221007C35B3 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C56D4C4D1814E221007C35B3 /* XCTest.framework */; }; + C56D4C4F1814E222007C35B3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C56D4C2E1814E221007C35B3 /* Foundation.framework */; }; + C56D4C501814E222007C35B3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C56D4C321814E221007C35B3 /* UIKit.framework */; }; + C56D4C581814E222007C35B3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C56D4C561814E222007C35B3 /* InfoPlist.strings */; }; + C56D4C5A1814E222007C35B3 /* socketIOexampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4C591814E222007C35B3 /* socketIOexampleTests.m */; }; + C56D4D0D1814E3AE007C35B3 /* SBJsonParser.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4CE71814E3AE007C35B3 /* SBJsonParser.m */; }; + C56D4D0E1814E3AE007C35B3 /* SBJsonStreamParser.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4CE91814E3AE007C35B3 /* SBJsonStreamParser.m */; }; + C56D4D0F1814E3AE007C35B3 /* SBJsonStreamParserAccumulator.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4CEB1814E3AE007C35B3 /* SBJsonStreamParserAccumulator.m */; }; + C56D4D101814E3AE007C35B3 /* SBJsonStreamParserAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4CED1814E3AE007C35B3 /* SBJsonStreamParserAdapter.m */; }; + C56D4D111814E3AE007C35B3 /* SBJsonStreamParserState.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4CEF1814E3AE007C35B3 /* SBJsonStreamParserState.m */; }; + C56D4D121814E3AE007C35B3 /* SBJsonStreamTokeniser.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4CF11814E3AE007C35B3 /* SBJsonStreamTokeniser.m */; }; + C56D4D131814E3AE007C35B3 /* SBJsonStreamWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4CF31814E3AE007C35B3 /* SBJsonStreamWriter.m */; }; + C56D4D141814E3AE007C35B3 /* SBJsonStreamWriterAccumulator.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4CF51814E3AE007C35B3 /* SBJsonStreamWriterAccumulator.m */; }; + C56D4D151814E3AE007C35B3 /* SBJsonStreamWriterState.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4CF71814E3AE007C35B3 /* SBJsonStreamWriterState.m */; }; + C56D4D161814E3AE007C35B3 /* SBJsonWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4CF91814E3AE007C35B3 /* SBJsonWriter.m */; }; + C56D4D171814E3AE007C35B3 /* SocketIO.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4CFB1814E3AE007C35B3 /* SocketIO.m */; }; + C56D4D181814E3AE007C35B3 /* SocketIOJSONSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4CFD1814E3AE007C35B3 /* SocketIOJSONSerialization.m */; }; + C56D4D191814E3AE007C35B3 /* SocketIOPacket.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4CFF1814E3AE007C35B3 /* SocketIOPacket.m */; }; + C56D4D1A1814E3AE007C35B3 /* SocketIOTransportWebsocket.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4D021814E3AE007C35B3 /* SocketIOTransportWebsocket.m */; }; + C56D4D1B1814E3AE007C35B3 /* SocketIOTransportXHR.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4D041814E3AE007C35B3 /* SocketIOTransportXHR.m */; }; + C56D4D1C1814E3AE007C35B3 /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = C56D4D061814E3AE007C35B3 /* base64.c */; }; + C56D4D1D1814E3AE007C35B3 /* NSData+SRB64Additions.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4D091814E3AE007C35B3 /* NSData+SRB64Additions.m */; }; + C56D4D1E1814E3AE007C35B3 /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = C56D4D0C1814E3AE007C35B3 /* SRWebSocket.m */; }; + C56D4D201814E3ED007C35B3 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C56D4D1F1814E3ED007C35B3 /* Security.framework */; }; + C56D4D221814E3F5007C35B3 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C56D4D211814E3F5007C35B3 /* SystemConfiguration.framework */; }; + C56D4D241814E3FB007C35B3 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C56D4D231814E3FB007C35B3 /* MobileCoreServices.framework */; }; + C56D4D261814E400007C35B3 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C56D4D251814E3FF007C35B3 /* CFNetwork.framework */; }; + C56D4D281814E41A007C35B3 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = C56D4D271814E41A007C35B3 /* libicucore.dylib */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + C56D4C511814E222007C35B3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = C56D4C231814E221007C35B3 /* Project object */; + proxyType = 1; + remoteGlobalIDString = C56D4C2A1814E221007C35B3; + remoteInfo = socketIOexample; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + C56D4C2B1814E221007C35B3 /* socketIOexample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = socketIOexample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + C56D4C2E1814E221007C35B3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + C56D4C301814E221007C35B3 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + C56D4C321814E221007C35B3 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + C56D4C361814E221007C35B3 /* socketIOexample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "socketIOexample-Info.plist"; sourceTree = ""; }; + C56D4C381814E221007C35B3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + C56D4C3A1814E221007C35B3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + C56D4C3C1814E221007C35B3 /* socketIOexample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "socketIOexample-Prefix.pch"; sourceTree = ""; }; + C56D4C3D1814E221007C35B3 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + C56D4C3E1814E221007C35B3 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + C56D4C411814E221007C35B3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + C56D4C431814E221007C35B3 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + C56D4C441814E221007C35B3 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + C56D4C461814E221007C35B3 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + C56D4C4C1814E221007C35B3 /* socketIOexampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = socketIOexampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + C56D4C4D1814E221007C35B3 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + C56D4C551814E222007C35B3 /* socketIOexampleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "socketIOexampleTests-Info.plist"; sourceTree = ""; }; + C56D4C571814E222007C35B3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + C56D4C591814E222007C35B3 /* socketIOexampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = socketIOexampleTests.m; sourceTree = ""; }; + C56D4CE51814E3AE007C35B3 /* SBJson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJson.h; sourceTree = ""; }; + C56D4CE61814E3AE007C35B3 /* SBJsonParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonParser.h; sourceTree = ""; }; + C56D4CE71814E3AE007C35B3 /* SBJsonParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonParser.m; sourceTree = ""; }; + C56D4CE81814E3AE007C35B3 /* SBJsonStreamParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParser.h; sourceTree = ""; }; + C56D4CE91814E3AE007C35B3 /* SBJsonStreamParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParser.m; sourceTree = ""; }; + C56D4CEA1814E3AE007C35B3 /* SBJsonStreamParserAccumulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParserAccumulator.h; sourceTree = ""; }; + C56D4CEB1814E3AE007C35B3 /* SBJsonStreamParserAccumulator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParserAccumulator.m; sourceTree = ""; }; + C56D4CEC1814E3AE007C35B3 /* SBJsonStreamParserAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParserAdapter.h; sourceTree = ""; }; + C56D4CED1814E3AE007C35B3 /* SBJsonStreamParserAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParserAdapter.m; sourceTree = ""; }; + C56D4CEE1814E3AE007C35B3 /* SBJsonStreamParserState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParserState.h; sourceTree = ""; }; + C56D4CEF1814E3AE007C35B3 /* SBJsonStreamParserState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParserState.m; sourceTree = ""; }; + C56D4CF01814E3AE007C35B3 /* SBJsonStreamTokeniser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamTokeniser.h; sourceTree = ""; }; + C56D4CF11814E3AE007C35B3 /* SBJsonStreamTokeniser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamTokeniser.m; sourceTree = ""; }; + C56D4CF21814E3AE007C35B3 /* SBJsonStreamWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamWriter.h; sourceTree = ""; }; + C56D4CF31814E3AE007C35B3 /* SBJsonStreamWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamWriter.m; sourceTree = ""; }; + C56D4CF41814E3AE007C35B3 /* SBJsonStreamWriterAccumulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamWriterAccumulator.h; sourceTree = ""; }; + C56D4CF51814E3AE007C35B3 /* SBJsonStreamWriterAccumulator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamWriterAccumulator.m; sourceTree = ""; }; + C56D4CF61814E3AE007C35B3 /* SBJsonStreamWriterState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamWriterState.h; sourceTree = ""; }; + C56D4CF71814E3AE007C35B3 /* SBJsonStreamWriterState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamWriterState.m; sourceTree = ""; }; + C56D4CF81814E3AE007C35B3 /* SBJsonWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonWriter.h; sourceTree = ""; }; + C56D4CF91814E3AE007C35B3 /* SBJsonWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonWriter.m; sourceTree = ""; }; + C56D4CFA1814E3AE007C35B3 /* SocketIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SocketIO.h; sourceTree = ""; }; + C56D4CFB1814E3AE007C35B3 /* SocketIO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SocketIO.m; sourceTree = ""; }; + C56D4CFC1814E3AE007C35B3 /* SocketIOJSONSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SocketIOJSONSerialization.h; sourceTree = ""; }; + C56D4CFD1814E3AE007C35B3 /* SocketIOJSONSerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SocketIOJSONSerialization.m; sourceTree = ""; }; + C56D4CFE1814E3AE007C35B3 /* SocketIOPacket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SocketIOPacket.h; sourceTree = ""; }; + C56D4CFF1814E3AE007C35B3 /* SocketIOPacket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SocketIOPacket.m; sourceTree = ""; }; + C56D4D001814E3AE007C35B3 /* SocketIOTransport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SocketIOTransport.h; sourceTree = ""; }; + C56D4D011814E3AE007C35B3 /* SocketIOTransportWebsocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SocketIOTransportWebsocket.h; sourceTree = ""; }; + C56D4D021814E3AE007C35B3 /* SocketIOTransportWebsocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SocketIOTransportWebsocket.m; sourceTree = ""; }; + C56D4D031814E3AE007C35B3 /* SocketIOTransportXHR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SocketIOTransportXHR.h; sourceTree = ""; }; + C56D4D041814E3AE007C35B3 /* SocketIOTransportXHR.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SocketIOTransportXHR.m; sourceTree = ""; }; + C56D4D061814E3AE007C35B3 /* base64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = base64.c; sourceTree = ""; }; + C56D4D071814E3AE007C35B3 /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base64.h; sourceTree = ""; }; + C56D4D081814E3AE007C35B3 /* NSData+SRB64Additions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+SRB64Additions.h"; sourceTree = ""; }; + C56D4D091814E3AE007C35B3 /* NSData+SRB64Additions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+SRB64Additions.m"; sourceTree = ""; }; + C56D4D0A1814E3AE007C35B3 /* SocketRocket-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SocketRocket-Prefix.pch"; sourceTree = ""; }; + C56D4D0B1814E3AE007C35B3 /* SRWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRWebSocket.h; sourceTree = ""; }; + C56D4D0C1814E3AE007C35B3 /* SRWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRWebSocket.m; sourceTree = ""; }; + C56D4D1F1814E3ED007C35B3 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + C56D4D211814E3F5007C35B3 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; + C56D4D231814E3FB007C35B3 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; + C56D4D251814E3FF007C35B3 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; + C56D4D271814E41A007C35B3 /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = usr/lib/libicucore.dylib; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + C56D4C281814E221007C35B3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C56D4D281814E41A007C35B3 /* libicucore.dylib in Frameworks */, + C56D4D261814E400007C35B3 /* CFNetwork.framework in Frameworks */, + C56D4D241814E3FB007C35B3 /* MobileCoreServices.framework in Frameworks */, + C56D4D221814E3F5007C35B3 /* SystemConfiguration.framework in Frameworks */, + C56D4D201814E3ED007C35B3 /* Security.framework in Frameworks */, + C56D4C311814E221007C35B3 /* CoreGraphics.framework in Frameworks */, + C56D4C331814E221007C35B3 /* UIKit.framework in Frameworks */, + C56D4C2F1814E221007C35B3 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C56D4C491814E221007C35B3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C56D4C4E1814E221007C35B3 /* XCTest.framework in Frameworks */, + C56D4C501814E222007C35B3 /* UIKit.framework in Frameworks */, + C56D4C4F1814E222007C35B3 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + C56D4C221814E221007C35B3 = { + isa = PBXGroup; + children = ( + C56D4C341814E221007C35B3 /* socketIOexample */, + C56D4C531814E222007C35B3 /* socketIOexampleTests */, + C56D4C2D1814E221007C35B3 /* Frameworks */, + C56D4C2C1814E221007C35B3 /* Products */, + ); + sourceTree = ""; + }; + C56D4C2C1814E221007C35B3 /* Products */ = { + isa = PBXGroup; + children = ( + C56D4C2B1814E221007C35B3 /* socketIOexample.app */, + C56D4C4C1814E221007C35B3 /* socketIOexampleTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + C56D4C2D1814E221007C35B3 /* Frameworks */ = { + isa = PBXGroup; + children = ( + C56D4D271814E41A007C35B3 /* libicucore.dylib */, + C56D4D251814E3FF007C35B3 /* CFNetwork.framework */, + C56D4D231814E3FB007C35B3 /* MobileCoreServices.framework */, + C56D4D211814E3F5007C35B3 /* SystemConfiguration.framework */, + C56D4D1F1814E3ED007C35B3 /* Security.framework */, + C56D4C2E1814E221007C35B3 /* Foundation.framework */, + C56D4C301814E221007C35B3 /* CoreGraphics.framework */, + C56D4C321814E221007C35B3 /* UIKit.framework */, + C56D4C4D1814E221007C35B3 /* XCTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + C56D4C341814E221007C35B3 /* socketIOexample */ = { + isa = PBXGroup; + children = ( + C56D4CE31814E3AE007C35B3 /* socketio.objc */, + C56D4C3D1814E221007C35B3 /* AppDelegate.h */, + C56D4C3E1814E221007C35B3 /* AppDelegate.m */, + C56D4C401814E221007C35B3 /* Main.storyboard */, + C56D4C431814E221007C35B3 /* ViewController.h */, + C56D4C441814E221007C35B3 /* ViewController.m */, + C56D4C461814E221007C35B3 /* Images.xcassets */, + C56D4C351814E221007C35B3 /* Supporting Files */, + ); + path = socketIOexample; + sourceTree = ""; + }; + C56D4C351814E221007C35B3 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + C56D4C361814E221007C35B3 /* socketIOexample-Info.plist */, + C56D4C371814E221007C35B3 /* InfoPlist.strings */, + C56D4C3A1814E221007C35B3 /* main.m */, + C56D4C3C1814E221007C35B3 /* socketIOexample-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + C56D4C531814E222007C35B3 /* socketIOexampleTests */ = { + isa = PBXGroup; + children = ( + C56D4C591814E222007C35B3 /* socketIOexampleTests.m */, + C56D4C541814E222007C35B3 /* Supporting Files */, + ); + path = socketIOexampleTests; + sourceTree = ""; + }; + C56D4C541814E222007C35B3 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + C56D4C551814E222007C35B3 /* socketIOexampleTests-Info.plist */, + C56D4C561814E222007C35B3 /* InfoPlist.strings */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + C56D4CE31814E3AE007C35B3 /* socketio.objc */ = { + isa = PBXGroup; + children = ( + C56D4CE41814E3AE007C35B3 /* JSON-Framework */, + C56D4CFA1814E3AE007C35B3 /* SocketIO.h */, + C56D4CFB1814E3AE007C35B3 /* SocketIO.m */, + C56D4CFC1814E3AE007C35B3 /* SocketIOJSONSerialization.h */, + C56D4CFD1814E3AE007C35B3 /* SocketIOJSONSerialization.m */, + C56D4CFE1814E3AE007C35B3 /* SocketIOPacket.h */, + C56D4CFF1814E3AE007C35B3 /* SocketIOPacket.m */, + C56D4D001814E3AE007C35B3 /* SocketIOTransport.h */, + C56D4D011814E3AE007C35B3 /* SocketIOTransportWebsocket.h */, + C56D4D021814E3AE007C35B3 /* SocketIOTransportWebsocket.m */, + C56D4D031814E3AE007C35B3 /* SocketIOTransportXHR.h */, + C56D4D041814E3AE007C35B3 /* SocketIOTransportXHR.m */, + C56D4D051814E3AE007C35B3 /* SocketRocket */, + ); + path = socketio.objc; + sourceTree = SOURCE_ROOT; + }; + C56D4CE41814E3AE007C35B3 /* JSON-Framework */ = { + isa = PBXGroup; + children = ( + C56D4CE51814E3AE007C35B3 /* SBJson.h */, + C56D4CE61814E3AE007C35B3 /* SBJsonParser.h */, + C56D4CE71814E3AE007C35B3 /* SBJsonParser.m */, + C56D4CE81814E3AE007C35B3 /* SBJsonStreamParser.h */, + C56D4CE91814E3AE007C35B3 /* SBJsonStreamParser.m */, + C56D4CEA1814E3AE007C35B3 /* SBJsonStreamParserAccumulator.h */, + C56D4CEB1814E3AE007C35B3 /* SBJsonStreamParserAccumulator.m */, + C56D4CEC1814E3AE007C35B3 /* SBJsonStreamParserAdapter.h */, + C56D4CED1814E3AE007C35B3 /* SBJsonStreamParserAdapter.m */, + C56D4CEE1814E3AE007C35B3 /* SBJsonStreamParserState.h */, + C56D4CEF1814E3AE007C35B3 /* SBJsonStreamParserState.m */, + C56D4CF01814E3AE007C35B3 /* SBJsonStreamTokeniser.h */, + C56D4CF11814E3AE007C35B3 /* SBJsonStreamTokeniser.m */, + C56D4CF21814E3AE007C35B3 /* SBJsonStreamWriter.h */, + C56D4CF31814E3AE007C35B3 /* SBJsonStreamWriter.m */, + C56D4CF41814E3AE007C35B3 /* SBJsonStreamWriterAccumulator.h */, + C56D4CF51814E3AE007C35B3 /* SBJsonStreamWriterAccumulator.m */, + C56D4CF61814E3AE007C35B3 /* SBJsonStreamWriterState.h */, + C56D4CF71814E3AE007C35B3 /* SBJsonStreamWriterState.m */, + C56D4CF81814E3AE007C35B3 /* SBJsonWriter.h */, + C56D4CF91814E3AE007C35B3 /* SBJsonWriter.m */, + ); + path = "JSON-Framework"; + sourceTree = ""; + }; + C56D4D051814E3AE007C35B3 /* SocketRocket */ = { + isa = PBXGroup; + children = ( + C56D4D061814E3AE007C35B3 /* base64.c */, + C56D4D071814E3AE007C35B3 /* base64.h */, + C56D4D081814E3AE007C35B3 /* NSData+SRB64Additions.h */, + C56D4D091814E3AE007C35B3 /* NSData+SRB64Additions.m */, + C56D4D0A1814E3AE007C35B3 /* SocketRocket-Prefix.pch */, + C56D4D0B1814E3AE007C35B3 /* SRWebSocket.h */, + C56D4D0C1814E3AE007C35B3 /* SRWebSocket.m */, + ); + path = SocketRocket; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + C56D4C2A1814E221007C35B3 /* socketIOexample */ = { + isa = PBXNativeTarget; + buildConfigurationList = C56D4C5D1814E222007C35B3 /* Build configuration list for PBXNativeTarget "socketIOexample" */; + buildPhases = ( + C56D4C271814E221007C35B3 /* Sources */, + C56D4C281814E221007C35B3 /* Frameworks */, + C56D4C291814E221007C35B3 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = socketIOexample; + productName = socketIOexample; + productReference = C56D4C2B1814E221007C35B3 /* socketIOexample.app */; + productType = "com.apple.product-type.application"; + }; + C56D4C4B1814E221007C35B3 /* socketIOexampleTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = C56D4C601814E222007C35B3 /* Build configuration list for PBXNativeTarget "socketIOexampleTests" */; + buildPhases = ( + C56D4C481814E221007C35B3 /* Sources */, + C56D4C491814E221007C35B3 /* Frameworks */, + C56D4C4A1814E221007C35B3 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + C56D4C521814E222007C35B3 /* PBXTargetDependency */, + ); + name = socketIOexampleTests; + productName = socketIOexampleTests; + productReference = C56D4C4C1814E221007C35B3 /* socketIOexampleTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + C56D4C231814E221007C35B3 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0500; + ORGANIZATIONNAME = saturngod; + TargetAttributes = { + C56D4C4B1814E221007C35B3 = { + TestTargetID = C56D4C2A1814E221007C35B3; + }; + }; + }; + buildConfigurationList = C56D4C261814E221007C35B3 /* Build configuration list for PBXProject "socketIOexample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = C56D4C221814E221007C35B3; + productRefGroup = C56D4C2C1814E221007C35B3 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + C56D4C2A1814E221007C35B3 /* socketIOexample */, + C56D4C4B1814E221007C35B3 /* socketIOexampleTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + C56D4C291814E221007C35B3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C56D4C471814E221007C35B3 /* Images.xcassets in Resources */, + C56D4C391814E221007C35B3 /* InfoPlist.strings in Resources */, + C56D4C421814E221007C35B3 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C56D4C4A1814E221007C35B3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C56D4C581814E222007C35B3 /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + C56D4C271814E221007C35B3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C56D4D1D1814E3AE007C35B3 /* NSData+SRB64Additions.m in Sources */, + C56D4C451814E221007C35B3 /* ViewController.m in Sources */, + C56D4D161814E3AE007C35B3 /* SBJsonWriter.m in Sources */, + C56D4D181814E3AE007C35B3 /* SocketIOJSONSerialization.m in Sources */, + C56D4D101814E3AE007C35B3 /* SBJsonStreamParserAdapter.m in Sources */, + C56D4D191814E3AE007C35B3 /* SocketIOPacket.m in Sources */, + C56D4D0D1814E3AE007C35B3 /* SBJsonParser.m in Sources */, + C56D4D1C1814E3AE007C35B3 /* base64.c in Sources */, + C56D4D171814E3AE007C35B3 /* SocketIO.m in Sources */, + C56D4D131814E3AE007C35B3 /* SBJsonStreamWriter.m in Sources */, + C56D4D121814E3AE007C35B3 /* SBJsonStreamTokeniser.m in Sources */, + C56D4D0F1814E3AE007C35B3 /* SBJsonStreamParserAccumulator.m in Sources */, + C56D4C3F1814E221007C35B3 /* AppDelegate.m in Sources */, + C56D4D111814E3AE007C35B3 /* SBJsonStreamParserState.m in Sources */, + C56D4D141814E3AE007C35B3 /* SBJsonStreamWriterAccumulator.m in Sources */, + C56D4D151814E3AE007C35B3 /* SBJsonStreamWriterState.m in Sources */, + C56D4C3B1814E221007C35B3 /* main.m in Sources */, + C56D4D0E1814E3AE007C35B3 /* SBJsonStreamParser.m in Sources */, + C56D4D1E1814E3AE007C35B3 /* SRWebSocket.m in Sources */, + C56D4D1A1814E3AE007C35B3 /* SocketIOTransportWebsocket.m in Sources */, + C56D4D1B1814E3AE007C35B3 /* SocketIOTransportXHR.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C56D4C481814E221007C35B3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C56D4C5A1814E222007C35B3 /* socketIOexampleTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + C56D4C521814E222007C35B3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C56D4C2A1814E221007C35B3 /* socketIOexample */; + targetProxy = C56D4C511814E222007C35B3 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + C56D4C371814E221007C35B3 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + C56D4C381814E221007C35B3 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + C56D4C401814E221007C35B3 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + C56D4C411814E221007C35B3 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + C56D4C561814E222007C35B3 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + C56D4C571814E222007C35B3 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + C56D4C5B1814E222007C35B3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + C56D4C5C1814E222007C35B3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + C56D4C5E1814E222007C35B3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "socketIOexample/socketIOexample-Prefix.pch"; + INFOPLIST_FILE = "socketIOexample/socketIOexample-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 6.1; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + C56D4C5F1814E222007C35B3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "socketIOexample/socketIOexample-Prefix.pch"; + INFOPLIST_FILE = "socketIOexample/socketIOexample-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 6.1; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + C56D4C611814E222007C35B3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; + BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/socketIOexample.app/socketIOexample"; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "socketIOexample/socketIOexample-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "socketIOexampleTests/socketIOexampleTests-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = Debug; + }; + C56D4C621814E222007C35B3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; + BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/socketIOexample.app/socketIOexample"; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "socketIOexample/socketIOexample-Prefix.pch"; + INFOPLIST_FILE = "socketIOexampleTests/socketIOexampleTests-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C56D4C261814E221007C35B3 /* Build configuration list for PBXProject "socketIOexample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C56D4C5B1814E222007C35B3 /* Debug */, + C56D4C5C1814E222007C35B3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C56D4C5D1814E222007C35B3 /* Build configuration list for PBXNativeTarget "socketIOexample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C56D4C5E1814E222007C35B3 /* Debug */, + C56D4C5F1814E222007C35B3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C56D4C601814E222007C35B3 /* Build configuration list for PBXNativeTarget "socketIOexampleTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C56D4C611814E222007C35B3 /* Debug */, + C56D4C621814E222007C35B3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = C56D4C231814E221007C35B3 /* Project object */; +} diff --git a/socketio.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/socketIOexample/socketIOexample.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 68% rename from socketio.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to socketIOexample/socketIOexample.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 9a95c5a..f20aab7 100644 --- a/socketio.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/socketIOexample/socketIOexample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:socketIOexample.xcodeproj"> diff --git a/socketIOexample/socketIOexample.xcodeproj/project.xcworkspace/xcuserdata/lusi.xcuserdatad/UserInterfaceState.xcuserstate b/socketIOexample/socketIOexample.xcodeproj/project.xcworkspace/xcuserdata/lusi.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..18a6462cc0809cbe6368977025e0f424ea6ee4dc GIT binary patch literal 16915 zcmc(G30zcV*Z8yCVHpM(5QYI1Muwf)H$cr~5kpZ?aKnXngey#r49*NHsrB6U%*sqF zGfi+WD>EzGQ`>B@Tr!t7ZF#kQ(bBT|pF5WUMygW zx4K$fFmwf~M8nYtGzLvT6Hz_l z(G285Zq$nApsUb)bSqknZbP@DJJ1qzCt8Z`MJv#1v;l2Io6u(TAlibqqbJal=xOvO zdJDac_M&&tyJ#QUj}D>t(J}M^`UHK3PNGxj8}tJ@hknHbQ_Nu*4#QD67Wcu)xF1f# zI;_VAY{VvP#&+z$h4?Z&7+;P@;!$`suEt~VSX_f^@kBfYPsL8`!*lUGyZ|r60=^dC zh;PET;>CCgz7sFUcjHdH7O%tW@dmsRZ^E1L6ZlE|6yAYf#;@R4@oV^Xya&I5-^B0Z z!}th3iod}B#wYP9dC$fIN{d5ml$kCUg!GvrzF9C?YnMcyU*$a~}vIZBR^kI5(GG&w_lCcluga%dMCYy-b+{02k2({Abp5#pfxvw7J~}P$ZJ0D5OBKD1O4I8pBM!-B$?2-jD@jq7QrH!oJBDOi)Jwoq70-( znJ5coqa2ir@=!j?XR%Dlrm@G_4)!cN#y()z!@o2juhh#sXSRA=Eq-4~gWomB>E|2k zl_}-?3}>6$KeW;1_jqgk(CVx3c*l*c^3^Mo%K7PSO--(rrU|Z=M$g<*zS%j)&v6 zvc=Cgc>@f-5JJzO8rZz{=^m#Swlq5g&;`WSc-p)Td>O2!UYYxM15129-e2bQJKdh9 zAm^&kD5UE^BUw@h8qHEz+5t2c)TIX1qH$=vs4&xES0}@_mv;ljb9jbWGE>(pE%WsA zL>B4++5n@m+~O#yD7FmM+los~dRtj(p}yE&Y|)q5%FTskg^rTqvasp+%_=(a_-ZySk4G2Lenu?}LKPQf=867wlgS7q&O&4i(veXXL!1{{2m|VlVVLSN7 z;C5Ac8UlRv?GLDZ6KY<=`bl`NL^EMMC0?(yy%SxDrb(@{P)ohC�P_ZGPu;H(%}o zMe?*bz3pq!EaXAcri`lTX$w*^8~H>^yiC)9{467sYwZCJ^{-btXSm!vu*MBExtl#c zzj2%oRL1CT^SO*=p4N6_mo)i|uF*9{pQm9a@2?!){ez$96gBO_XG4JPtsscTAUzAv zbx5}XEkswNYY;;Mx|V5KCd*>kEQjT8K-Z%i(2eLObThhz<*|HL$gW^x*jSN+x`4z4 zSCk_Ls+-9=3ARn{xxCBVHKgIvA~Ef;rv z87t^OcQc*Hy}0u6wKd|B$HS5<{rs#hxw{Xo>O?Ei{Y=lS%n=0e0O~~XooEf}U`@n4MXwwL=zu(re;VXvef}eF&(_TJ)5tFhNMqpj}9}5j~5Z zL(iiZ(2M9Lw38LFVm5#cWP{jcZ16_38@-HPL9e3M(CcUqyPOSSC9IqcWfg3g$TxMF z-BE5UvlN+&%(kHxi`irm+g&Z5x&dN>FiI|j5oFf^bP(y*z@bwu*`&kh zXa_pNN?944)m5&RnVM2KZN2T92`+!Lrph(l>-4&K5G2u9euzE-3)E$tK&aP3%f|r= z2@gRG$I%H1L$Kj<^o7(ge!fYkF)a+Xe~G@5+9gW9Ma>=PJ66g19YjB(pU`Qr{AbY5 zs2!c1*9Fol!+E?}k~Owr`VE*A*wG#kN&v;M4FTe2hJE9cGymjzM$hWwSODY<83qd2;W%~k5z~MNeUfC~1<1e)LYjGr&qiO$X z7?^rctqUi2(T){3I)sl{iQ_^W60izP08V7J9XN@N4>A0>Z@}=6t5?qc?-+ig=wKP# z0n0zq>1r|fx?P0e3es^FI6_#1`{N9(#hGjZo5&`yIyQMd&PJIy6X)T4HU+%N$*i7D z4>>|d^0TJ%-X41b)_$Pq?~Rv&RB$0hugA;#d|+5h+Wa23v)$9?AJ+=5Od}6R*{oJS z3<@$AjH6_~L584nj;m3$Y}(4w9>=D}?`>=Fw|RNd)`3sw7d1;9rK&6~bGFQJ`a(Mb zQj0Ahwb;t0vVL_#hYi*HM(fQDZAuhmK@lDxZc#D&R|g)*oPi~Z%NWt(ncFhD&F^-# z1n^C8HTs*iLtFvN*FAd(t`Hemf=h83F2_Sz18ZbFo57ma<6-y;T#1L{5v-ZH*p+N1 zo5j-V>O7uV70w3OipgbQ!Fg}MG(eQl#n19uPd9pLQPm!wOLQ_uI$OaZ7uAAqto5{3 z@pE{0a1t;-Q}TW){}zDu^6#YuOqp-BnGANF#_F&H^&OAH<9q2lo+NUvj=6zzb0*g5 z-ShO#a~gttn}+K{Wqmrn63jeqz>S#4GjJ1b#xCYzvzd>zvAL|B&EJS;VmF?JTd)VW z;@NB=yMf)p?qti^3bra#H|7L%1J*nA?{uRaw#e;qHu}1J9+#)W%z{0P;EYO8$Qo`GqU4c8U&Z=(-mLP|y2cTXzW4?+75j>c76zA>xPu4dOT#)Nfv6pBDOxybzQa_g(^#zCmg;Hzf(Y&z{49PBKhGAiTiIfE+dBLrehI+sVz;w9 z0Nj%QTV8x~=-clutDA2z!7?1hg|G~3kwvGmo4c3s7T$MBuI$GLU>WbRrR*+P#>g})(!0#{^XKOIgU>Nw9nYx>>hx2uLSMnGv~<4iQ-1t z95x`$W+{ThpxgJuU*hjB3G93P0|5Jp-N#k}u=^!or~JQs`oQG*c83|ZsnBL!SO+_1 zasa@hZtBiq@boSrzJC%F5Z~480RRZ%8xW((vnRYgL8Q)79E3o^NhBQAB!YEx5II{L za$=C!fZlcf_bg52EN2t%gJ>E8Ti)*r2Gb;-BttMw`j7;YNK_<=sM$KUo^4GMqiho)XE3gA`mjMdJz$CWEhWre2z!WigHA z#e^yR$hGNREe*|iwJvK>FzhE|NR7z*vFzy%Qp=uUX;Wc&Vu%X$U8;+^YF&OeFYWmZv}zq`BF)4_u4FH<*Vvov-R|55nMG!cw6+irX=OXv zF1C9u@e&{LvzOT`>{XH0?2Dj>kOoJR#|xQbi0}V?U@7<|HGC_aZ?N(8%DlfHE#VmN zbhq)^A(F3*#%RU`k_SOXFC@Z6#9vFUBiFOn*&g-=n6M#AY3kSkn!nz!MdbF2z}!KW zkUQC1>}|Fezy!VZ2BgU%NVA{ZjiSgs4KC0wP)Ji%N5*f#2ok?maTtu>{@uO-gK)YaMnaAz%9_*KvF4y$W&_a8S>na|w z&ZuxZn|$yrO1pMQw#37@Xr);90M)N*Yj8C>hj~Raq%ZZj8~11D!B)%x| zezF}j@CovyDDPsXE0EB-_L+Uh*|>^39S}j!Wyjg4EJ?JY z-BlOzPQdZ`=)dRq{7q;{4hE2a@_&=V>fynW02wF#4@IZQ|wFj75kcfvx$5~zDD`vTNFb+hbnPC`<8tN z{`L1DZl|C^3>D#NpeRz6wZaSb7HS62Fxm@wH@>mEj9u%T-s364k)PZ>sj{U43g*5B zFVDA(3{(>bi+#cRcgV#0q7u8QxV+S%FEkYu>1`H=MPE`tH zg0?^=T&J0DwuwKB3X2vll+^SONQ;rrfs6gRwuBO@K)TJ8QjW@K7!9WpG?L0`6#Iew z$bMp{*%|gT`-Pol=Qh)5ltE*ulE%?^_?3V%*{`r2zX^DvfTs!gb`~q(6_9XEn+&T1 z%Bq~xc{hX#@Kn?6nL7%~?@gj3BRYjrio6730jO}n^QGA^&hLVXQ@c2;@)* z2w{K7{y~W!auJt+Ad)1(hZ2M+P%y0m%9zzoe>3=pP(p@i#Tl&L`>FydU_gGcO$i7$ z&7eAwlUkZdvuHNWp}91V=Fs65x9OJaZ=OO)N|zl?l(Xx96V|%;(%j8x~;%_d+&*eJJ>El zI!H@sDJ?@$v>esa3OWq@3(;?o_$V%AR6z5Rn)nu1TUYfm5W>v!OFimVDH9~o+^b8# z2~hdPVFFG%L@ViVz%hbW(UE|soK}-Y`WHEBgZS9$ra4 zV}Sl@r_0+(XNum!-;N0&#)!^fAk+$pj8-~Bz+f`~_l4E41Az+NPt;G_P_2OD1RT%M z|DK;9t-PH=0`^i>v`#u-T=1p3q?rrp)o_8}!VT+TV5qQl(rZL;|9E_e&{F}#z8X7F zEp`Y{t^;9JF({0~RWN!Zn0s0Qn$mSKgf61DN?vBS(1h5^P~GgLi$$_Tr}Uo;msWl| zy+bU0{pTs2bcqQ4p9~G2RZHnI$S%;k1f1MKmkSu~@IZc7G7nwm`LC>25X1_)QUr0I zfKxl@{Q?v+(`v;6W3Yb>U5Db=(hk~5*9y3wfYSt=zLu^BBfU|;8qqEniTCC5La+vI zp4Gtd*3uOC*?o-$P8LY z2QevRX;Z|rPKEOIgC;-n>cQMe!FW&gnk7ALY}70xl4+j$JQcJ+p|h z%{Pa>OT6xqIqcqM{3typnQ;Nz1Z)giU1=LW5pC#kdV+o`V3UB&0=D$Bp#g46w(J7S z_O)o)tpD7yP0@=f&vI8|6_f=3@g=xkp%nFtzC0L8iSA!T@d3xInDqlsV7_!U0vy7k zBJc+tMc@wxnffQkL28>r9OeiIe#t-q4-)WY0v^1MlW}2WBNrjy%LQD{4heW9OPg55 z&j?%rl2Vu59zeGTQ737l=$lL$>uPEa4DQ0l#c~NEHYFFw#dC1H4H0mOfJ+5jwhj{M znWzC}fO|g;ZoGllQ?E=N5BEdF!#PkL7d;H$Sm)e8f(!I2CA7CouWA|~^9grrLOMmC ztp;u}f-~6Qb+v*IDy9HKh6JFCx)7u_o$C*#g3}0iXa@&s7N}1B#Vsf<8*V{yIb5!Q zhY5Iicdn2t;0(R5B5_6z#OMkES29aCC$&SMt}FfpG26MKkV(ZH_<|z@T*WLsEQMLE zA+s*$N1IoT+hnJaT7y`oW#`$xJJOWKqQ!d$)IS5+-M3RvYwkBGOK}W6!3Tf zPY{i2EX?ceLTZOhc`0PJi<{ZAC2j#v3KHoNiEQO&3mAx)(nF-5o7)>5dW>slpyiXn zKn%Gy0J@v$3psYd&}+q^Q^lbkO$Zq`a<__7d=qywcMG>j!1V(Dmw>^|n!b)(%-zP_ z&fOv41_93#@InD!EpCK#)hl=hw>x+-bo*@JK1rE;;DLBew`WLzBe0dh!ew_)@CMMu zP^H>uaI5d;RzYfuyNA1%TfyDOt>oa`1FrJ|1|!uZ;AR24HgK!C2e>sTpX)?$R$M7y z;H6hIrgOx28;pp!r@g8uKGB00qo%H)6mB|;SD>LGNT(F7h=I9u3p!}+C0T@{a%hXF z+F|o}MGzG3cSNzfm0lntHlE=8pHEIw9b>J*YnCx-QZM5uD<_5!d-8 z_Z9cGfZGLpm4N53<-QdSwSX4{>@=`Rtg*mWG|UvMF@Y;no|XWQ#oC<|l8$b1w~H6S zp;x@A8i?e?w+j-=ONZ4qL#)_1+B>F=Z{tM+r6m7ATY@y6l@W-!xpUmF+;7}@?sx7F z?oSz#L6m)sfSG^=c*{n>*9jN|`33>sDBznmqkI`BlgYy1J3dIi{H12QXnpGsMqW9UMOf6$-Y?KgS5Qc*$US(VYKM_iEZ5i9k`GnP7540rR`EE zDi#2PDkCX@c-~1B~H4t1wU-$T7UQb)=rR2_G>PiL4)PcHYz{bl` zd;LyBtJZX7b!Gi~wP*IOgUWJbd7@xy#KTbr2IGz{ODxl)RcmDinNbGk+!6tUuYPy8 zIhI*vj@~v_RwyeH@SOr)Dk`U#ChE2VVg^Awq;*JdyDPg)HY8+vi3~i4WddFf*NS1X zbmT}gZ)G7fhslPA%p4)B67W3&zBh=tI}sFuxL!Hof94{)@xr7Ln3oIdDzX|`?YMvk z9w@COLvE-5-gz5*-OFFKED}(oyhQS+4Be;?9*3uZRh8xFCfcKSdg}0NQ$Ph_;Bi5$zH4BNj$n z6Cp(09m5b`E2=B@@wRa<#)>Ok}sFvC%<35TE0f! zA%9T*xO|sJ#IeK68!RSNLN1~5Ke;EB`^w-hf zMt>jE7&AL&LCn=LEauvn>tk+=xijXjnB_6|#H@%}8M7+p>6l+)qhigm!((e=$Hz{L zt&8Pjn_~U3b7R|M7sOs2%VO`3T_3wG_W9TsV|T{xj(sKewb=c!2V)P#9*#X4`$6nS zv8Q6sDElfM%Bjj3N}rM`1?9EM>y@`DmnfGimnk1mZc#q0d_=ib`MmNiCPS~4pEa9_+FA`2Be3|fd!kL6$ z63!+3mhgMRpNSEPeG>a6<|kSc?TLkn#fbwG%M&XSuSgu8Sd};`(UrI)adqOWiSHyH zP5dnJhs0kK&nNz&LMoz)Qbns`RdK35szg(uTh*qzMzvVATy>9Xg=(d0m1=`(lj=d$7S$uFt*WP0uc+QteW?0EbyD@E>TA`v zs$W#+RKKZySN)lUljKS9N$E)$NtsDGNqI>HNkvHmk_IIWPTG|8MAELLmy=#idOhil zq_>jxCLK%qFzMr@<4K<;eV+91q*F=f)hczK+O95Bk5SK7U!|U}UZ`HAzD<3HdZ~Ju z`fl|m^<(O7>h0?1)w|WNtM{tkS07d%RsUOkQhiGOmHMpuocdSw`Q*stm}F&gd~#B9 za&l^NzhrZAW%B9db17jdkttCrF)7NF_!LcwCFSyzij*r-hNo1e)TE3{nUFFmWpc{x zDVtMvrM#JPFy&Cn;gq8(AEbPg@>R-@DW_9@PC1+MXDUvmsj}4YR8#7-)J3Uxr7llh znYt>JfL zp)Xtm=&MUxoOXNKJ!$LHwxm6t_EOs0Y44=%OFNMEUfTOt}+p~=*kHCByXQ>f|CJfhjE*{0dvKexZVe_{XP{sa53?Z37Ew*K4u zKbfJ<$jWeJ6lDy^7?d$MV@O76MtMd>#)OPX8Iv=nX4GdmGa54ZjHV1%#=MOCGj?R0 z)8=Z2YiDQ$?Mm%B?MCfp?L*of+BdatYv0lC(;m>ir+r_0M0-s8q4s<2kJ{7PpS9<- z=e2*rHFqvEEHffAAu}h_p2=s<%iNgxOy-`<6PahSNLFH2QdV+SYF582O_nw*J1Z|s zmvwnoY1YuJVOf<~BeF(ijmfIb8lN>Wt08Ma)}pMAtSwo)vOdo`m#xU|mu<>=Zws$&Z)^6mvd9j zlAL>T?#o$~vnHoA=b@Y@bDqw5F6W(`!#Tg?oX<_o&CJct&C9jq+H;F?hv!bnt;?O7 zTc5ipcYp55+;8$|o+7U>Z%|%S9?QEf?}oga^A_dZm3MdEioBJ1>+<&I9mxAVKP+FD zZ_b~P-HAIDc#YAj{@(n3`3Ljg&;Ka@c>bsP zU*w-GNGQ-1loyOFs4l1}7*{a8V0MAOU~a)x1q%yU!F2^U7A!Bgw_s(#>Vl4fbp;y> z9xT{W@N~f|1)mh0)+u!c-2mNqU4zc8YtgmpygI*bp^oXU)7_xES+_`cmu|IglkR!l zOS;{Ut%f?rYdyH=x_Zr_feqj9Ac*6LZ@dxA2#jenYmNoEQ+#hFq} zIVPRSU^1EPrXtfo(`Ba7rimur)NGn*YB9|=`AiE;g6VqGO{QB+cbit4R+%0!tut*f zZ8kk*+HTrude!v0=?&96rhTS^rbDKWO<$YNn9iDhGyP%4X1O`qtTgvAtITTiKy#hB z#eBPYrFoP2A@d{V$IRQ!Pnn-Fzh-{J{I>aB^M3Pt=J(A<%-@^;w8UBLmPX5UmL-yNf*o8DG#tFXCjb8NTR7TfNy zEwwGT-D_KETW#yGt+PF4d)oG#?M2%z+sn4sY;V}!vb|&5XZzarn?2U9wOj1N?bGa6 z+3&D#us>?wYTssm*8ZIR1^a9E*X?iE57-ad57|Gqe_}sj|IYrS{fzyr{WtsXjxa~G zL+R+_P&w3&EJwaW=P)>|4u_-IG0-u}F~QO3a5-i=W;wi$Hpe{2RgRk-cRE%();iWZ rHafOA9(8PUY + + diff --git a/socketio.xcodeproj/xcuserdata/htainlinshwe.xcuserdatad/xcschemes/socketio.xcscheme b/socketIOexample/socketIOexample.xcodeproj/xcuserdata/lusi.xcuserdatad/xcschemes/socketIOexample.xcscheme similarity index 61% rename from socketio.xcodeproj/xcuserdata/htainlinshwe.xcuserdatad/xcschemes/socketio.xcscheme rename to socketIOexample/socketIOexample.xcodeproj/xcuserdata/lusi.xcuserdatad/xcschemes/socketIOexample.xcscheme index c68ff0d..4d6003b 100644 --- a/socketio.xcodeproj/xcuserdata/htainlinshwe.xcuserdatad/xcschemes/socketio.xcscheme +++ b/socketIOexample/socketIOexample.xcodeproj/xcuserdata/lusi.xcuserdatad/xcschemes/socketIOexample.xcscheme @@ -1,5 +1,6 @@ + BlueprintIdentifier = "C56D4C2A1814E221007C35B3" + BuildableName = "socketIOexample.app" + BlueprintName = "socketIOexample" + ReferencedContainer = "container:socketIOexample.xcodeproj"> @@ -27,14 +28,24 @@ shouldUseLaunchSchemeArgsEnv = "YES" buildConfiguration = "Debug"> + + + + + BlueprintIdentifier = "C56D4C2A1814E221007C35B3" + BuildableName = "socketIOexample.app" + BlueprintName = "socketIOexample" + ReferencedContainer = "container:socketIOexample.xcodeproj"> @@ -50,10 +61,10 @@ + BlueprintIdentifier = "C56D4C2A1814E221007C35B3" + BuildableName = "socketIOexample.app" + BlueprintName = "socketIOexample" + ReferencedContainer = "container:socketIOexample.xcodeproj"> @@ -68,10 +79,10 @@ + BlueprintIdentifier = "C56D4C2A1814E221007C35B3" + BuildableName = "socketIOexample.app" + BlueprintName = "socketIOexample" + ReferencedContainer = "container:socketIOexample.xcodeproj"> diff --git a/socketio.xcodeproj/xcuserdata/htainlinshwe.xcuserdatad/xcschemes/xcschememanagement.plist b/socketIOexample/socketIOexample.xcodeproj/xcuserdata/lusi.xcuserdatad/xcschemes/xcschememanagement.plist similarity index 71% rename from socketio.xcodeproj/xcuserdata/htainlinshwe.xcuserdatad/xcschemes/xcschememanagement.plist rename to socketIOexample/socketIOexample.xcodeproj/xcuserdata/lusi.xcuserdatad/xcschemes/xcschememanagement.plist index 3251531..446cad4 100644 --- a/socketio.xcodeproj/xcuserdata/htainlinshwe.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/socketIOexample/socketIOexample.xcodeproj/xcuserdata/lusi.xcuserdatad/xcschemes/xcschememanagement.plist @@ -4,7 +4,7 @@ SchemeUserState - socketio.xcscheme + socketIOexample.xcscheme orderHint 0 @@ -12,7 +12,12 @@ SuppressBuildableAutocreation - ADB6917D142F5DF10095DB55 + C56D4C2A1814E221007C35B3 + + primary + + + C56D4C4B1814E221007C35B3 primary diff --git a/socketIOexample/socketIOexample/AppDelegate.h b/socketIOexample/socketIOexample/AppDelegate.h new file mode 100644 index 0000000..b634ff2 --- /dev/null +++ b/socketIOexample/socketIOexample/AppDelegate.h @@ -0,0 +1,15 @@ +// +// AppDelegate.h +// socketIOexample +// +// Created by Htain Lin Shwe on 21/10/13. +// Copyright (c) 2013 saturngod. All rights reserved. +// + +#import + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +@end diff --git a/socketIOexample/socketIOexample/AppDelegate.m b/socketIOexample/socketIOexample/AppDelegate.m new file mode 100644 index 0000000..5f63f14 --- /dev/null +++ b/socketIOexample/socketIOexample/AppDelegate.m @@ -0,0 +1,46 @@ +// +// AppDelegate.m +// socketIOexample +// +// Created by Htain Lin Shwe on 21/10/13. +// Copyright (c) 2013 saturngod. All rights reserved. +// + +#import "AppDelegate.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + // Override point for customization after application launch. + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application +{ + // 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. + // 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. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application +{ + // 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. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application +{ + // 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. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application +{ + // 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. +} + +- (void)applicationWillTerminate:(UIApplication *)application +{ + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. +} + +@end diff --git a/socketIOexample/socketIOexample/Base.lproj/Main.storyboard b/socketIOexample/socketIOexample/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f1acb38 --- /dev/null +++ b/socketIOexample/socketIOexample/Base.lproj/Main.storyboard @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/socketIOexample/socketIOexample/Images.xcassets/AppIcon.appiconset/Contents.json b/socketIOexample/socketIOexample/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..a396706 --- /dev/null +++ b/socketIOexample/socketIOexample/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/socketIOexample/socketIOexample/Images.xcassets/LaunchImage.launchimage/Contents.json b/socketIOexample/socketIOexample/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000..c79ebd3 --- /dev/null +++ b/socketIOexample/socketIOexample/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/socketIOexample/socketIOexample/ViewController.h b/socketIOexample/socketIOexample/ViewController.h new file mode 100644 index 0000000..b6ac633 --- /dev/null +++ b/socketIOexample/socketIOexample/ViewController.h @@ -0,0 +1,18 @@ +// +// ViewController.h +// socketIOexample +// +// Created by Htain Lin Shwe on 21/10/13. +// Copyright (c) 2013 saturngod. All rights reserved. +// + +#import +#import "SocketIO.h" +@interface ViewController : UIViewController +@property (nonatomic,strong) SocketIO* socketIO; + +@property (nonatomic,assign) IBOutlet UITextView* chatLog; +@property (nonatomic,assign) IBOutlet UITextField* chatbox; + +-(IBAction)sendMsg:(id)sender; +@end diff --git a/socketIOexample/socketIOexample/ViewController.m b/socketIOexample/socketIOexample/ViewController.m new file mode 100644 index 0000000..19d96a3 --- /dev/null +++ b/socketIOexample/socketIOexample/ViewController.m @@ -0,0 +1,80 @@ +// +// ViewController.m +// socketIOexample +// +// Created by Htain Lin Shwe on 21/10/13. +// Copyright (c) 2013 saturngod. All rights reserved. +// + +#import "ViewController.h" +#import "SocketIOPacket.h" +@interface ViewController () + +@end + +@implementation ViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + _socketIO = [[SocketIO alloc] initWithDelegate:self]; + [_socketIO connectToHost:@"localhost" onPort:3000]; + [_socketIO sendEvent:@"join" withData:@"iOSuser"]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +-(IBAction)sendMsg:(id)sender { + [_socketIO sendEvent:@"message" withData:_chatbox.text]; + [self addNewEventWithNickName:@"iOSuser" AndMessage:_chatbox.text]; + _chatbox.text = @""; + +} + +-(void)addNewEventWithNickName:(NSString*)nickname AndMessage:(NSString*)message +{ + _chatLog.text = [_chatLog.text stringByAppendingFormat:@"%@ - %@\n",nickname,message]; + + + +} +# pragma mark - +# pragma mark socket.IO-objc delegate methods + +- (void) socketIODidConnect:(SocketIO *)socket +{ + NSLog(@"socket.io connected."); +} + +- (void) socketIO:(SocketIO *)socket didReceiveEvent:(SocketIOPacket *)packet +{ + NSLog(@"didReceiveEvent()"); + + if([packet.name isEqualToString:@"message"]) + { + NSArray* args = packet.args; + NSDictionary* arg = args[0]; + + [self addNewEventWithNickName:arg[@"nickname"] AndMessage:arg[@"message"]]; + + } +} + +- (void) socketIO:(SocketIO *)socket onError:(NSError *)error +{ + NSLog(@"onError() %@", error); +} + + +- (void) socketIODidDisconnect:(SocketIO *)socket disconnectedWithError:(NSError *)error +{ + NSLog(@"socket.io disconnected. did error occur? %@", error); +} + + +@end diff --git a/socketio/en.lproj/InfoPlist.strings b/socketIOexample/socketIOexample/en.lproj/InfoPlist.strings similarity index 100% rename from socketio/en.lproj/InfoPlist.strings rename to socketIOexample/socketIOexample/en.lproj/InfoPlist.strings diff --git a/socketIOexample/socketIOexample/main.m b/socketIOexample/socketIOexample/main.m new file mode 100644 index 0000000..043b2bf --- /dev/null +++ b/socketIOexample/socketIOexample/main.m @@ -0,0 +1,18 @@ +// +// main.m +// socketIOexample +// +// Created by Htain Lin Shwe on 21/10/13. +// Copyright (c) 2013 saturngod. All rights reserved. +// + +#import + +#import "AppDelegate.h" + +int main(int argc, char * argv[]) +{ + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/socketio/socketio-Info.plist b/socketIOexample/socketIOexample/socketIOexample-Info.plist similarity index 83% rename from socketio/socketio-Info.plist rename to socketIOexample/socketIOexample/socketIOexample-Info.plist index e6e4b90..14b2c9e 100644 --- a/socketio/socketio-Info.plist +++ b/socketIOexample/socketIOexample/socketIOexample-Info.plist @@ -8,10 +8,8 @@ ${PRODUCT_NAME} CFBundleExecutable ${EXECUTABLE_NAME} - CFBundleIconFile - CFBundleIdentifier - com.comquas.${PRODUCT_NAME:rfc1034identifier} + com.saturngod.${PRODUCT_NAME:rfc1034identifier} CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -26,8 +24,12 @@ 1.0 LSRequiresIPhoneOS - NSMainNibFile - MainWindow + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + UISupportedInterfaceOrientations UIInterfaceOrientationPortrait diff --git a/socketIOexample/socketIOexample/socketIOexample-Prefix.pch b/socketIOexample/socketIOexample/socketIOexample-Prefix.pch new file mode 100644 index 0000000..82a2bb4 --- /dev/null +++ b/socketIOexample/socketIOexample/socketIOexample-Prefix.pch @@ -0,0 +1,16 @@ +// +// Prefix header +// +// The contents of this file are implicitly included at the beginning of every source file. +// + +#import + +#ifndef __IPHONE_5_0 +#warning "This project uses features only available in iOS SDK 5.0 and later." +#endif + +#ifdef __OBJC__ + #import + #import +#endif diff --git a/socketIOexample/socketIOexampleTests/en.lproj/InfoPlist.strings b/socketIOexample/socketIOexampleTests/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/socketIOexample/socketIOexampleTests/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/socketIOexample/socketIOexampleTests/socketIOexampleTests-Info.plist b/socketIOexample/socketIOexampleTests/socketIOexampleTests-Info.plist new file mode 100644 index 0000000..d0e4b1b --- /dev/null +++ b/socketIOexample/socketIOexampleTests/socketIOexampleTests-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.saturngod.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/socketIOexample/socketIOexampleTests/socketIOexampleTests.m b/socketIOexample/socketIOexampleTests/socketIOexampleTests.m new file mode 100644 index 0000000..b417807 --- /dev/null +++ b/socketIOexample/socketIOexampleTests/socketIOexampleTests.m @@ -0,0 +1,34 @@ +// +// socketIOexampleTests.m +// socketIOexampleTests +// +// Created by Htain Lin Shwe on 21/10/13. +// Copyright (c) 2013 saturngod. All rights reserved. +// + +#import + +@interface socketIOexampleTests : XCTestCase + +@end + +@implementation socketIOexampleTests + +- (void)setUp +{ + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown +{ + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample +{ + XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); +} + +@end diff --git a/JSON/NSObject+SBJson.m b/socketIOexample/socketio.objc/JSON-Framework/SBJson.h old mode 100644 new mode 100755 similarity index 67% rename from JSON/NSObject+SBJson.m rename to socketIOexample/socketio.objc/JSON-Framework/SBJson.h index 9d6396d..58b9acb --- a/JSON/NSObject+SBJson.m +++ b/socketIOexample/socketio.objc/JSON-Framework/SBJson.h @@ -1,20 +1,20 @@ /* - Copyright (C) 2009 Stig Brautaset. All rights reserved. - + Copyright (C) 2009-2011 Stig Brautaset. All rights reserved. + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - + * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -27,32 +27,10 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#import "NSObject+SBJson.h" -#import "SBJsonWriter.h" #import "SBJsonParser.h" +#import "SBJsonWriter.h" +#import "SBJsonStreamParser.h" +#import "SBJsonStreamParserAdapter.h" +#import "SBJsonStreamWriter.h" +#import "SBJsonStreamTokeniser.h" -@implementation NSObject (NSObject_SBJsonWriting) - -- (NSString *)JSONRepresentation { - SBJsonWriter *writer = [[[SBJsonWriter alloc] init] autorelease]; - NSString *json = [writer stringWithObject:self]; - if (!json) - NSLog(@"-JSONRepresentation failed. Error is: %@", writer.error); - return json; -} - -@end - - - -@implementation NSString (NSString_SBJsonParsing) - -- (id)JSONValue { - SBJsonParser *parser = [[[SBJsonParser alloc] init] autorelease]; - id repr = [parser objectWithString:self]; - if (!repr) - NSLog(@"-JSONValue failed. Error is: %@", parser.error); - return repr; -} - -@end diff --git a/JSON/SBJsonParser.h b/socketIOexample/socketio.objc/JSON-Framework/SBJsonParser.h old mode 100644 new mode 100755 similarity index 73% rename from JSON/SBJsonParser.h rename to socketIOexample/socketio.objc/JSON-Framework/SBJsonParser.h index 751122f..ddfff03 --- a/JSON/SBJsonParser.h +++ b/socketIOexample/socketio.objc/JSON-Framework/SBJsonParser.h @@ -30,18 +30,16 @@ #import /** - @brief Parse JSON Strings and NSData objects + Parse JSON Strings and NSData objects This uses SBJsonStreamParser internally. - @see @ref objc2json - */ @interface SBJsonParser : NSObject /** - @brief The maximum recursing depth. + The maximum recursing depth. Defaults to 32. If the input is nested deeper than this the input will be deemed to be malicious and the parser returns nil, signalling an error. ("Nested too deep".) You can @@ -50,7 +48,7 @@ @property NSUInteger maxDepth; /** - @brief Description of parse error + Description of parse error This method returns the trace of the last method that failed. You need to check the return value of the call you're making to figure out @@ -62,10 +60,10 @@ @property(copy) NSString *error; /** - @brief Return the object represented by the given NSData object. + Return the object represented by the given NSData object. The data *must* be UTF8 encoded. - + @param data An NSData containing UTF8 encoded data to parse. @return The NSArray or NSDictionary represented by the object, or nil if an error occured. @@ -73,28 +71,15 @@ - (id)objectWithData:(NSData*)data; /** - @brief Return the object represented by the given string + Parse string and return the represented dictionary or array. - This method converts its input to an NSData object containing UTF8 and calls -objectWithData: with it. + Calls objectWithData: internally. - @return The NSArray or NSDictionary represented by the object, or nil if an error occured. - */ -- (id)objectWithString:(NSString *)repr; - -/** - @brief Return the object represented by the given string - - This method calls objectWithString: internally. If an error occurs, and if @p error - is not nil, it creates an NSError object and returns this through its second argument. - - @param jsonText the json string to parse - @param error pointer to an NSError object to populate on error + @param string An NSString containing JSON text. @return The NSArray or NSDictionary represented by the object, or nil if an error occured. */ - -- (id)objectWithString:(NSString*)jsonText - error:(NSError**)error; +- (id)objectWithString:(NSString *)string; @end diff --git a/JSON/SBJsonParser.m b/socketIOexample/socketio.objc/JSON-Framework/SBJsonParser.m old mode 100644 new mode 100755 similarity index 73% rename from JSON/SBJsonParser.m rename to socketIOexample/socketio.objc/JSON-Framework/SBJsonParser.m index 6f2e26b..15e2823 --- a/JSON/SBJsonParser.m +++ b/socketIOexample/socketio.objc/JSON-Framework/SBJsonParser.m @@ -27,6 +27,10 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#if !__has_feature(objc_arc) +#error "This source file must be compiled with ARC enabled!" +#endif + #import "SBJsonParser.h" #import "SBJsonStreamParser.h" #import "SBJsonStreamParserAdapter.h" @@ -44,10 +48,6 @@ - (id)init { return self; } -- (void)dealloc { - [error release]; - [super dealloc]; -} #pragma mark Methods @@ -58,12 +58,12 @@ - (id)objectWithData:(NSData *)data { return nil; } - SBJsonStreamParserAccumulator *accumulator = [[[SBJsonStreamParserAccumulator alloc] init] autorelease]; + SBJsonStreamParserAccumulator *accumulator = [[SBJsonStreamParserAccumulator alloc] init]; - SBJsonStreamParserAdapter *adapter = [[[SBJsonStreamParserAdapter alloc] init] autorelease]; + SBJsonStreamParserAdapter *adapter = [[SBJsonStreamParserAdapter alloc] init]; adapter.delegate = accumulator; - SBJsonStreamParser *parser = [[[SBJsonStreamParser alloc] init] autorelease]; + SBJsonStreamParser *parser = [[SBJsonStreamParser alloc] init]; parser.maxDepth = self.maxDepth; parser.delegate = adapter; @@ -84,21 +84,8 @@ - (id)objectWithData:(NSData *)data { return nil; } -- (id)objectWithString:(NSString *)repr { - return [self objectWithData:[repr dataUsingEncoding:NSUTF8StringEncoding]]; -} - -- (id)objectWithString:(NSString*)repr error:(NSError**)error_ { - id tmp = [self objectWithString:repr]; - if (tmp) - return tmp; - - if (error_) { - NSDictionary *ui = [NSDictionary dictionaryWithObjectsAndKeys:error, NSLocalizedDescriptionKey, nil]; - *error_ = [NSError errorWithDomain:@"org.brautaset.SBJsonParser.ErrorDomain" code:0 userInfo:ui]; - } - - return nil; +- (id)objectWithString:(NSString *)string { + return [self objectWithData:[string dataUsingEncoding:NSUTF8StringEncoding]]; } @end diff --git a/JSON/SBJsonStreamParser.h b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParser.h old mode 100644 new mode 100755 similarity index 68% rename from JSON/SBJsonStreamParser.h rename to socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParser.h index bced473..930df49 --- a/JSON/SBJsonStreamParser.h +++ b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParser.h @@ -1,22 +1,22 @@ /* Copyright (c) 2010, Stig Brautaset. All rights reserved. - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - + Neither the name of the the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A @@ -32,7 +32,6 @@ #import -@class SBJsonTokeniser; @class SBJsonStreamParser; @class SBJsonStreamParserState; @@ -44,8 +43,8 @@ typedef enum { /** - @brief Delegate for interacting directly with the stream parser - + Delegate for interacting directly with the stream parser + You will most likely find it much more convenient to implement the SBJsonStreamParserAdapterDelegate protocol instead. */ @@ -82,61 +81,75 @@ typedef enum { /** - @brief Parse a stream of JSON data. - + Parse a stream of JSON data. + Using this class directly you can reduce the apparent latency for each download/parse cycle of documents over a slow connection. You can start parsing *and return chunks of the parsed document* before the entire document is downloaded. - + Using this class is also useful to parse huge documents on disk - bit by bit so you don't have to keep them all in memory. - - @see SBJsonStreamParserAdapter for more information. - - @see @ref objc2json + bit by bit so you don't have to keep them all in memory. + + JSON is mapped to Objective-C types in the following way: + + - null -> NSNull + - string -> NSString + - array -> NSMutableArray + - object -> NSMutableDictionary + - true -> NSNumber's -numberWithBool:YES + - false -> NSNumber's -numberWithBool:NO + - number -> NSNumber + + Since Objective-C doesn't have a dedicated class for boolean values, + these turns into NSNumber instances. However, since these are + initialised with the -initWithBool: method they round-trip back to JSON + properly. In other words, they won't silently suddenly become 0 or 1; + they'll be represented as 'true' and 'false' again. + + Integers are parsed into either a `long long` or `unsigned long long` + type if they fit, else a `double` is used. All real & exponential numbers + are represented using a `double`. Previous versions of this library used + an NSDecimalNumber in some cases, but this is no longer the case. + See also SBJsonStreamParserAdapter for more information. + */ -@interface SBJsonStreamParser : NSObject { -@private - SBJsonTokeniser *tokeniser; -} +@interface SBJsonStreamParser : NSObject -@property (nonatomic, assign) SBJsonStreamParserState *state; // Private -@property (nonatomic, readonly, retain) NSMutableArray *stateStack; // Private +@property (nonatomic, unsafe_unretained) SBJsonStreamParserState *state; // Private +@property (nonatomic, readonly, strong) NSMutableArray *stateStack; // Private /** - @brief Expect multiple documents separated by whitespace + Expect multiple documents separated by whitespace - Normally the @p -parse: method returns SBJsonStreamParserComplete when it's found a complete JSON document. + Normally the -parse: method returns SBJsonStreamParserComplete when it's found a complete JSON document. Attempting to parse any more data at that point is considered an error. ("Garbage after JSON".) - + If you set this property to true the parser will never return SBJsonStreamParserComplete. Rather, once an object is completed it will expect another object to immediately follow, separated only by (optional) whitespace. - @see The TweetStream app in the Examples */ @property BOOL supportMultipleDocuments; /** - @brief Delegate to receive messages + Delegate to receive messages The object set here receives a series of messages as the parser breaks down the JSON stream into valid tokens. - @note Usually this should be an instance of SBJsonStreamParserAdapter, but you can - substitute your own implementation of the SBJsonStreamParserDelegate protocol if you need to. + substitute your own implementation of the SBJsonStreamParserDelegate protocol if you need to. */ -@property (assign) id delegate; +@property (unsafe_unretained) id delegate; /** - @brief The max parse depth - + The max parse depth + If the input is nested deeper than this the parser will halt parsing and return an error. - Defaults to 32. + Defaults to 32. */ @property NSUInteger maxDepth; @@ -144,17 +157,17 @@ typedef enum { @property (copy) NSString *error; /** - @brief Parse some JSON - + Parse some JSON + The JSON is assumed to be UTF8 encoded. This can be a full JSON document, or a part of one. @param data An NSData object containing the next chunk of JSON - @return - @li SBJsonStreamParserComplete if a full document was found - @li SBJsonStreamParserWaitingForData if a partial document was found and more data is required to complete it - @li SBJsonStreamParserError if an error occured. (See the error property for details in this case.) - + @return + - SBJsonStreamParserComplete if a full document was found + - SBJsonStreamParserWaitingForData if a partial document was found and more data is required to complete it + - SBJsonStreamParserError if an error occured. (See the error property for details in this case.) + */ - (SBJsonStreamParserStatus)parse:(NSData*)data; diff --git a/JSON/SBJsonStreamParser.m b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParser.m old mode 100644 new mode 100755 similarity index 56% rename from JSON/SBJsonStreamParser.m rename to socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParser.m index 19aaf0c..ba2f748 --- a/JSON/SBJsonStreamParser.m +++ b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParser.m @@ -30,12 +30,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#if !__has_feature(objc_arc) +#error "This source file must be compiled with ARC enabled!" +#endif + #import "SBJsonStreamParser.h" -#import "SBJsonTokeniser.h" +#import "SBJsonStreamTokeniser.h" #import "SBJsonStreamParserState.h" -#import -@implementation SBJsonStreamParser +#define SBStringIsSurrogateHighCharacter(character) ((character >= 0xD800UL) && (character <= 0xDBFFUL)) + +@implementation SBJsonStreamParser { + SBJsonStreamTokeniser *tokeniser; +} @synthesize supportMultipleDocuments; @synthesize error; @@ -52,41 +59,35 @@ - (id)init { maxDepth = 32u; stateStack = [[NSMutableArray alloc] initWithCapacity:maxDepth]; state = [SBJsonStreamParserStateStart sharedInstance]; - tokeniser = [[SBJsonTokeniser alloc] init]; + tokeniser = [[SBJsonStreamTokeniser alloc] init]; } return self; } -- (void)dealloc { - self.error = nil; - self.state = nil; - [stateStack release]; - [tokeniser release]; - [super dealloc]; -} #pragma mark Methods - (NSString*)tokenName:(sbjson_token_t)token { switch (token) { - case sbjson_token_array_start: + case sbjson_token_array_open: return @"start of array"; break; - case sbjson_token_array_end: + case sbjson_token_array_close: return @"end of array"; break; - case sbjson_token_number: + case sbjson_token_integer: + case sbjson_token_real: return @"number"; break; - case sbjson_token_string: + case sbjson_token_string: + case sbjson_token_encoded: return @"string"; break; - case sbjson_token_true: - case sbjson_token_false: + case sbjson_token_bool: return @"boolean"; break; @@ -94,19 +95,19 @@ - (NSString*)tokenName:(sbjson_token_t)token { return @"null"; break; - case sbjson_token_keyval_separator: + case sbjson_token_entry_sep: return @"key-value separator"; break; - case sbjson_token_separator: + case sbjson_token_value_sep: return @"value separator"; break; - case sbjson_token_object_start: + case sbjson_token_object_open: return @"start of object"; break; - case sbjson_token_object_end: + case sbjson_token_object_close: return @"end of object"; break; @@ -119,7 +120,7 @@ - (NSString*)tokenName:(sbjson_token_t)token { } - (void)maxDepthError { - self.error = [NSString stringWithFormat:@"Input depth exceeds max depth of %lu", maxDepth]; + self.error = [NSString stringWithFormat:@"Input depth exceeds max depth of %lu", (unsigned long)maxDepth]; self.state = [SBJsonStreamParserStateError sharedInstance]; } @@ -168,8 +169,7 @@ - (void) handleTokenNotExpectedHere: (sbjson_token_t) tok { } - (SBJsonStreamParserStatus)parse:(NSData *)data_ { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - @try { + @autoreleasepool { [tokeniser appendData:data_]; for (;;) { @@ -177,8 +177,10 @@ - (SBJsonStreamParserStatus)parse:(NSData *)data_ { if ([state isError]) return SBJsonStreamParserError; - NSObject *token; - sbjson_token_t tok = [tokeniser getToken:&token]; + char *token; + NSUInteger token_len; + sbjson_token_t tok = [tokeniser getToken:&token length:&token_len]; + switch (tok) { case sbjson_token_eof: return [state parserShouldReturn:self]; @@ -198,55 +200,78 @@ - (SBJsonStreamParserStatus)parse:(NSData *)data_ { } switch (tok) { - case sbjson_token_object_start: + case sbjson_token_object_open: [self handleObjectStart]; break; - case sbjson_token_object_end: + case sbjson_token_object_close: [self handleObjectEnd: tok]; break; - case sbjson_token_array_start: + case sbjson_token_array_open: [self handleArrayStart]; break; - case sbjson_token_array_end: + case sbjson_token_array_close: [self handleArrayEnd: tok]; break; - case sbjson_token_separator: - case sbjson_token_keyval_separator: - [state parser:self shouldTransitionTo:tok]; - break; - - case sbjson_token_true: - [delegate parser:self foundBoolean:YES]; + case sbjson_token_value_sep: + case sbjson_token_entry_sep: [state parser:self shouldTransitionTo:tok]; break; - case sbjson_token_false: - [delegate parser:self foundBoolean:NO]; + case sbjson_token_bool: + [delegate parser:self foundBoolean:token[0] == 't']; [state parser:self shouldTransitionTo:tok]; break; + case sbjson_token_null: [delegate parserFoundNull:self]; [state parser:self shouldTransitionTo:tok]; break; - - case sbjson_token_number: - [delegate parser:self foundNumber:(NSNumber*)token]; + + case sbjson_token_integer: { + const int UNSIGNED_LONG_LONG_MAX_DIGITS = 20; + if (token_len <= UNSIGNED_LONG_LONG_MAX_DIGITS) { + if (*token == '-') + [delegate parser:self foundNumber: @(strtoll(token, NULL, 10))]; + else + [delegate parser:self foundNumber: @(strtoull(token, NULL, 10))]; + + [state parser:self shouldTransitionTo:tok]; + break; + } + } + // FALLTHROUGH + + case sbjson_token_real: { + [delegate parser:self foundNumber: @(strtod(token, NULL))]; [state parser:self shouldTransitionTo:tok]; break; - - case sbjson_token_string: + } + + case sbjson_token_string: { + NSString *string = [[NSString alloc] initWithBytes:token length:token_len encoding:NSUTF8StringEncoding]; if ([state needKey]) - [delegate parser:self foundObjectKey:(NSString*)token]; + [delegate parser:self foundObjectKey:string]; else - [delegate parser:self foundString:(NSString*)token]; + [delegate parser:self foundString:string]; [state parser:self shouldTransitionTo:tok]; break; - + } + + case sbjson_token_encoded: { + NSString *string = [self decodeStringToken:token length:token_len]; + if ([state needKey]) + [delegate parser:self foundObjectKey:string]; + else + [delegate parser:self foundString:string]; + [state parser:self shouldTransitionTo:tok]; + break; + } + default: break; } @@ -254,11 +279,60 @@ - (SBJsonStreamParserStatus)parse:(NSData *)data_ { } } return SBJsonStreamParserComplete; + } +} +- (unichar)decodeHexQuad:(char *)quad { + unichar ch = 0; + for (NSUInteger i = 0; i < 4; i++) { + int c = quad[i]; + ch *= 16; + switch (c) { + case '0' ... '9': ch += c - '0'; break; + case 'a' ... 'f': ch += 10 + c - 'a'; break; + case 'A' ... 'F': ch += 10 + c - 'A'; break; + default: @throw @"FUT FUT FUT"; + } } - @finally { - [pool drain]; + return ch; +} + +- (NSString*)decodeStringToken:(char*)bytes length:(NSUInteger)len { + NSMutableString *string = [NSMutableString stringWithCapacity:len]; + + for (NSUInteger i = 0; i < len;) { + switch (bytes[i]) { + case '\\': { + switch (bytes[++i]) { + case '"': [string appendString:@"\""]; i++; break; + case '/': [string appendString:@"/"]; i++; break; + case '\\': [string appendString:@"\\"]; i++; break; + case 'b': [string appendString:@"\b"]; i++; break; + case 'f': [string appendString:@"\f"]; i++; break; + case 'n': [string appendString:@"\n"]; i++; break; + case 'r': [string appendString:@"\r"]; i++; break; + case 't': [string appendString:@"\t"]; i++; break; + case 'u': { + unichar hi = [self decodeHexQuad:bytes + i + 1]; + i += 5; + if (SBStringIsSurrogateHighCharacter(hi)) { + // Skip past \u that we know is there.. + unichar lo = [self decodeHexQuad:bytes + i + 2]; + i += 6; + [string appendFormat:@"%C%C", hi, lo]; + } else { + [string appendFormat:@"%C", hi]; + } + break; + } + default: @throw @"FUT FUT FUT"; + } + break; + } + default: [string appendFormat:@"%c", bytes[i++]]; break; + } } + return string; } @end diff --git a/JSON/SBJsonStreamParserAccumulator.h b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParserAccumulator.h old mode 100644 new mode 100755 similarity index 100% rename from JSON/SBJsonStreamParserAccumulator.h rename to socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParserAccumulator.h diff --git a/JSON/SBJsonStreamParserAccumulator.m b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParserAccumulator.m old mode 100644 new mode 100755 similarity index 93% rename from JSON/SBJsonStreamParserAccumulator.m rename to socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParserAccumulator.m index 72716da..82d8fe8 --- a/JSON/SBJsonStreamParserAccumulator.m +++ b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParserAccumulator.m @@ -27,25 +27,25 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#if !__has_feature(objc_arc) +#error "This source file must be compiled with ARC enabled!" +#endif + #import "SBJsonStreamParserAccumulator.h" @implementation SBJsonStreamParserAccumulator @synthesize value; -- (void)dealloc { - [value release]; - [super dealloc]; -} #pragma mark SBJsonStreamParserAdapterDelegate - (void)parser:(SBJsonStreamParser*)parser foundArray:(NSArray *)array { - value = [array retain]; + value = array; } - (void)parser:(SBJsonStreamParser*)parser foundObject:(NSDictionary *)dict { - value = [dict retain]; + value = dict; } @end diff --git a/JSON/SBJsonStreamParserAdapter.h b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParserAdapter.h old mode 100644 new mode 100755 similarity index 70% rename from JSON/SBJsonStreamParserAdapter.h rename to socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParserAdapter.h index 6431cca..8b4bc5d --- a/JSON/SBJsonStreamParserAdapter.h +++ b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParserAdapter.h @@ -40,14 +40,13 @@ typedef enum { } SBJsonStreamParserAdapterType; /** - @brief Delegate for getting objects & arrays from the stream parser adapter - - @see The TweetStream example project. + Delegate for getting objects & arrays from the stream parser adapter + */ @protocol SBJsonStreamParserAdapterDelegate /** - @brief Called if a JSON array is found + Called if a JSON array is found This method is called if a JSON array is found. @@ -55,7 +54,7 @@ typedef enum { - (void)parser:(SBJsonStreamParser*)parser foundArray:(NSArray*)array; /** - @brief Called when a JSON object is found + Called when a JSON object is found This method is called if a JSON object is found. */ @@ -64,57 +63,53 @@ typedef enum { @end /** - @brief SBJsonStreamParserDelegate protocol adapter - + SBJsonStreamParserDelegate protocol adapter + Rather than implementing the SBJsonStreamParserDelegate protocol yourself you will most likely find it much more convenient to use an instance of this class and implement the SBJsonStreamParserAdapterDelegate protocol instead. - + The default behaviour is that the delegate only receives one call from either the -parser:foundArray: or -parser:foundObject: method when the document is fully parsed. However, if your inputs contains multiple JSON documents and you set the parser's -supportMultipleDocuments property to YES you will get one call for each full method. - - @code - SBJsonStreamParserAdapter *adapter = [[[SBJsonStreamParserAdapter alloc] init] autorelease]; - adapter.delegate = self; - - SBJsonStreamParser *parser = [[[SBJsonStreamParser alloc] init] autorelease]; - parser.delegate = adapter; - parser.supportMultipleDocuments = YES; - - // Note that this input contains multiple top-level JSON documents - NSData *json = [@"[]{}[]{}" dataWithEncoding:NSUTF8StringEncoding]; - [parser parse:data]; - @endcode - - In the above example @p self will have the following sequence of methods called on it: - - @li -parser:foundArray: - @li -parser:foundObject: - @li -parser:foundArray: - @li -parser:foundObject: + + SBJsonStreamParserAdapter *adapter = [[[SBJsonStreamParserAdapter alloc] init] autorelease]; + adapter.delegate = self; + + SBJsonStreamParser *parser = [[[SBJsonStreamParser alloc] init] autorelease]; + parser.delegate = adapter; + parser.supportMultipleDocuments = YES; + + // Note that this input contains multiple top-level JSON documents + NSData *json = [@"[]{}[]{}" dataWithEncoding:NSUTF8StringEncoding]; + [parser parse:data]; + + In the above example self will have the following sequence of methods called on it: + + - -parser:foundArray: + - -parser:foundObject: + - -parser:foundArray: + - -parser:foundObject: Often you won't have control over the input you're parsing, so can't make use of - this feature. But, all is not lost: this class will let you get the same effect by + this feature. But, all is not lost: this class will let you get the same effect by allowing you to skip one or more of the outer enclosing objects. Thus, the next example results in the same sequence of -parser:foundArray: / -parser:foundObject: being called on your delegate. - - @code - SBJsonStreamParserAdapter *adapter = [[[SBJsonStreamParserAdapter alloc] init] autorelease]; - adapter.delegate = self; - adapter.levelsToSkip = 1; - - SBJsonStreamParser *parser = [[[SBJsonStreamParser alloc] init] autorelease]; - parser.delegate = adapter; - - // Note that this input contains A SINGLE top-level document - NSData *json = [@"[[],{},[],{}]" dataWithEncoding:NSUTF8StringEncoding]; - [parser parse:data]; - @endcode - + + SBJsonStreamParserAdapter *adapter = [[[SBJsonStreamParserAdapter alloc] init] autorelease]; + adapter.delegate = self; + adapter.levelsToSkip = 1; + + SBJsonStreamParser *parser = [[[SBJsonStreamParser alloc] init] autorelease]; + parser.delegate = adapter; + + // Note that this input contains A SINGLE top-level document + NSData *json = [@"[[],{},[],{}]" dataWithEncoding:NSUTF8StringEncoding]; + [parser parse:data]; + */ @interface SBJsonStreamParserAdapter : NSObject { @private @@ -128,21 +123,20 @@ typedef enum { } /** - @brief How many levels to skip + How many levels to skip This is useful for parsing huge JSON documents, or documents coming in over a very slow link. If you set this to N it will skip the outer N levels and call the -parser:foundArray: or -parser:foundObject: methods for each of the inner objects, as appropriate. - - @see The StreamParserIntegrationTest.m file for examples + */ @property NSUInteger levelsToSkip; /** - @brief Your delegate object + Your delegate object Set this to the object you want to receive the SBJsonStreamParserAdapterDelegate messages. */ -@property (assign) id delegate; +@property (unsafe_unretained) id delegate; @end diff --git a/JSON/SBJsonStreamParserAdapter.m b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParserAdapter.m old mode 100644 new mode 100755 similarity index 94% rename from JSON/SBJsonStreamParserAdapter.m rename to socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParserAdapter.m index 003dceb..7259a06 --- a/JSON/SBJsonStreamParserAdapter.m +++ b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParserAdapter.m @@ -30,6 +30,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#if !__has_feature(objc_arc) +#error "This source file must be compiled with ARC enabled!" +#endif + #import "SBJsonStreamParserAdapter.h" @interface SBJsonStreamParserAdapter () @@ -59,11 +63,6 @@ - (id)init { return self; } -- (void)dealloc { - [keyStack release]; - [stack release]; - [super dealloc]; -} #pragma mark Private methods @@ -116,7 +115,7 @@ - (void)parser:(SBJsonStreamParser*)parser found:(id)obj { - (void)parserFoundObjectStart:(SBJsonStreamParser*)parser { if (++depth > self.levelsToSkip) { - dict = [[NSMutableDictionary new] autorelease]; + dict = [NSMutableDictionary new]; [stack addObject:dict]; currentType = SBJsonStreamParserAdapterObject; } @@ -128,16 +127,15 @@ - (void)parser:(SBJsonStreamParser*)parser foundObjectKey:(NSString*)key_ { - (void)parserFoundObjectEnd:(SBJsonStreamParser*)parser { if (depth-- > self.levelsToSkip) { - id value = [dict retain]; + id value = dict; [self pop]; [self parser:parser found:value]; - [value release]; } } - (void)parserFoundArrayStart:(SBJsonStreamParser*)parser { if (++depth > self.levelsToSkip) { - array = [[NSMutableArray new] autorelease]; + array = [NSMutableArray new]; [stack addObject:array]; currentType = SBJsonStreamParserAdapterArray; } @@ -145,10 +143,9 @@ - (void)parserFoundArrayStart:(SBJsonStreamParser*)parser { - (void)parserFoundArrayEnd:(SBJsonStreamParser*)parser { if (depth-- > self.levelsToSkip) { - id value = [array retain]; + id value = array; [self pop]; [self parser:parser found:value]; - [value release]; } } diff --git a/JSON/SBJsonStreamParserState.h b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParserState.h old mode 100644 new mode 100755 similarity index 98% rename from JSON/SBJsonStreamParserState.h rename to socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParserState.h index ea893cb..35b3f24 --- a/JSON/SBJsonStreamParserState.h +++ b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParserState.h @@ -32,7 +32,7 @@ #import -#import "SBJsonTokeniser.h" +#import "SBJsonStreamTokeniser.h" #import "SBJsonStreamParser.h" @interface SBJsonStreamParserState : NSObject diff --git a/JSON/SBJsonStreamParserState.m b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParserState.m old mode 100644 new mode 100755 similarity index 84% rename from JSON/SBJsonStreamParserState.m rename to socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParserState.m index a24c6f6..9481f97 --- a/JSON/SBJsonStreamParserState.m +++ b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamParserState.m @@ -30,13 +30,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#if !__has_feature(objc_arc) +#error "This source file must be compiled with ARC enabled!" +#endif + #import "SBJsonStreamParserState.h" -#import "SBJsonStreamParser.h" #define SINGLETON \ + (id)sharedInstance { \ - static id state; \ - if (!state) state = [[self alloc] init]; \ + static id state = nil; \ + if (!state) { \ + @synchronized(self) { \ + if (!state) state = [[self alloc] init]; \ + } \ + } \ return state; \ } @@ -75,23 +82,23 @@ @implementation SBJsonStreamParserStateStart SINGLETON - (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { - return token == sbjson_token_array_start || token == sbjson_token_object_start; + return token == sbjson_token_array_open || token == sbjson_token_object_open; } - (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { SBJsonStreamParserState *state = nil; switch (tok) { - case sbjson_token_array_start: + case sbjson_token_array_open: state = [SBJsonStreamParserStateArrayStart sharedInstance]; break; - case sbjson_token_object_start: + case sbjson_token_object_open: state = [SBJsonStreamParserStateObjectStart sharedInstance]; break; - case sbjson_token_array_end: - case sbjson_token_object_end: + case sbjson_token_array_close: + case sbjson_token_object_close: if (parser.supportMultipleDocuments) state = parser.state; else @@ -156,8 +163,9 @@ - (NSString*)name { return @"at beginning of object"; } - (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { switch (token) { - case sbjson_token_object_end: + case sbjson_token_object_close: case sbjson_token_string: + case sbjson_token_encoded: return YES; break; default: @@ -185,7 +193,7 @@ @implementation SBJsonStreamParserStateObjectGotKey - (NSString*)name { return @"after object key"; } - (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { - return token == sbjson_token_keyval_separator; + return token == sbjson_token_entry_sep; } - (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { @@ -204,13 +212,14 @@ - (NSString*)name { return @"as object value"; } - (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { switch (token) { - case sbjson_token_object_start: - case sbjson_token_array_start: - case sbjson_token_true: - case sbjson_token_false: + case sbjson_token_object_open: + case sbjson_token_array_open: + case sbjson_token_bool: case sbjson_token_null: - case sbjson_token_number: - case sbjson_token_string: + case sbjson_token_integer: + case sbjson_token_real: + case sbjson_token_string: + case sbjson_token_encoded: return YES; break; @@ -236,8 +245,8 @@ - (NSString*)name { return @"after object value"; } - (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { switch (token) { - case sbjson_token_object_end: - case sbjson_token_separator: + case sbjson_token_object_close: + case sbjson_token_value_sep: return YES; break; default: @@ -262,7 +271,7 @@ @implementation SBJsonStreamParserStateObjectNeedKey - (NSString*)name { return @"in place of object key"; } - (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { - return sbjson_token_string == token; + return sbjson_token_string == token || sbjson_token_encoded == token; } - (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { @@ -285,9 +294,9 @@ - (NSString*)name { return @"at array start"; } - (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { switch (token) { - case sbjson_token_object_end: - case sbjson_token_keyval_separator: - case sbjson_token_separator: + case sbjson_token_object_close: + case sbjson_token_entry_sep: + case sbjson_token_value_sep: return NO; break; @@ -313,11 +322,11 @@ - (NSString*)name { return @"after array value"; } - (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { - return token == sbjson_token_array_end || token == sbjson_token_separator; + return token == sbjson_token_array_close || token == sbjson_token_value_sep; } - (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { - if (tok == sbjson_token_separator) + if (tok == sbjson_token_value_sep) parser.state = [SBJsonStreamParserStateArrayNeedValue sharedInstance]; } @@ -334,10 +343,10 @@ - (NSString*)name { return @"as array value"; } - (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { switch (token) { - case sbjson_token_array_end: - case sbjson_token_keyval_separator: - case sbjson_token_object_end: - case sbjson_token_separator: + case sbjson_token_array_close: + case sbjson_token_entry_sep: + case sbjson_token_object_close: + case sbjson_token_value_sep: return NO; break; diff --git a/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamTokeniser.h b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamTokeniser.h new file mode 100755 index 0000000..b24b4ff --- /dev/null +++ b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamTokeniser.h @@ -0,0 +1,40 @@ +// +// Created by SuperPappi on 09/01/2013. +// +// To change the template use AppCode | Preferences | File Templates. +// + +#import + +typedef enum { + sbjson_token_error = -1, + sbjson_token_eof, + + sbjson_token_array_open, + sbjson_token_array_close, + sbjson_token_value_sep, + + sbjson_token_object_open, + sbjson_token_object_close, + sbjson_token_entry_sep, + + sbjson_token_bool, + sbjson_token_null, + + sbjson_token_integer, + sbjson_token_real, + + sbjson_token_string, + sbjson_token_encoded, +} sbjson_token_t; + + +@interface SBJsonStreamTokeniser : NSObject + +@property (nonatomic, readonly, copy) NSString *error; + +- (void)appendData:(NSData*)data_; +- (sbjson_token_t)getToken:(char**)tok length:(NSUInteger*)len; + +@end + diff --git a/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamTokeniser.m b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamTokeniser.m new file mode 100755 index 0000000..c0e9b29 --- /dev/null +++ b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamTokeniser.m @@ -0,0 +1,397 @@ +// +// Created by SuperPappi on 09/01/2013. +// +// To change the template use AppCode | Preferences | File Templates. +// + + +#import "SBJsonStreamTokeniser.h" + +#define SBStringIsIllegalSurrogateHighCharacter(character) (((character) >= 0xD800UL) && ((character) <= 0xDFFFUL)) +#define SBStringIsSurrogateLowCharacter(character) ((character >= 0xDC00UL) && (character <= 0xDFFFUL)) +#define SBStringIsSurrogateHighCharacter(character) ((character >= 0xD800UL) && (character <= 0xDBFFUL)) + +@implementation SBJsonStreamTokeniser { + NSMutableData *data; + const char *bytes; + NSUInteger index; + NSUInteger offset; +} + +- (void)setError:(NSString *)error { + _error = [NSString stringWithFormat:@"%@ at index %lu", error, (unsigned long)(offset + index)]; +} + +- (void)appendData:(NSData *)data_ { + if (!data) { + data = [data_ mutableCopy]; + + } else if (index) { + // Discard data we've already parsed + [data replaceBytesInRange:NSMakeRange(0, index) withBytes:"" length:0]; + [data appendData:data_]; + + // Add to the offset for reporting + offset += index; + + // Reset index to point to current position + index = 0u; + + } + else { + [data appendData:data_]; + } + + bytes = [data bytes]; +} + +- (void)skipWhitespace { + while (index < data.length) { + switch (bytes[index]) { + case ' ': + case '\t': + case '\r': + case '\n': + index++; + break; + default: + return; + } + } +} + +- (BOOL)getUnichar:(unichar *)ch { + if ([self haveRemainingCharacters:1]) { + *ch = (unichar) bytes[index]; + return YES; + } + return NO; +} + +- (BOOL)haveOneMoreCharacter { + return [self haveRemainingCharacters:1]; +} + +- (BOOL)haveRemainingCharacters:(NSUInteger)length { + return data.length - index >= length; +} + +- (sbjson_token_t)match:(char *)str retval:(sbjson_token_t)tok token:(char **)token length:(NSUInteger *)length { + NSUInteger len = strlen(str); + if ([self haveRemainingCharacters:len]) { + if (!memcmp(bytes + index, str, len)) { + *token = str; + *length = len; + index += len; + return tok; + } + [self setError: [NSString stringWithFormat:@"Expected '%s' after initial '%.1s'", str, str]]; + return sbjson_token_error; + } + + return sbjson_token_eof; +} + +- (BOOL)decodeHexQuad:(unichar*)quad { + unichar tmp = 0; + + for (int i = 0; i < 4; i++, index++) { + unichar c = bytes[index]; + tmp *= 16; + switch (c) { + case '0' ... '9': + tmp += c - '0'; + break; + + case 'a' ... 'f': + tmp += 10 + c - 'a'; + break; + + case 'A' ... 'F': + tmp += 10 + c - 'A'; + break; + + default: + return NO; + } + } + *quad = tmp; + return YES; +} + +- (sbjson_token_t)getStringToken:(char **)token length:(NSUInteger *)length { + + // Skip initial " + index++; + + NSUInteger string_start = index; + sbjson_token_t tok = sbjson_token_string; + + for (;;) { + if (![self haveOneMoreCharacter]) + return sbjson_token_eof; + + switch (bytes[index]) { + case 0 ... 0x1F: + [self setError:[NSString stringWithFormat:@"Unescaped control character [0x%0.2X] in string", bytes[index]]]; + return sbjson_token_error; + + case '"': + *token = (char *)(bytes + string_start); + *length = index - string_start; + index++; + return tok; + + case '\\': + tok = sbjson_token_encoded; + index++; + if (![self haveOneMoreCharacter]) + return sbjson_token_eof; + + if (bytes[index] == 'u') { + index++; + if (![self haveRemainingCharacters:4]) + return sbjson_token_eof; + + unichar hi; + if (![self decodeHexQuad:&hi]) { + [self setError:@"Invalid hex quad"]; + return sbjson_token_error; + } + + if (SBStringIsSurrogateHighCharacter(hi)) { + if (![self haveRemainingCharacters:6]) + return sbjson_token_eof; + + unichar lo; + if (bytes[index++] != '\\' || bytes[index++] != 'u' || ![self decodeHexQuad:&lo]) { + [self setError:@"Missing low character in surrogate pair"]; + return sbjson_token_error; + } + + if (!SBStringIsSurrogateLowCharacter(lo)) { + [self setError:@"Invalid low character in surrogate pair"]; + return sbjson_token_error; + } + + } else if (SBStringIsIllegalSurrogateHighCharacter(hi)) { + [self setError:@"Invalid high character in surrogate pair"]; + return sbjson_token_error; + + } + + + } else { + switch (bytes[index]) { + case '\\': + case '/': + case '"': + case 'b': + case 'n': + case 'r': + case 't': + case 'f': + index++; + break; + + default: + [self setError:[NSString stringWithFormat:@"Illegal escape character [%x]", bytes[index]]]; + return sbjson_token_error; + } + } + + break; + + default: + index++; + break; + } + } + + @throw @"FUT FUT FUT"; +} + +- (sbjson_token_t)getNumberToken:(char **)token length:(NSUInteger *)length { + NSUInteger num_start = index; + if (bytes[index] == '-') { + index++; + + if (![self haveOneMoreCharacter]) + return sbjson_token_eof; + } + + sbjson_token_t tok = sbjson_token_integer; + if (bytes[index] == '0') { + index++; + + if (![self haveOneMoreCharacter]) + return sbjson_token_eof; + + if (isdigit(bytes[index])) { + [self setError:@"Leading zero is illegal in number"]; + return sbjson_token_error; + } + } + + while (isdigit(bytes[index])) { + index++; + if (![self haveOneMoreCharacter]) + return sbjson_token_eof; + } + + if (![self haveOneMoreCharacter]) + return sbjson_token_eof; + + + if (bytes[index] == '.') { + index++; + tok = sbjson_token_real; + + if (![self haveOneMoreCharacter]) + return sbjson_token_eof; + + NSUInteger frac_start = index; + while (isdigit(bytes[index])) { + index++; + if (![self haveOneMoreCharacter]) + return sbjson_token_eof; + } + + if (frac_start == index) { + [self setError:@"No digits after decimal point"]; + return sbjson_token_error; + } + } + + if (bytes[index] == 'e' || bytes[index] == 'E') { + index++; + tok = sbjson_token_real; + + if (![self haveOneMoreCharacter]) + return sbjson_token_eof; + + if (bytes[index] == '-' || bytes[index] == '+') { + index++; + if (![self haveOneMoreCharacter]) + return sbjson_token_eof; + } + + NSUInteger exp_start = index; + while (isdigit(bytes[index])) { + index++; + if (![self haveOneMoreCharacter]) + return sbjson_token_eof; + } + + if (exp_start == index) { + [self setError:@"No digits in exponent"]; + return sbjson_token_error; + } + + } + + if (num_start + 1 == index && bytes[num_start] == '-') { + [self setError:@"No digits after initial minus"]; + return sbjson_token_error; + } + + *token = (char *)(bytes + num_start); + *length = index - num_start; + return tok; +} + + +- (sbjson_token_t)getToken:(char **)token length:(NSUInteger *)length { + [self skipWhitespace]; + NSUInteger copyOfIndex = index; + + unichar ch; + if (![self getUnichar:&ch]) + return sbjson_token_eof; + + sbjson_token_t tok; + switch (ch) { + case '{': { + index++; + tok = sbjson_token_object_open; + break; + } + case '}': { + index++; + tok = sbjson_token_object_close; + break; + + } + case '[': { + index++; + tok = sbjson_token_array_open; + break; + + } + case ']': { + index++; + tok = sbjson_token_array_close; + break; + + } + case 't': { + tok = [self match:"true" retval:sbjson_token_bool token:token length:length]; + break; + + } + case 'f': { + tok = [self match:"false" retval:sbjson_token_bool token:token length:length]; + break; + + } + case 'n': { + tok = [self match:"null" retval:sbjson_token_null token:token length:length]; + break; + + } + case ',': { + index++; + tok = sbjson_token_value_sep; + break; + + } + case ':': { + index++; + tok = sbjson_token_entry_sep; + break; + + } + case '"': { + tok = [self getStringToken:token length:length]; + break; + + } + case '-': + case '0' ... '9': { + tok = [self getNumberToken:token length:length]; + break; + + } + case '+': { + self.error = @"Leading + is illegal in number"; + tok = sbjson_token_error; + break; + + } + default: { + self.error = [NSString stringWithFormat:@"Illegal start of token [%c]", ch]; + tok = sbjson_token_error; + break; + } + } + + if (tok == sbjson_token_eof) { + // We ran out of bytes before we could finish parsing the current token. + // Back up to the start & wait for more data. + index = copyOfIndex; + } + + return tok; +} + +@end \ No newline at end of file diff --git a/JSON/SBJsonStreamWriter.h b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamWriter.h old mode 100644 new mode 100755 similarity index 75% rename from JSON/SBJsonStreamWriter.h rename to socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamWriter.h index c450861..7794a14 --- a/JSON/SBJsonStreamWriter.h +++ b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamWriter.h @@ -1,22 +1,22 @@ /* Copyright (c) 2010, Stig Brautaset. All rights reserved. - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - + Neither the name of the the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A @@ -36,23 +36,21 @@ @interface NSObject (SBProxyForJson) /** - @brief Allows generation of JSON for otherwise unsupported classes. - + Allows generation of JSON for otherwise unsupported classes. + If you have a custom class that you want to create a JSON representation for you can implement this method in your class. It should return a representation of your object defined in terms of objects that can be translated into JSON. For example, a Person object might implement it like this: - - @code - - (id)proxyForJson { - return [NSDictionary dictionaryWithObjectsAndKeys: - name, @"name", - phone, @"phone", - email, @"email", - nil]; - } - @endcode - + + - (id)proxyForJson { + return [NSDictionary dictionaryWithObjectsAndKeys: + name, @"name", + phone, @"phone", + email, @"email", + nil]; + } + */ - (id)proxyForJson; @@ -69,16 +67,33 @@ @class SBJsonStreamWriterState; /** - @brief The Stream Writer class. - + The Stream Writer class. + Accepts a stream of messages and writes JSON of these to its delegate object. - + This class provides a range of high-, mid- and low-level methods. You can mix and match calls to these. For example, you may want to call -writeArrayOpen to start an array and then repeatedly call -writeObject: with various objects before finishing off with a -writeArrayClose call. - - @see @ref json2objc + + Objective-C types are mapped to JSON types in the following way: + + - NSNull -> null + - NSString -> string + - NSArray -> array + - NSDictionary -> object + - NSNumber's -initWithBool:YES -> true + - NSNumber's -initWithBool:NO -> false + - NSNumber -> number + + NSNumber instances created with the -numberWithBool: method are + converted into the JSON boolean "true" and "false" values, and vice + versa. Any other NSNumber instances are converted to a JSON number the + way you would expect. + + @warning: In JSON the keys of an object must be strings. NSDictionary + keys need not be, but attempting to convert an NSDictionary with + non-string keys into JSON will throw an exception.* */ @@ -86,18 +101,18 @@ NSMutableDictionary *cache; } -@property (nonatomic, assign) SBJsonStreamWriterState *state; // Internal -@property (nonatomic, readonly, retain) NSMutableArray *stateStack; // Internal +@property (nonatomic, unsafe_unretained) SBJsonStreamWriterState *state; // Internal +@property (nonatomic, readonly, strong) NSMutableArray *stateStack; // Internal /** - @brief delegate to receive JSON output + delegate to receive JSON output Delegate that will receive messages with output. */ -@property (assign) id delegate; +@property (unsafe_unretained) id delegate; /** - @brief The maximum recursing depth. - + The maximum recursing depth. + Defaults to 512. If the input is nested deeper than this the input will be deemed to be malicious and the parser returns nil, signalling an error. ("Nested too deep".) You can turn off this security feature by setting the maxDepth value to 0. @@ -105,8 +120,8 @@ @property NSUInteger maxDepth; /** - @brief Whether we are generating human-readable (multiline) JSON. - + Whether we are generating human-readable (multiline) JSON. + Set whether or not to generate human-readable JSON. The default is NO, which produces JSON without any whitespace between tokens. If set to YES, generates human-readable JSON with linebreaks after each array value and dictionary key/value pair, indented two @@ -115,17 +130,24 @@ @property BOOL humanReadable; /** - @brief Whether or not to sort the dictionary keys in the output. - + Whether or not to sort the dictionary keys in the output. + If this is set to YES, the dictionary keys in the JSON output will be in sorted order. (This is useful if you need to compare two structures, for example.) The default is NO. */ @property BOOL sortKeys; +/** + An optional comparator to be used if sortKeys is YES. + + If this is nil, sorting will be done via @selector(compare:). + */ +@property (copy) NSComparator sortKeysComparator; + /// Contains the error description after an error has occured. @property (copy) NSString *error; -/** +/** Write an NSDictionary to the JSON stream. @return YES if successful, or NO on failure */ @@ -137,7 +159,7 @@ */ - (BOOL)writeArray:(NSArray *)array; -/** +/** Start writing an Object to the stream @return YES if successful, or NO on failure */ diff --git a/JSON/SBJsonStreamWriter.m b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamWriter.m old mode 100644 new mode 100755 similarity index 92% rename from JSON/SBJsonStreamWriter.m rename to socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamWriter.m index 031dc24..61cd58e --- a/JSON/SBJsonStreamWriter.m +++ b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamWriter.m @@ -30,10 +30,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#if !__has_feature(objc_arc) +#error "This source file must be compiled with ARC enabled!" +#endif + #import "SBJsonStreamWriter.h" #import "SBJsonStreamWriterState.h" -static NSNumber *kNotANumber; static NSNumber *kTrue; static NSNumber *kFalse; static NSNumber *kPositiveInfinity; @@ -48,11 +51,11 @@ @implementation SBJsonStreamWriter @synthesize stateStack; @synthesize humanReadable; @synthesize sortKeys; +@synthesize sortKeysComparator; + (void)initialize { - kNotANumber = [NSDecimalNumber notANumber]; - kPositiveInfinity = [NSNumber numberWithDouble:+INFINITY]; - kNegativeInfinity = [NSNumber numberWithDouble:-INFINITY]; + kPositiveInfinity = [NSNumber numberWithDouble:+HUGE_VAL]; + kNegativeInfinity = [NSNumber numberWithDouble:-HUGE_VAL]; kTrue = [NSNumber numberWithBool:YES]; kFalse = [NSNumber numberWithBool:NO]; } @@ -72,14 +75,6 @@ - (id)init { return self; } -- (void)dealloc { - self.error = nil; - self.state = nil; - [stateStack release]; - [cache release]; - [super dealloc]; -} - #pragma mark Methods - (void)appendBytes:(const void *)bytes length:(NSUInteger)length { @@ -91,8 +86,15 @@ - (BOOL)writeObject:(NSDictionary *)dict { return NO; NSArray *keys = [dict allKeys]; - if (sortKeys) - keys = [keys sortedArrayUsingSelector:@selector(compare:)]; + + if (sortKeys) { + if (sortKeysComparator) { + keys = [keys sortedArrayWithOptions:NSSortStable usingComparator:sortKeysComparator]; + } + else{ + keys = [keys sortedArrayUsingSelector:@selector(compare:)]; + } + } for (id k in keys) { if (![k isKindOfClass:[NSString class]]) { @@ -336,7 +338,7 @@ - (BOOL)writeNumber:(NSNumber*)number { self.error = @"-Infinity is not a valid number in JSON"; return NO; - } else if ([kNotANumber isEqualToNumber:number]) { + } else if (isnan([number doubleValue])) { self.error = @"NaN is not a valid number in JSON"; return NO; } @@ -352,15 +354,10 @@ - (BOOL)writeNumber:(NSNumber*)number { case 'C': case 'I': case 'S': case 'L': case 'Q': len = snprintf(num, sizeof num, "%llu", [number unsignedLongLongValue]); break; - case 'f': case 'd': default: - if ([number isKindOfClass:[NSDecimalNumber class]]) { - char const *utf8 = [[number stringValue] UTF8String]; - [delegate writer:self appendBytes:utf8 length: strlen(utf8)]; - [state transitionState:self]; - return YES; - } - len = snprintf(num, sizeof num, "%.17g", [number doubleValue]); + case 'f': case 'd': default: { + len = snprintf(num, sizeof num, "%.17g", [number doubleValue]); break; + } } [delegate writer:self appendBytes:num length: len]; [state transitionState:self]; diff --git a/JSON/SBJsonStreamWriterAccumulator.h b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamWriterAccumulator.h old mode 100644 new mode 100755 similarity index 100% rename from JSON/SBJsonStreamWriterAccumulator.h rename to socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamWriterAccumulator.h diff --git a/JSON/SBJsonStreamWriterAccumulator.m b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamWriterAccumulator.m old mode 100644 new mode 100755 similarity index 95% rename from JSON/SBJsonStreamWriterAccumulator.m rename to socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamWriterAccumulator.m index c82a1d2..d78c317 --- a/JSON/SBJsonStreamWriterAccumulator.m +++ b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamWriterAccumulator.m @@ -27,6 +27,10 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#if !__has_feature(objc_arc) +#error "This source file must be compiled with ARC enabled!" +#endif + #import "SBJsonStreamWriterAccumulator.h" @@ -42,10 +46,6 @@ - (id)init { return self; } -- (void)dealloc { - [data release]; - [super dealloc]; -} #pragma mark SBJsonStreamWriterDelegate diff --git a/JSON/SBJsonStreamWriterState.h b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamWriterState.h old mode 100644 new mode 100755 similarity index 100% rename from JSON/SBJsonStreamWriterState.h rename to socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamWriterState.h diff --git a/JSON/SBJsonStreamWriterState.m b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamWriterState.m old mode 100644 new mode 100755 similarity index 93% rename from JSON/SBJsonStreamWriterState.m rename to socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamWriterState.m index 9f04cac..a87b447 --- a/JSON/SBJsonStreamWriterState.m +++ b/socketIOexample/socketio.objc/JSON-Framework/SBJsonStreamWriterState.m @@ -30,13 +30,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#if !__has_feature(objc_arc) +#error "This source file must be compiled with ARC enabled!" +#endif + #import "SBJsonStreamWriterState.h" #import "SBJsonStreamWriter.h" #define SINGLETON \ + (id)sharedInstance { \ - static id state; \ - if (!state) state = [[self alloc] init]; \ + static id state = nil; \ + if (!state) { \ + @synchronized(self) { \ + if (!state) state = [[self alloc] init]; \ + } \ + } \ return state; \ } diff --git a/JSON/SBJsonWriter.h b/socketIOexample/socketio.objc/JSON-Framework/SBJsonWriter.h old mode 100644 new mode 100755 similarity index 74% rename from JSON/SBJsonWriter.h rename to socketIOexample/socketio.objc/JSON-Framework/SBJsonWriter.h index b0ae5e9..aaf6b0b --- a/JSON/SBJsonWriter.h +++ b/socketIOexample/socketio.objc/JSON-Framework/SBJsonWriter.h @@ -1,20 +1,20 @@ /* Copyright (C) 2009 Stig Brautaset. All rights reserved. - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - + * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -30,18 +30,17 @@ #import /** - @brief The JSON writer class. - + The JSON writer class. + This uses SBJsonStreamWriter internally. - - @see @ref json2objc + */ @interface SBJsonWriter : NSObject /** - @brief The maximum recursing depth. - + The maximum recursing depth. + Defaults to 32. If the input is nested deeper than this the input will be deemed to be malicious and the parser returns nil, signalling an error. ("Nested too deep".) You can turn off this security feature by setting the maxDepth value to 0. @@ -49,8 +48,8 @@ @property NSUInteger maxDepth; /** - @brief Return an error trace, or nil if there was no errors. - + Return an error trace, or nil if there was no errors. + Note that this method returns the trace of the last method that failed. You need to check the return value of the call you're making to figure out if the call actually failed, before you know call this method. @@ -58,8 +57,8 @@ @property (readonly, copy) NSString *error; /** - @brief Whether we are generating human-readable (multiline) JSON. - + Whether we are generating human-readable (multiline) JSON. + Set whether or not to generate human-readable JSON. The default is NO, which produces JSON without any whitespace. (Except inside strings.) If set to YES, generates human-readable JSON with linebreaks after each array value and dictionary key/value pair, indented two @@ -68,43 +67,37 @@ @property BOOL humanReadable; /** - @brief Whether or not to sort the dictionary keys in the output. - + Whether or not to sort the dictionary keys in the output. + If this is set to YES, the dictionary keys in the JSON output will be in sorted order. (This is useful if you need to compare two structures, for example.) The default is NO. */ @property BOOL sortKeys; /** - @brief Return JSON representation for the given object. - + An optional comparator to be used if sortKeys is YES. + + If this is nil, sorting will be done via @selector(compare:). + */ +@property (copy) NSComparator sortKeysComparator; + +/** + Generates string with JSON representation for the given object. + Returns a string containing JSON representation of the passed in value, or nil on error. - If nil is returned and @p error is not NULL, @p *error can be interrogated to find the cause of the error. - + If nil is returned and error is not NULL, *error can be interrogated to find the cause of the error. + @param value any instance that can be represented as JSON text. */ - (NSString*)stringWithObject:(id)value; /** - @brief Return JSON representation for the given object. - + Generates JSON representation for the given object. + Returns an NSData object containing JSON represented as UTF8 text, or nil on error. - + @param value any instance that can be represented as JSON text. */ - (NSData*)dataWithObject:(id)value; -/** - @brief Return JSON representation (or fragment) for the given object. - - Returns a string containing JSON representation of the passed in value, or nil on error. - If nil is returned and @p error is not NULL, @p *error can be interrogated to find the cause of the error. - - @param value any instance that can be represented as a JSON fragment - @param error pointer to object to be populated with NSError on failure - - */- (NSString*)stringWithObject:(id)value - error:(NSError**)error; - - @end diff --git a/JSON/SBJsonWriter.m b/socketIOexample/socketio.objc/JSON-Framework/SBJsonWriter.m old mode 100644 new mode 100755 similarity index 78% rename from JSON/SBJsonWriter.m rename to socketIOexample/socketio.objc/JSON-Framework/SBJsonWriter.m index 15d1414..34e4db4 --- a/JSON/SBJsonWriter.m +++ b/socketIOexample/socketio.objc/JSON-Framework/SBJsonWriter.m @@ -27,6 +27,10 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#if !__has_feature(objc_arc) +#error "This source file must be compiled with ARC enabled!" +#endif + #import "SBJsonWriter.h" #import "SBJsonStreamWriter.h" #import "SBJsonStreamWriterAccumulator.h" @@ -44,6 +48,8 @@ @implementation SBJsonWriter @synthesize error; @synthesize maxDepth; +@synthesize sortKeysComparator; + - (id)init { self = [super init]; if (self) { @@ -52,39 +58,23 @@ - (id)init { return self; } -- (void)dealloc { - [error release]; - [super dealloc]; -} - (NSString*)stringWithObject:(id)value { NSData *data = [self dataWithObject:value]; if (data) - return [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; + return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; return nil; -} - -- (NSString*)stringWithObject:(id)value error:(NSError**)error_ { - NSString *tmp = [self stringWithObject:value]; - if (tmp) - return tmp; - - if (error_) { - NSDictionary *ui = [NSDictionary dictionaryWithObjectsAndKeys:error, NSLocalizedDescriptionKey, nil]; - *error_ = [NSError errorWithDomain:@"org.brautaset.SBJsonWriter.ErrorDomain" code:0 userInfo:ui]; - } - - return nil; } - (NSData*)dataWithObject:(id)object { self.error = nil; - SBJsonStreamWriterAccumulator *accumulator = [[[SBJsonStreamWriterAccumulator alloc] init] autorelease]; + SBJsonStreamWriterAccumulator *accumulator = [[SBJsonStreamWriterAccumulator alloc] init]; - SBJsonStreamWriter *streamWriter = [[[SBJsonStreamWriter alloc] init] autorelease]; + SBJsonStreamWriter *streamWriter = [[SBJsonStreamWriter alloc] init]; streamWriter.sortKeys = self.sortKeys; streamWriter.maxDepth = self.maxDepth; + streamWriter.sortKeysComparator = self.sortKeysComparator; streamWriter.humanReadable = self.humanReadable; streamWriter.delegate = accumulator; diff --git a/socketIOexample/socketio.objc/SocketIO.h b/socketIOexample/socketio.objc/SocketIO.h new file mode 100755 index 0000000..05f6b65 --- /dev/null +++ b/socketIOexample/socketio.objc/SocketIO.h @@ -0,0 +1,125 @@ +// +// SocketIO.h +// v0.4.0.1 ARC +// +// based on +// socketio-cocoa https://github.com/fpotter/socketio-cocoa +// by Fred Potter +// +// using +// https://github.com/square/SocketRocket +// https://github.com/stig/json-framework/ +// +// reusing some parts of +// /socket.io/socket.io.js +// +// Created by Philipp Kyeck http://beta-interactive.de +// +// Updated by +// samlown https://github.com/samlown +// kayleg https://github.com/kayleg +// taiyangc https://github.com/taiyangc +// + +#import + +#import "SocketIOTransport.h" + +@class SocketIO; +@class SocketIOPacket; + +typedef void(^SocketIOCallback)(id argsData); + +extern NSString* const SocketIOError; + +typedef enum { + SocketIOServerRespondedWithInvalidConnectionData = -1, + SocketIOServerRespondedWithDisconnect = -2, + SocketIOHeartbeatTimeout = -3, + SocketIOWebSocketClosed = -4, + SocketIOTransportsNotSupported = -5, + SocketIOHandshakeFailed = -6, + SocketIODataCouldNotBeSend = -7 +} SocketIOErrorCodes; + + +@protocol SocketIODelegate +@optional +- (void) socketIODidConnect:(SocketIO *)socket; +- (void) socketIODidDisconnect:(SocketIO *)socket disconnectedWithError:(NSError *)error; +- (void) socketIO:(SocketIO *)socket didReceiveMessage:(SocketIOPacket *)packet; +- (void) socketIO:(SocketIO *)socket didReceiveJSON:(SocketIOPacket *)packet; +- (void) socketIO:(SocketIO *)socket didReceiveEvent:(SocketIOPacket *)packet; +- (void) socketIO:(SocketIO *)socket didSendMessage:(SocketIOPacket *)packet; +- (void) socketIO:(SocketIO *)socket onError:(NSError *)error; + +// TODO: deprecated -> to be removed +- (void) socketIO:(SocketIO *)socket failedToConnectWithError:(NSError *)error __attribute__((deprecated)); +- (void) socketIOHandshakeFailed:(SocketIO *)socket __attribute__((deprecated)); +@end + + +@interface SocketIO : NSObject +{ + NSString *_host; + NSInteger _port; + NSString *_sid; + NSString *_endpoint; + NSDictionary *_params; + + __unsafe_unretained id _delegate; + + NSObject *_transport; + + BOOL _isConnected; + BOOL _isConnecting; + BOOL _useSecure; + + NSURLConnection *_handshake; + + // heartbeat + NSTimeInterval _heartbeatTimeout; + dispatch_source_t _timeout; + + NSMutableArray *_queue; + + // acknowledge + NSMutableDictionary *_acks; + NSInteger _ackCount; + + // http request + NSMutableData *_httpRequestData; + + // get all arguments from ack? (https://github.com/pkyeck/socket.IO-objc/pull/85) + BOOL _returnAllDataFromAck; +} + +@property (nonatomic, readonly) NSString *host; +@property (nonatomic, readonly) NSInteger port; +@property (nonatomic, readonly) NSString *sid; +@property (nonatomic, readonly) NSTimeInterval heartbeatTimeout; +@property (nonatomic) BOOL useSecure; +@property (nonatomic, readonly) BOOL isConnected, isConnecting; +@property (nonatomic, unsafe_unretained) id delegate; +@property (nonatomic) BOOL returnAllDataFromAck; + +- (id) initWithDelegate:(id)delegate; +- (void) connectToHost:(NSString *)host onPort:(NSInteger)port; +- (void) connectToHost:(NSString *)host onPort:(NSInteger)port withParams:(NSDictionary *)params; +- (void) connectToHost:(NSString *)host onPort:(NSInteger)port withParams:(NSDictionary *)params withNamespace:(NSString *)endpoint; +- (void) connectToHost:(NSString *)host onPort:(NSInteger)port withParams:(NSDictionary *)params withNamespace:(NSString *)endpoint withConnectionTimeout: (NSTimeInterval) connectionTimeout; + +- (void) disconnect; +- (void) disconnectForced; + +- (void) sendMessage:(NSString *)data; +- (void) sendMessage:(NSString *)data withAcknowledge:(SocketIOCallback)function; +- (void) sendJSON:(NSDictionary *)data; +- (void) sendJSON:(NSDictionary *)data withAcknowledge:(SocketIOCallback)function; +- (void) sendEvent:(NSString *)eventName withData:(id)data; +- (void) sendEvent:(NSString *)eventName withData:(id)data andAcknowledge:(SocketIOCallback)function; +- (void) sendAcknowledgement:(NSString*)pId withArgs:(NSArray *)data; + +- (void) setResourceName:(NSString *)name; + +@end \ No newline at end of file diff --git a/socketIOexample/socketio.objc/SocketIO.m b/socketIOexample/socketio.objc/SocketIO.m new file mode 100755 index 0000000..ce6b5bd --- /dev/null +++ b/socketIOexample/socketio.objc/SocketIO.m @@ -0,0 +1,839 @@ +// +// SocketIO.m +// v0.4.0.1 ARC +// +// based on +// socketio-cocoa https://github.com/fpotter/socketio-cocoa +// by Fred Potter +// +// using +// https://github.com/square/SocketRocket +// https://github.com/stig/json-framework/ +// +// reusing some parts of +// /socket.io/socket.io.js +// +// Created by Philipp Kyeck http://beta-interactive.de +// +// Updated by +// samlown https://github.com/samlown +// kayleg https://github.com/kayleg +// taiyangc https://github.com/taiyangc +// + +#import "SocketIO.h" +#import "SocketIOPacket.h" +#import "SocketIOJSONSerialization.h" + +#import "SocketIOTransportWebsocket.h" +#import "SocketIOTransportXHR.h" + +#define DEBUG_LOGS 1 +#define DEBUG_CERTIFICATE 1 + +#if DEBUG_LOGS +#define DEBUGLOG(...) NSLog(__VA_ARGS__) +#else +#define DEBUGLOG(...) +#endif + +static NSString* kResourceName = @"socket.io"; +static NSString* kHandshakeURL = @"%@://%@%@/%@/1/?t=%d%@"; +static NSString* kForceDisconnectURL = @"%@://%@%@/%@/1/xhr-polling/%@?disconnect"; + +float const defaultConnectionTimeout = 10.0f; + +NSString* const SocketIOError = @"SocketIOError"; +NSString* const SocketIOException = @"SocketIOException"; + +# pragma mark - +# pragma mark SocketIO's private interface + +@interface SocketIO (Private) + +- (NSArray*) arrayOfCaptureComponentsMatchedByRegex:(NSString*)regex; + +- (void) setTimeout; +- (void) onTimeout; + +- (void) onConnect:(SocketIOPacket *)packet; +- (void) onDisconnect:(NSError *)error; + +- (void) sendDisconnect; +- (void) sendHearbeat; +- (void) send:(SocketIOPacket *)packet; + +- (NSString *) addAcknowledge:(SocketIOCallback)function; +- (void) removeAcknowledgeForKey:(NSString *)key; +- (NSMutableArray*) getMatchesFrom:(NSString*)data with:(NSString*)regex; + +@end + +# pragma mark - +# pragma mark SocketIO implementation + +@implementation SocketIO + +@synthesize isConnected = _isConnected, + isConnecting = _isConnecting, + useSecure = _useSecure, + delegate = _delegate, + heartbeatTimeout = _heartbeatTimeout, + returnAllDataFromAck = _returnAllDataFromAck; + +- (id) initWithDelegate:(id)delegate +{ + self = [super init]; + if (self) { + _delegate = delegate; + _queue = [[NSMutableArray alloc] init]; + _ackCount = 0; + _acks = [[NSMutableDictionary alloc] init]; + _returnAllDataFromAck = NO; + } + return self; +} + +- (void) connectToHost:(NSString *)host onPort:(NSInteger)port +{ + [self connectToHost:host onPort:port withParams:nil withNamespace:@"" withConnectionTimeout:defaultConnectionTimeout]; +} + +- (void) connectToHost:(NSString *)host onPort:(NSInteger)port withParams:(NSDictionary *)params +{ + [self connectToHost:host onPort:port withParams:params withNamespace:@"" withConnectionTimeout:defaultConnectionTimeout]; +} + +- (void) connectToHost:(NSString *)host + onPort:(NSInteger)port + withParams:(NSDictionary *)params + withNamespace:(NSString *)endpoint +{ + [self connectToHost:host onPort:port withParams:params withNamespace:endpoint withConnectionTimeout:defaultConnectionTimeout]; +} + +- (void) connectToHost:(NSString *)host + onPort:(NSInteger)port + withParams:(NSDictionary *)params + withNamespace:(NSString *)endpoint + withConnectionTimeout:(NSTimeInterval)connectionTimeout +{ + if (!_isConnected && !_isConnecting) { + _isConnecting = YES; + + _host = host; + _port = port; + _params = params; + _endpoint = [endpoint copy]; + + // create a query parameters string + NSMutableString *query = [[NSMutableString alloc] initWithString:@""]; + [params enumerateKeysAndObjectsUsingBlock: ^(id key, id value, BOOL *stop) { + [query appendFormat:@"&%@=%@", key, value]; + }]; + + // do handshake via HTTP request + NSString *protocol = _useSecure ? @"https" : @"http"; + NSString *port = _port ? [NSString stringWithFormat:@":%d", _port] : @""; + NSString *handshakeUrl = [NSString stringWithFormat:kHandshakeURL, protocol, _host, port, kResourceName, rand(), query]; + + DEBUGLOG(@"Connecting to socket with URL: %@", handshakeUrl); + query = nil; + + // make a request + NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:handshakeUrl] + cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData + timeoutInterval:connectionTimeout]; + + _handshake = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; + [_handshake scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; + [_handshake start]; + + if (_handshake) { + _httpRequestData = [NSMutableData data]; + } + else { + // connection failed + [self connection:_handshake didFailWithError:nil]; + } + } +} + +- (void) disconnect +{ + if (_isConnected) { + [self sendDisconnect]; + } + else if (_isConnecting) { + [_handshake cancel]; + [self onDisconnect: nil]; + } +} + +- (void) disconnectForced +{ + NSString *protocol = [self useSecure] ? @"https" : @"http"; + NSString *port = _port ? [NSString stringWithFormat:@":%d", _port] : @""; + NSString *urlString = [NSString stringWithFormat:kForceDisconnectURL, protocol, _host, port, kResourceName, _sid]; + NSURL *url = [NSURL URLWithString:urlString]; + DEBUGLOG(@"Force disconnect at: %@", urlString); + + NSURLRequest *request = [NSURLRequest requestWithURL:url]; + NSError *error = nil; + NSHTTPURLResponse *response = nil; + + [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; + + if (error || [response statusCode] != 200) { + DEBUGLOG(@"Error during disconnect: %@", error); + } + + [self onDisconnect:error]; +} + +- (void) sendMessage:(NSString *)data +{ + [self sendMessage:data withAcknowledge:nil]; +} + +- (void) sendMessage:(NSString *)data withAcknowledge:(SocketIOCallback)function +{ + SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"message"]; + packet.data = data; + packet.pId = [self addAcknowledge:function]; + [self send:packet]; +} + +- (void) sendJSON:(NSDictionary *)data +{ + [self sendJSON:data withAcknowledge:nil]; +} + +- (void) sendJSON:(NSDictionary *)data withAcknowledge:(SocketIOCallback)function +{ + SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"json"]; + packet.data = [SocketIOJSONSerialization JSONStringFromObject:data error:nil]; + packet.pId = [self addAcknowledge:function]; + [self send:packet]; +} + +- (void) sendEvent:(NSString *)eventName withData:(id)data +{ + [self sendEvent:eventName withData:data andAcknowledge:nil]; +} + +- (void) sendEvent:(NSString *)eventName withData:(id)data andAcknowledge:(SocketIOCallback)function +{ + NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObject:eventName forKey:@"name"]; + + // do not require arguments + if (data != nil) { + [dict setObject:[NSArray arrayWithObject:data] forKey:@"args"]; + } + + SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"event"]; + packet.data = [SocketIOJSONSerialization JSONStringFromObject:dict error:nil]; + packet.pId = [self addAcknowledge:function]; + if (function) { + packet.ack = @"data"; + } + [self send:packet]; +} + +- (void) sendAcknowledgement:(NSString *)pId withArgs:(NSArray *)data +{ + SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"ack"]; + packet.data = [SocketIOJSONSerialization JSONStringFromObject:data error:nil]; + packet.pId = pId; + packet.ack = @"data"; + + [self send:packet]; +} + +- (void) setResourceName:(NSString *)name +{ + kResourceName = [name copy]; +} + +# pragma mark - +# pragma mark private methods + +- (void) sendDisconnect +{ + SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"disconnect"]; + [self send:packet]; + [self onDisconnect:nil]; +} + +- (void) sendConnect +{ + SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"connect"]; + [self send:packet]; +} + +- (void) sendHeartbeat +{ + SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"heartbeat"]; + [self send:packet]; +} + +- (void) send:(SocketIOPacket *)packet +{ + if (![self isConnected] && ![self isConnecting]) { + DEBUGLOG(@"Already disconnected!"); + return; + } + DEBUGLOG(@"send()"); + NSNumber *type = [packet typeAsNumber]; + NSMutableArray *encoded = [NSMutableArray arrayWithObject:type]; + + NSString *pId = packet.pId != nil ? packet.pId : @""; + if ([packet.ack isEqualToString:@"data"]) { + pId = [pId stringByAppendingString:@"+"]; + } + + // Do not write pid for acknowledgements + if ([type intValue] != 6) { + [encoded addObject:pId]; + } + + // Add the end point for the namespace to be used, as long as it is not + // an ACK, heartbeat, or disconnect packet + if ([type intValue] != 6 && [type intValue] != 2 && [type intValue] != 0) { + [encoded addObject:_endpoint]; + } + else { + [encoded addObject:@""]; + } + + if (packet.data != nil) { + NSString *ackpId = @""; + // This is an acknowledgement packet, so, prepend the ack pid to the data + if ([type intValue] == 6) { + ackpId = [NSString stringWithFormat:@":%@%@", packet.pId, @"+"]; + } + [encoded addObject:[NSString stringWithFormat:@"%@%@", ackpId, packet.data]]; + } + + NSString *req = [encoded componentsJoinedByString:@":"]; + if (![_transport isReady]) { + DEBUGLOG(@"queue >>> %@", req); + [_queue addObject:packet]; + } + else { + DEBUGLOG(@"send() >>> %@", req); + [_transport send:req]; + + if ([_delegate respondsToSelector:@selector(socketIO:didSendMessage:)]) { + [_delegate socketIO:self didSendMessage:packet]; + } + } +} + +- (void) doQueue +{ + DEBUGLOG(@"doQueue() >> %lu", (unsigned long)[_queue count]); + + // TODO send all packets at once ... not as seperate packets + while ([_queue count] > 0) { + SocketIOPacket *packet = [_queue objectAtIndex:0]; + [self send:packet]; + [_queue removeObject:packet]; + } +} + +- (void) onConnect:(SocketIOPacket *)packet +{ + DEBUGLOG(@"onConnect()"); + + _isConnected = YES; + + // Send the connected packet so the server knows what it's dealing with. + // Only required when endpoint/namespace is present + if ([_endpoint length] > 0) { + // Make sure the packet we received has an endpoint, otherwise send it again + if (![packet.endpoint isEqualToString:_endpoint]) { + DEBUGLOG(@"onConnect() >> End points do not match, resending connect packet"); + [self sendConnect]; + return; + } + } + + _isConnecting = NO; + + if ([_delegate respondsToSelector:@selector(socketIODidConnect:)]) { + [_delegate socketIODidConnect:self]; + } + + // send any queued packets + [self doQueue]; + + [self setTimeout]; +} + +# pragma mark - +# pragma mark Acknowledge methods + +- (NSString *) addAcknowledge:(SocketIOCallback)function +{ + if (function) { + ++_ackCount; + NSString *ac = [NSString stringWithFormat:@"%ld", (long)_ackCount]; + [_acks setObject:[function copy] forKey:ac]; + return ac; + } + return nil; +} + +- (void) removeAcknowledgeForKey:(NSString *)key +{ + [_acks removeObjectForKey:key]; +} + +# pragma mark - +# pragma mark Heartbeat methods + +- (void) onTimeout +{ + if (_timeout) { + dispatch_source_cancel(_timeout); + _timeout = NULL; + } + + DEBUGLOG(@"Timed out waiting for heartbeat."); + [self onDisconnect:[NSError errorWithDomain:SocketIOError + code:SocketIOHeartbeatTimeout + userInfo:nil]]; +} + +- (void) setTimeout +{ + DEBUGLOG(@"start/reset timeout"); + if (_timeout) { + dispatch_source_cancel(_timeout); + _timeout = NULL; + } + + _timeout = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, + 0, + 0, + dispatch_get_main_queue()); + + dispatch_source_set_timer(_timeout, + dispatch_time(DISPATCH_TIME_NOW, _heartbeatTimeout * NSEC_PER_SEC), + 0, + 0); + + __weak SocketIO *weakSelf = self; + + dispatch_source_set_event_handler(_timeout, ^{ + [weakSelf onTimeout]; + }); + + dispatch_resume(_timeout); + +} + + +# pragma mark - +# pragma mark Regex helper method +- (NSMutableArray*) getMatchesFrom:(NSString*)data with:(NSString*)regex +{ + NSRegularExpression *nsregexTest = [NSRegularExpression regularExpressionWithPattern:regex options:0 error:nil]; + NSArray *nsmatchesTest = [nsregexTest matchesInString:data options:0 range:NSMakeRange(0, [data length])]; + NSMutableArray *arr = [NSMutableArray array]; + + for (NSTextCheckingResult *nsmatchTest in nsmatchesTest) { + NSMutableArray *localMatch = [NSMutableArray array]; + for (NSUInteger i = 0, l = [nsmatchTest numberOfRanges]; i < l; i++) { + NSRange range = [nsmatchTest rangeAtIndex:i]; + NSString *nsmatchStr = nil; + if (range.location != NSNotFound && NSMaxRange(range) <= [data length]) { + nsmatchStr = [data substringWithRange:[nsmatchTest rangeAtIndex:i]]; + } + else { + nsmatchStr = @""; + } + [localMatch addObject:nsmatchStr]; + } + [arr addObject:localMatch]; + } + + return arr; +} + + +#pragma mark - +#pragma mark SocketIOTransport callbacks + +- (void) onData:(NSString *)data +{ + DEBUGLOG(@"onData %@", data); + + // data arrived -> reset timeout + [self setTimeout]; + + // check if data is valid (from socket.io.js) + NSString *regex = @"^([^:]+):([0-9]+)?(\\+)?:([^:]+)?:?(.*)?$"; + NSString *regexPieces = @"^([0-9]+)(\\+)?(.*)"; + + // create regex result + NSMutableArray *test = [self getMatchesFrom:data with:regex]; + + // valid data-string arrived + if ([test count] > 0) { + NSArray *result = [test objectAtIndex:0]; + + int idx = [[result objectAtIndex:1] intValue]; + SocketIOPacket *packet = [[SocketIOPacket alloc] initWithTypeIndex:idx]; + + packet.pId = [result objectAtIndex:2]; + + packet.ack = [result objectAtIndex:3]; + packet.endpoint = [result objectAtIndex:4]; + packet.data = [result objectAtIndex:5]; + + // + switch (idx) { + case 0: { + DEBUGLOG(@"disconnect"); + [self onDisconnect:[NSError errorWithDomain:SocketIOError + code:SocketIOServerRespondedWithDisconnect + userInfo:nil]]; + break; + } + case 1: { + DEBUGLOG(@"connected"); + // from socket.io.js ... not sure when data will contain sth?! + // packet.qs = data || ''; + [self onConnect:packet]; + break; + } + case 2: { + DEBUGLOG(@"heartbeat"); + [self sendHeartbeat]; + break; + } + case 3: { + DEBUGLOG(@"message"); + if (packet.data && ![packet.data isEqualToString:@""]) { + if ([_delegate respondsToSelector:@selector(socketIO:didReceiveMessage:)]) { + [_delegate socketIO:self didReceiveMessage:packet]; + } + } + break; + } + case 4: { + DEBUGLOG(@"json"); + if (packet.data && ![packet.data isEqualToString:@""]) { + if ([_delegate respondsToSelector:@selector(socketIO:didReceiveJSON:)]) { + [_delegate socketIO:self didReceiveJSON:packet]; + } + } + break; + } + case 5: { + DEBUGLOG(@"event"); + if (packet.data && ![packet.data isEqualToString:@""]) { + NSDictionary *json = [packet dataAsJSON]; + packet.name = [json objectForKey:@"name"]; + packet.args = [json objectForKey:@"args"]; + if ([_delegate respondsToSelector:@selector(socketIO:didReceiveEvent:)]) { + [_delegate socketIO:self didReceiveEvent:packet]; + } + } + break; + } + case 6: { + DEBUGLOG(@"ack"); + + // create regex result + NSMutableArray *pieces = [self getMatchesFrom:packet.data with:regexPieces]; + + if ([pieces count] > 0) { + NSArray *piece = [pieces objectAtIndex:0]; + int ackId = [[piece objectAtIndex:1] intValue]; + DEBUGLOG(@"ack id found: %d", ackId); + + NSString *argsStr = [piece objectAtIndex:3]; + id argsData = nil; + if (argsStr && ![argsStr isEqualToString:@""]) { + argsData = [SocketIOJSONSerialization objectFromJSONData:[argsStr dataUsingEncoding:NSUTF8StringEncoding] error:nil]; + // either send complete response or only the first arg to callback + if (!_returnAllDataFromAck && [argsData count] > 0) { + argsData = [argsData objectAtIndex:0]; + } + } + + // get selector for ackId + NSString *key = [NSString stringWithFormat:@"%d", ackId]; + SocketIOCallback callbackFunction = [_acks objectForKey:key]; + if (callbackFunction != nil) { + callbackFunction(argsData); + [self removeAcknowledgeForKey:key]; + } + } + + break; + } + case 7: { + DEBUGLOG(@"error"); + break; + } + case 8: { + DEBUGLOG(@"noop"); + break; + } + default: { + DEBUGLOG(@"command not found or not yet supported"); + break; + } + } + + packet = nil; + } + else { + DEBUGLOG(@"ERROR: data that has arrived wasn't valid"); + } +} + +- (void) onDisconnect:(NSError *)error +{ + DEBUGLOG(@"onDisconnect()"); + BOOL wasConnected = _isConnected; + BOOL wasConnecting = _isConnecting; + + _isConnected = NO; + _isConnecting = NO; + _sid = nil; + + [_queue removeAllObjects]; + + // Kill the heartbeat timer + if (_timeout) { + dispatch_source_cancel(_timeout); + _timeout = NULL; + } + + // Disconnect the websocket, just in case + if (_transport != nil) { + // clear websocket's delegate - otherwise crashes + _transport.delegate = nil; + [_transport close]; + } + + if ((wasConnected || wasConnecting)) { + if ([_delegate respondsToSelector:@selector(socketIODidDisconnect:disconnectedWithError:)]) { + [_delegate socketIODidDisconnect:self disconnectedWithError:error]; + } + } +} + +- (void) onError:(NSError *)error +{ + if ([_delegate respondsToSelector:@selector(socketIO:onError:)]) { + [_delegate socketIO:self onError:error]; + } +} + + +# pragma mark - +# pragma mark Handshake callbacks (NSURLConnectionDataDelegate) +- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response +{ + // check for server status code (http://gigliwood.com/weblog/Cocoa/Q__When_is_an_conne.html) + if ([response respondsToSelector:@selector(statusCode)]) { + NSInteger statusCode = [((NSHTTPURLResponse *)response) statusCode]; + DEBUGLOG(@"didReceiveResponse() %i", statusCode); + + if (statusCode >= 400) { + // stop connecting; no more delegate messages + [connection cancel]; + + NSString *error = [NSString stringWithFormat:NSLocalizedString(@"Server returned status code %d", @""), statusCode]; + NSDictionary *errorInfo = [NSDictionary dictionaryWithObject:error forKey:NSLocalizedDescriptionKey]; + NSError *statusError = [NSError errorWithDomain:SocketIOError + code:statusCode + userInfo:errorInfo]; + // call error callback manually + [self connection:connection didFailWithError:statusError]; + } + } + + [_httpRequestData setLength:0]; +} + +- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + [_httpRequestData appendData:data]; +} + +- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + NSLog(@"ERROR: handshake failed ... %@", [error localizedDescription]); + + _isConnected = NO; + _isConnecting = NO; + + if ([_delegate respondsToSelector:@selector(socketIO:onError:)]) { + NSMutableDictionary *errorInfo = [NSDictionary dictionaryWithObject:error forKey:NSLocalizedDescriptionKey]; + + NSError *err = [NSError errorWithDomain:SocketIOError + code:SocketIOHandshakeFailed + userInfo:errorInfo]; + + [_delegate socketIO:self onError:err]; + } + // TODO: deprecated - to be removed + else if ([_delegate respondsToSelector:@selector(socketIOHandshakeFailed:)]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_delegate socketIOHandshakeFailed:self]; +#pragma clang diagnostic pop + } +} + +- (void) connectionDidFinishLoading:(NSURLConnection *)connection +{ + NSString *responseString = [[NSString alloc] initWithData:_httpRequestData encoding:NSASCIIStringEncoding]; + + DEBUGLOG(@"connectionDidFinishLoading() %@", responseString); + NSArray *data = [responseString componentsSeparatedByString:@":"]; + // should be SID : heartbeat timeout : connection timeout : supported transports + + // check each returned value (thanks for the input https://github.com/taiyangc) + BOOL connectionFailed = false; + NSError* error; + + _sid = [data objectAtIndex:0]; + if ([_sid length] < 1 || [data count] < 4) { + // did not receive valid data, possibly missing a useSecure? + connectionFailed = true; + } + else { + // check SID + DEBUGLOG(@"sid: %@", _sid); + NSString *regex = @"[^0-9]"; + NSPredicate *regexTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex]; + if ([_sid rangeOfString:@"error"].location != NSNotFound || [regexTest evaluateWithObject:_sid]) { + [self connectToHost:_host onPort:_port withParams:_params withNamespace:_endpoint]; + return; + } + + // check heartbeat timeout + _heartbeatTimeout = [[data objectAtIndex:1] floatValue]; + if (_heartbeatTimeout == 0.0) { + // couldn't find float value -> fail + connectionFailed = true; + } + else { + // add small buffer of 7sec (magic xD) otherwise heartbeat will be too late and connection is closed + _heartbeatTimeout += 7.0; + } + DEBUGLOG(@"heartbeatTimeout: %f", _heartbeatTimeout); + + // index 2 => connection timeout + + // get transports + NSString *t = [data objectAtIndex:3]; + NSArray *transports = [t componentsSeparatedByString:@","]; + DEBUGLOG(@"transports: %@", transports); + + if ([transports indexOfObject:@"websocket"] != NSNotFound) { + DEBUGLOG(@"websocket supported -> using it now"); + _transport = [[SocketIOTransportWebsocket alloc] initWithDelegate:self]; + } + else if ([transports indexOfObject:@"xhr-polling"] != NSNotFound) { + DEBUGLOG(@"xhr polling supported -> using it now"); + _transport = [[SocketIOTransportXHR alloc] initWithDelegate:self]; + } + else { + DEBUGLOG(@"no transport found that is supported :( -> fail"); + connectionFailed = true; + error = [NSError errorWithDomain:SocketIOError + code:SocketIOTransportsNotSupported + userInfo:nil]; + } + } + + // if connection didn't return the values we need -> fail + if (connectionFailed) { + // error already set!? + if (error == nil) { + error = [NSError errorWithDomain:SocketIOError + code:SocketIOServerRespondedWithInvalidConnectionData + userInfo:nil]; + } + + if ([_delegate respondsToSelector:@selector(socketIO:onError:)]) { + [_delegate socketIO:self onError:error]; + } + // TODO: deprecated - to be removed + else if ([_delegate respondsToSelector:@selector(socketIO:failedToConnectWithError:)]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_delegate socketIO:self failedToConnectWithError:error]; +#pragma clang diagnostic pop + } + + // make sure to do call all cleanup code + [self onDisconnect:error]; + + return; + } + + [_transport open]; +} + +#if DEBUG_CERTIFICATE + +// to deal with self-signed certificates +- (BOOL) connection:(NSURLConnection *)connection +canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace +{ + return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]; +} + +- (void) connection:(NSURLConnection *)connection +didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + if ([challenge.protectionSpace.authenticationMethod + isEqualToString:NSURLAuthenticationMethodServerTrust]) { + // we only trust our own domain + if ([challenge.protectionSpace.host isEqualToString:_host]) { + SecTrustRef trust = challenge.protectionSpace.serverTrust; + NSURLCredential *credential = [NSURLCredential credentialForTrust:trust]; + [challenge.sender useCredential:credential forAuthenticationChallenge:challenge]; + } + } + + [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; +} +#endif + + +# pragma mark - + +- (void) dealloc +{ + [_handshake cancel]; + _handshake = nil; + + _host = nil; + _sid = nil; + _endpoint = nil; + + _transport.delegate = nil; + _transport = nil; + + if (_timeout) { + dispatch_source_cancel(_timeout); + _timeout = NULL; + } + + _queue = nil; + _acks = nil; +} + + +@end diff --git a/socketIOexample/socketio.objc/SocketIOJSONSerialization.h b/socketIOexample/socketio.objc/SocketIOJSONSerialization.h new file mode 100644 index 0000000..c2fff02 --- /dev/null +++ b/socketIOexample/socketio.objc/SocketIOJSONSerialization.h @@ -0,0 +1,31 @@ +// +// SocketIOJSONSerialization.h +// v0.4.0.1 ARC +// +// based on +// socketio-cocoa https://github.com/fpotter/socketio-cocoa +// by Fred Potter +// +// using +// https://github.com/square/SocketRocket +// https://github.com/stig/json-framework/ +// +// reusing some parts of +// /socket.io/socket.io.js +// +// Created by Philipp Kyeck http://beta-interactive.de +// +// Updated by +// samlown https://github.com/samlown +// kayleg https://github.com/kayleg +// taiyangc https://github.com/taiyangc +// + +#import + +@interface SocketIOJSONSerialization : NSObject + ++ (id) objectFromJSONData:(NSData *)data error:(NSError **)error; ++ (NSString *) JSONStringFromObject:(id)object error:(NSError **)error; + +@end diff --git a/socketIOexample/socketio.objc/SocketIOJSONSerialization.m b/socketIOexample/socketio.objc/SocketIOJSONSerialization.m new file mode 100644 index 0000000..5483cb2 --- /dev/null +++ b/socketIOexample/socketio.objc/SocketIOJSONSerialization.m @@ -0,0 +1,115 @@ +// +// SocketIOJSONSerialization.m +// v0.4.0.1 ARC +// +// based on +// socketio-cocoa https://github.com/fpotter/socketio-cocoa +// by Fred Potter +// +// using +// https://github.com/square/SocketRocket +// https://github.com/stig/json-framework/ +// +// reusing some parts of +// /socket.io/socket.io.js +// +// Created by Philipp Kyeck http://beta-interactive.de +// +// Updated by +// samlown https://github.com/samlown +// kayleg https://github.com/kayleg +// taiyangc https://github.com/taiyangc +// + +#import "SocketIOJSONSerialization.h" + +extern NSString * const SocketIOException; + +// covers the methods in SBJson and JSONKit +@interface NSObject (SocketIOJSONSerialization) + +// used by both JSONKit and SBJson +- (id) objectWithData:(NSData *)data; + +// Use by JSONKit serialization +- (NSString *) JSONString; +- (id) decoder; + +// Used by SBJsonWriter +- (NSString *) stringWithObject:(id)object; + +@end + +@implementation SocketIOJSONSerialization + ++ (id) objectFromJSONData:(NSData *)data error:(NSError **)error { + Class serializer; + + // try SBJson first + serializer = NSClassFromString(@"SBJsonParser"); + if (serializer) { + id parser; + id object; + + parser = [[serializer alloc] init]; + object = [parser objectWithData:data]; + + return object; + } + + // try Foundation's JSON coder, available in OS X 10.7/iOS 5.0 + serializer = NSClassFromString(@"NSJSONSerialization"); + if (serializer) { + return [serializer JSONObjectWithData:data options:0 error:error]; + } + + // lastly, try JSONKit + serializer = NSClassFromString(@"JSONDecoder"); + if (serializer) { + return [[serializer decoder] objectWithData:data]; + } + + // unable to find a suitable JSON deseralizer + [NSException raise:SocketIOException format:@"socket.IO-objc requires SBJson, JSONKit or an OS that has NSJSONSerialization."]; + + return nil; +} + ++ (NSString *) JSONStringFromObject:(id)object error:(NSError **)error { + Class serializer; + NSString *jsonString; + + jsonString = nil; + serializer = NSClassFromString(@"SBJsonWriter"); + if (serializer) { + id writer; + + writer = [[serializer alloc] init]; + jsonString = [writer stringWithObject:object]; + + return jsonString; + } + + serializer = NSClassFromString(@"NSJSONSerialization"); + if (serializer) { + NSData *data; + + data = [serializer dataWithJSONObject:object options:0 error:error]; + + jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + + return jsonString; + } + + // lastly, try JSONKit + if ([object respondsToSelector:@selector(JSONString)]) { + return [object JSONString]; + } + + // unable to find a suitable JSON seralizer + [NSException raise:SocketIOException format:@"socket.IO-objc requires SBJson, JSONKit or an OS that has NSJSONSerialization."]; + + return nil; +} + +@end diff --git a/socketIOexample/socketio.objc/SocketIOPacket.h b/socketIOexample/socketio.objc/SocketIOPacket.h new file mode 100644 index 0000000..98c355b --- /dev/null +++ b/socketIOexample/socketio.objc/SocketIOPacket.h @@ -0,0 +1,52 @@ +// +// SocketIOPacket.h +// v0.4.0.1 ARC +// +// based on +// socketio-cocoa https://github.com/fpotter/socketio-cocoa +// by Fred Potter +// +// using +// https://github.com/square/SocketRocket +// https://github.com/stig/json-framework/ +// +// reusing some parts of +// /socket.io/socket.io.js +// +// Created by Philipp Kyeck http://beta-interactive.de +// +// Updated by +// samlown https://github.com/samlown +// kayleg https://github.com/kayleg +// taiyangc https://github.com/taiyangc +// + +#import + +@interface SocketIOPacket : NSObject +{ + NSString *type; + NSString *pId; + NSString *ack; + NSString *name; + NSString *data; + NSArray *args; + NSString *endpoint; + NSArray *_types; +} + +@property (nonatomic, copy) NSString *type; +@property (nonatomic, copy) NSString *pId; +@property (nonatomic, copy) NSString *ack; +@property (nonatomic, copy) NSString *name; +@property (nonatomic, copy) NSString *data; +@property (nonatomic, copy) NSString *endpoint; +@property (nonatomic, copy) NSArray *args; + +- (id) initWithType:(NSString *)packetType; +- (id) initWithTypeIndex:(int)index; +- (id) dataAsJSON; +- (NSNumber *) typeAsNumber; +- (NSString *) typeForIndex:(int)index; + +@end diff --git a/socketIOexample/socketio.objc/SocketIOPacket.m b/socketIOexample/socketio.objc/SocketIOPacket.m new file mode 100644 index 0000000..6a5ed26 --- /dev/null +++ b/socketIOexample/socketio.objc/SocketIOPacket.m @@ -0,0 +1,104 @@ +// +// SocketIOPacket.h +// v0.4.0.1 ARC +// +// based on +// socketio-cocoa https://github.com/fpotter/socketio-cocoa +// by Fred Potter +// +// using +// https://github.com/square/SocketRocket +// https://github.com/stig/json-framework/ +// +// reusing some parts of +// /socket.io/socket.io.js +// +// Created by Philipp Kyeck http://beta-interactive.de +// +// Updated by +// samlown https://github.com/samlown +// kayleg https://github.com/kayleg +// taiyangc https://github.com/taiyangc +// + +#import "SocketIOPacket.h" +#import "SocketIOJSONSerialization.h" + +@implementation SocketIOPacket + +@synthesize type, pId, name, ack, data, args, endpoint; + +- (id) init +{ + self = [super init]; + if (self) { + _types = [NSArray arrayWithObjects: @"disconnect", + @"connect", + @"heartbeat", + @"message", + @"json", + @"event", + @"ack", + @"error", + @"noop", + nil]; + } + return self; +} + +- (id) initWithType:(NSString *)packetType +{ + self = [self init]; + if (self) { + self.type = packetType; + } + return self; +} + +- (id) initWithTypeIndex:(int)index +{ + self = [self init]; + if (self) { + self.type = [self typeForIndex:index]; + } + return self; +} + +- (id) dataAsJSON +{ + if (self.data) { + NSData *utf8Data = [self.data dataUsingEncoding:NSUTF8StringEncoding]; + return [SocketIOJSONSerialization objectFromJSONData:utf8Data error:nil]; + } + else { + return nil; + } +} + +- (NSNumber *) typeAsNumber +{ + NSUInteger index = [_types indexOfObject:self.type]; + NSNumber *num = [NSNumber numberWithUnsignedInteger:index]; + return num; +} + +- (NSString *) typeForIndex:(int)index +{ + return [_types objectAtIndex:index]; +} + +- (void) dealloc +{ + _types = nil; + + type = nil; + pId = nil; + name = nil; + ack = nil; + data = nil; + args = nil; + endpoint = nil; +} + +@end + diff --git a/socketIOexample/socketio.objc/SocketIOTransport.h b/socketIOexample/socketio.objc/SocketIOTransport.h new file mode 100644 index 0000000..3b3a28b --- /dev/null +++ b/socketIOexample/socketio.objc/SocketIOTransport.h @@ -0,0 +1,50 @@ +// +// SocketIOTransport.h +// v0.4.0.1 ARC +// +// based on +// socketio-cocoa https://github.com/fpotter/socketio-cocoa +// by Fred Potter +// +// using +// https://github.com/square/SocketRocket +// https://github.com/stig/json-framework/ +// +// reusing some parts of +// /socket.io/socket.io.js +// +// Created by Philipp Kyeck http://beta-interactive.de +// +// Updated by +// samlown https://github.com/samlown +// kayleg https://github.com/kayleg +// taiyangc https://github.com/taiyangc +// + +#import + +@protocol SocketIOTransportDelegate + +- (void) onData:(id)message; +- (void) onDisconnect:(NSError*)error; +- (void) onError:(NSError*)error; + +@property (nonatomic, readonly) NSString *host; +@property (nonatomic, readonly) NSInteger port; +@property (nonatomic, readonly) NSString *sid; +@property (nonatomic, readonly) NSTimeInterval heartbeatTimeout; +@property (nonatomic) BOOL useSecure; + +@end + +@protocol SocketIOTransport + +- (id) initWithDelegate:(id )delegate; +- (void) open; +- (void) close; +- (BOOL) isReady; +- (void) send:(NSString *)request; + +@property (nonatomic, unsafe_unretained) id delegate; + +@end diff --git a/socketIOexample/socketio.objc/SocketIOTransportWebsocket.h b/socketIOexample/socketio.objc/SocketIOTransportWebsocket.h new file mode 100644 index 0000000..656806e --- /dev/null +++ b/socketIOexample/socketio.objc/SocketIOTransportWebsocket.h @@ -0,0 +1,36 @@ +// +// SocketIOTransportWebsocket.h +// v0.4.0.1 ARC +// +// based on +// socketio-cocoa https://github.com/fpotter/socketio-cocoa +// by Fred Potter +// +// using +// https://github.com/square/SocketRocket +// https://github.com/stig/json-framework/ +// +// reusing some parts of +// /socket.io/socket.io.js +// +// Created by Philipp Kyeck http://beta-interactive.de +// +// Updated by +// samlown https://github.com/samlown +// kayleg https://github.com/kayleg +// taiyangc https://github.com/taiyangc +// + +#import + +#import "SRWebSocket.h" +#import "SocketIOTransport.h" + +@interface SocketIOTransportWebsocket : NSObject +{ + SRWebSocket *_webSocket; +} + +@property (nonatomic, unsafe_unretained) id delegate; + +@end diff --git a/socketIOexample/socketio.objc/SocketIOTransportWebsocket.m b/socketIOexample/socketio.objc/SocketIOTransportWebsocket.m new file mode 100644 index 0000000..8396469 --- /dev/null +++ b/socketIOexample/socketio.objc/SocketIOTransportWebsocket.m @@ -0,0 +1,128 @@ +// +// SocketIOTransportWebsocket.m +// v0.4.0.1 ARC +// +// based on +// socketio-cocoa https://github.com/fpotter/socketio-cocoa +// by Fred Potter +// +// using +// https://github.com/square/SocketRocket +// https://github.com/stig/json-framework/ +// +// reusing some parts of +// /socket.io/socket.io.js +// +// Created by Philipp Kyeck http://beta-interactive.de +// +// Updated by +// samlown https://github.com/samlown +// kayleg https://github.com/kayleg +// taiyangc https://github.com/taiyangc +// + +#import "SocketIOTransportWebsocket.h" +#import "SocketIO.h" + +#define DEBUG_LOGS 0 + +#if DEBUG_LOGS +#define DEBUGLOG(...) NSLog(__VA_ARGS__) +#else +#define DEBUGLOG(...) +#endif + +static NSString* kInsecureSocketURL = @"ws://%@/socket.io/1/websocket/%@"; +static NSString* kSecureSocketURL = @"wss://%@/socket.io/1/websocket/%@"; +static NSString* kInsecureSocketPortURL = @"ws://%@:%d/socket.io/1/websocket/%@"; +static NSString* kSecureSocketPortURL = @"wss://%@:%d/socket.io/1/websocket/%@"; + +@implementation SocketIOTransportWebsocket + +@synthesize delegate; + +- (id) initWithDelegate:(id)delegate_ +{ + self = [super init]; + if (self) { + self.delegate = delegate_; + } + return self; +} + +- (BOOL) isReady +{ + return _webSocket.readyState == SR_OPEN; +} + +- (void) open +{ + NSString *urlStr; + NSString *format; + if (delegate.port) { + format = delegate.useSecure ? kSecureSocketPortURL : kInsecureSocketPortURL; + urlStr = [NSString stringWithFormat:format, delegate.host, delegate.port, delegate.sid]; + } + else { + format = delegate.useSecure ? kSecureSocketURL : kInsecureSocketURL; + urlStr = [NSString stringWithFormat:format, delegate.host, delegate.sid]; + } + NSURL *url = [NSURL URLWithString:urlStr]; + + _webSocket = nil; + + _webSocket = [[SRWebSocket alloc] initWithURL:url]; + _webSocket.delegate = self; + DEBUGLOG(@"Opening %@", url); + [_webSocket open]; +} + +- (void) dealloc +{ + [_webSocket setDelegate:nil]; +} + +- (void) close +{ + [_webSocket close]; +} + +- (void) send:(NSString*)request +{ + [_webSocket send:request]; +} + + + +# pragma mark - +# pragma mark WebSocket Delegate Methods + +- (void) webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message +{ + [delegate onData:message]; +} + +- (void) webSocketDidOpen:(SRWebSocket *)webSocket +{ + DEBUGLOG(@"Socket opened."); +} + +- (void) webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error +{ + DEBUGLOG(@"Socket failed with error ... %@", [error localizedDescription]); + // Assuming this resulted in a disconnect + [delegate onDisconnect:error]; +} + +- (void) webSocket:(SRWebSocket *)webSocket + didCloseWithCode:(NSInteger)code + reason:(NSString *)reason + wasClean:(BOOL)wasClean +{ + DEBUGLOG(@"Socket closed. %@", reason); + [delegate onDisconnect:[NSError errorWithDomain:SocketIOError + code:SocketIOWebSocketClosed + userInfo:nil]]; +} + +@end diff --git a/socketIOexample/socketio.objc/SocketIOTransportXHR.h b/socketIOexample/socketio.objc/SocketIOTransportXHR.h new file mode 100644 index 0000000..ea2c90d --- /dev/null +++ b/socketIOexample/socketio.objc/SocketIOTransportXHR.h @@ -0,0 +1,37 @@ +// +// SocketIOTransportXHR.h +// v0.4.0.1 ARC +// +// based on +// socketio-cocoa https://github.com/fpotter/socketio-cocoa +// by Fred Potter +// +// using +// https://github.com/square/SocketRocket +// https://github.com/stig/json-framework/ +// +// reusing some parts of +// /socket.io/socket.io.js +// +// Created by Philipp Kyeck http://beta-interactive.de +// +// Updated by +// samlown https://github.com/samlown +// kayleg https://github.com/kayleg +// taiyangc https://github.com/taiyangc +// + +#import + +#import "SocketIOTransport.h" + +@interface SocketIOTransportXHR : NSObject +{ + NSString *_url; + NSMutableData *_data; + NSMutableDictionary *_polls; +} + +@property (nonatomic, unsafe_unretained) id delegate; + +@end diff --git a/socketIOexample/socketio.objc/SocketIOTransportXHR.m b/socketIOexample/socketio.objc/SocketIOTransportXHR.m new file mode 100644 index 0000000..446c1f8 --- /dev/null +++ b/socketIOexample/socketio.objc/SocketIOTransportXHR.m @@ -0,0 +1,255 @@ +// +// SocketIOTransportXHR.m +// v0.4.0.1 ARC +// +// based on +// socketio-cocoa https://github.com/fpotter/socketio-cocoa +// by Fred Potter +// +// using +// https://github.com/square/SocketRocket +// https://github.com/stig/json-framework/ +// +// reusing some parts of +// /socket.io/socket.io.js +// +// Created by Philipp Kyeck http://beta-interactive.de +// +// Updated by +// samlown https://github.com/samlown +// kayleg https://github.com/kayleg +// taiyangc https://github.com/taiyangc +// + +#import "SocketIOTransportXHR.h" +#import "SocketIO.h" + +#define DEBUG_LOGS 0 + +#if DEBUG_LOGS +#define DEBUGLOG(...) NSLog(__VA_ARGS__) +#else +#define DEBUGLOG(...) +#endif + +static NSString* kInsecureXHRURL = @"http://%@/socket.io/1/xhr-polling/%@"; +static NSString* kSecureXHRURL = @"https://%@/socket.io/1/xhr-polling/%@"; +static NSString* kInsecureXHRPortURL = @"http://%@:%d/socket.io/1/xhr-polling/%@"; +static NSString* kSecureXHRPortURL = @"https://%@:%d/socket.io/1/xhr-polling/%@"; + +@interface SocketIOTransportXHR (Private) +- (void) checkAndStartPoll; +- (void) poll:(NSString *)data; +- (void) poll:(NSString *)data retryNumber:(int)retry; +@end + +@implementation SocketIOTransportXHR + +@synthesize delegate; + +- (id) initWithDelegate:(id)delegate_ +{ + self = [super init]; + if (self) { + self.delegate = delegate_; + _data = [[NSMutableData alloc] init]; + _polls = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (void) open +{ + NSString *format; + if (delegate.port) { + format = delegate.useSecure ? kSecureXHRPortURL : kInsecureXHRPortURL; + _url = [NSString stringWithFormat:format, delegate.host, delegate.port, delegate.sid]; + } + else { + format = delegate.useSecure ? kSecureXHRURL : kInsecureXHRURL; + _url = [NSString stringWithFormat:format, delegate.host, delegate.sid]; + } + DEBUGLOG(@"Opening XHR @ %@", _url); + [self poll:nil]; +} + +- (void) close +{ + NSMutableDictionary *pollData; + NSURLConnection *conn; + for (NSString *key in _polls) { + pollData = [_polls objectForKey:key]; + conn = [pollData objectForKey:@"connection"]; + [conn cancel]; + } + [_polls removeAllObjects]; +} + +- (BOOL) isReady +{ + return YES; +} + +- (void) send:(NSString *)request +{ + [self poll:request]; +} + + +#pragma mark - +#pragma mark private methods + +- (void) checkAndStartPoll +{ + BOOL restart = NO; + // no polls currently running -> start one + if ([_polls count] == 0) { + restart = YES; + } + else { + restart = YES; + // look for polls w/o data -> if there, no need to restart + for (NSString *key in _polls) { + NSMutableDictionary *pollData = [_polls objectForKey:key]; + if ([pollData objectForKey:@"data"] == nil) { + restart = NO; + break; + } + } + } + + if (restart) { + [self poll:nil]; + } +} + +- (void) poll:(NSString *)data +{ + [self poll:data retryNumber:0]; +} + +- (void) poll:(NSString *)data retryNumber:(int)retry +{ + NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970]; + double unix = timeStamp * 1000; + NSString *url = [_url stringByAppendingString:[NSString stringWithFormat:@"?t=%.0f", unix]]; + + DEBUGLOG(@"---------------------------------------------------------------------------------------"); + DEBUGLOG(@"poll() %@", url); + + NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] + cachePolicy:NSURLRequestUseProtocolCachePolicy + timeoutInterval:[delegate heartbeatTimeout]]; + if (data != nil) { + DEBUGLOG(@"poll() %@", data); + [req setHTTPMethod:@"POST"]; + [req setValue:@"text/plain; charset=UTF-8" forHTTPHeaderField:@"Content-Type"]; + [req setHTTPBody:[data dataUsingEncoding:NSUTF8StringEncoding]]; + } + [req setValue:@"Keep-Alive" forHTTPHeaderField:@"Connection"]; + + NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:req delegate:self]; + + // add pollData to polls dictionary + NSMutableDictionary *pollData = [[NSMutableDictionary alloc] init]; + [pollData setObject:[NSNumber numberWithInt:retry] forKey:@"retries"]; + [pollData setObject:conn forKey:@"connection"]; + [pollData setValue:data forKey:@"data"]; + [_polls setObject:pollData forKey:conn.description]; + + [conn start]; +} + + +#pragma mark - +#pragma mark NSURLConnection delegate methods + + +- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response +{ + [_data setLength:0]; +} + +- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + DEBUGLOG(@"didReceiveData(): %@", data); + [_data appendData:data]; +} + +- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + DEBUGLOG(@"didFailWithError: %@", [error localizedDescription]); + + // retry 3 times or throw error + NSMutableDictionary *pollData = [_polls objectForKey:connection.description]; + NSString *data = [pollData objectForKey:@"data"]; + [_polls removeObjectForKey:connection.description]; + + NSNumber *retries = [pollData objectForKey:@"retries"]; + if ([retries intValue] < 2) { + [self poll:data retryNumber:[retries intValue] + 1]; + } + else { + NSMutableDictionary *errorInfo = [[NSMutableDictionary alloc] init]; + [errorInfo setValue:[error localizedDescription] forKey:@"reason"]; + [errorInfo setValue:data forKey:@"data"]; + + if ([delegate respondsToSelector:@selector(onError:)]) { + [delegate onError:[NSError errorWithDomain:SocketIOError + code:SocketIODataCouldNotBeSend + userInfo:errorInfo]]; + } + } +} + +// Sometimes Socket.IO "batches" up messages in one packet, +// so we have to split them. +// +- (NSArray *)packetsFromPayload:(NSString *)payload +{ + // "Batched" format is: + // �[packet_0 length]�[packet_0]�[packet_1 length]�[packet_1]�[packet_n length]�[packet_n] + + if([payload hasPrefix:@"\ufffd"]) { + // Payload has multiple packets, split based on the '�' character + // Skip the first character, then split + NSArray *split = [[payload substringFromIndex:1] componentsSeparatedByString:@"\ufffd"]; + + // Init array with [split count] / 2 because we only need the odd-numbered + NSMutableArray *packets = [NSMutableArray arrayWithCapacity:[split count]/2]; + + // Now all of the odd-numbered indices are the packets (1, 3, 5, etc.) + [split enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if(idx % 2 != 0) { + [packets addObject:obj]; + } + }]; + + NSLog(@"Parsed a payload!"); + return packets; + } else { + // Regular single-packet payload + return @[payload]; + } +} + +- (void) connectionDidFinishLoading:(NSURLConnection *)connection +{ + NSString *message = [[NSString alloc] initWithData:_data encoding:NSUTF8StringEncoding]; + DEBUGLOG(@"response: __%@__", message); + + if (![message isEqualToString:@"1"]) { + NSArray *messages = [self packetsFromPayload:message]; + [messages enumerateObjectsUsingBlock:^(NSString *message, NSUInteger idx, BOOL *stop) { + [delegate onData:message]; + }]; + } + + // remove current connection from pool + [_polls removeObjectForKey:connection.description]; + + [self checkAndStartPoll]; +} + + +@end diff --git a/socketIOexample/socketio.objc/SocketRocket/NSData+SRB64Additions.h b/socketIOexample/socketio.objc/SocketRocket/NSData+SRB64Additions.h new file mode 100755 index 0000000..7d61063 --- /dev/null +++ b/socketIOexample/socketio.objc/SocketRocket/NSData+SRB64Additions.h @@ -0,0 +1,24 @@ +// +// Copyright 2012 Square Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + + +@interface NSData (SRB64Additions) + +- (NSString *)SR_stringByBase64Encoding; + +@end diff --git a/socketIOexample/socketio.objc/SocketRocket/NSData+SRB64Additions.m b/socketIOexample/socketio.objc/SocketRocket/NSData+SRB64Additions.m new file mode 100755 index 0000000..5874a18 --- /dev/null +++ b/socketIOexample/socketio.objc/SocketRocket/NSData+SRB64Additions.m @@ -0,0 +1,39 @@ +// +// Copyright 2012 Square Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "NSData+SRB64Additions.h" +#import "base64.h" + + +@implementation NSData (SRB64Additions) + +- (NSString *)SR_stringByBase64Encoding; +{ + size_t buffer_size = (([self length] * 3 + 2) / 2); + + char *buffer = (char *)malloc(buffer_size); + + int len = b64_ntop([self bytes], [self length], buffer, buffer_size); + + if (len == -1) { + free(buffer); + return nil; + } else{ + return [[NSString alloc] initWithBytesNoCopy:buffer length:len encoding:NSUTF8StringEncoding freeWhenDone:YES]; + } +} + +@end diff --git a/socketIOexample/socketio.objc/SocketRocket/SRWebSocket.h b/socketIOexample/socketio.objc/SocketRocket/SRWebSocket.h new file mode 100755 index 0000000..2d40bb1 --- /dev/null +++ b/socketIOexample/socketio.objc/SocketRocket/SRWebSocket.h @@ -0,0 +1,114 @@ +// +// Copyright 2012 Square Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import + +typedef enum { + SR_CONNECTING = 0, + SR_OPEN = 1, + SR_CLOSING = 2, + SR_CLOSED = 3, +} SRReadyState; + +@class SRWebSocket; + +extern NSString *const SRWebSocketErrorDomain; + +#pragma mark - SRWebSocketDelegate + +@protocol SRWebSocketDelegate; + +#pragma mark - SRWebSocket + +@interface SRWebSocket : NSObject + +@property (nonatomic, assign) id delegate; + +@property (nonatomic, readonly) SRReadyState readyState; +@property (nonatomic, readonly, retain) NSURL *url; + +// This returns the negotiated protocol. +// It will be nil until after the handshake completes. +@property (nonatomic, readonly, copy) NSString *protocol; + +// Protocols should be an array of strings that turn into Sec-WebSocket-Protocol. +- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols; +- (id)initWithURLRequest:(NSURLRequest *)request; + +// Some helper constructors. +- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; +- (id)initWithURL:(NSURL *)url; + +// Delegate queue will be dispatch_main_queue by default. +// You cannot set both OperationQueue and dispatch_queue. +- (void)setDelegateOperationQueue:(NSOperationQueue*) queue; +- (void)setDelegateDispatchQueue:(dispatch_queue_t) queue; + +// By default, it will schedule itself on +[NSRunLoop SR_networkRunLoop] using defaultModes. +- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; +- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; + +// SRWebSockets are intended for one-time-use only. Open should be called once and only once. +- (void)open; + +- (void)close; +- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; + +// Send a UTF8 String or Data. +- (void)send:(id)data; + +@end + +#pragma mark - SRWebSocketDelegate + +@protocol SRWebSocketDelegate + +// message will either be an NSString if the server is using text +// or NSData if the server is using binary. +- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message; + +@optional + +- (void)webSocketDidOpen:(SRWebSocket *)webSocket; +- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error; +- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean; + +@end + +#pragma mark - NSURLRequest (CertificateAdditions) + +@interface NSURLRequest (CertificateAdditions) + +@property (nonatomic, retain, readonly) NSArray *SR_SSLPinnedCertificates; + +@end + +#pragma mark - NSMutableURLRequest (CertificateAdditions) + +@interface NSMutableURLRequest (CertificateAdditions) + +@property (nonatomic, retain) NSArray *SR_SSLPinnedCertificates; + +@end + +#pragma mark - NSRunLoop (SRWebSocket) + +@interface NSRunLoop (SRWebSocket) + ++ (NSRunLoop *)SR_networkRunLoop; + +@end diff --git a/socketIOexample/socketio.objc/SocketRocket/SRWebSocket.m b/socketIOexample/socketio.objc/SocketRocket/SRWebSocket.m new file mode 100755 index 0000000..db5039a --- /dev/null +++ b/socketIOexample/socketio.objc/SocketRocket/SRWebSocket.m @@ -0,0 +1,1757 @@ +// +// Copyright 2012 Square Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + + +#import "SRWebSocket.h" + +#if TARGET_OS_IPHONE +#define HAS_ICU +#endif + +#ifdef HAS_ICU +#import +#endif + +#if TARGET_OS_IPHONE +#import +#else +#import +#endif + +#import +#import + +#import "base64.h" +#import "NSData+SRB64Additions.h" + +#if OS_OBJECT_USE_OBJC_RETAIN_RELEASE +#define sr_dispatch_retain(x) +#define sr_dispatch_release(x) +#define maybe_bridge(x) ((__bridge void *) x) +#else +#define sr_dispatch_retain(x) dispatch_retain(x) +#define sr_dispatch_release(x) dispatch_release(x) +#define maybe_bridge(x) (x) +#endif + +#if !__has_feature(objc_arc) +#error SocketRocket muust be compiled with ARC enabled +#endif + + +typedef enum { + SROpCodeTextFrame = 0x1, + SROpCodeBinaryFrame = 0x2, + // 3-7 reserved. + SROpCodeConnectionClose = 0x8, + SROpCodePing = 0x9, + SROpCodePong = 0xA, + // B-F reserved. +} SROpCode; + +typedef enum { + SRStatusCodeNormal = 1000, + SRStatusCodeGoingAway = 1001, + SRStatusCodeProtocolError = 1002, + SRStatusCodeUnhandledType = 1003, + // 1004 reserved. + SRStatusNoStatusReceived = 1005, + // 1004-1006 reserved. + SRStatusCodeInvalidUTF8 = 1007, + SRStatusCodePolicyViolated = 1008, + SRStatusCodeMessageTooBig = 1009, +} SRStatusCode; + +typedef struct { + BOOL fin; +// BOOL rsv1; +// BOOL rsv2; +// BOOL rsv3; + uint8_t opcode; + BOOL masked; + uint64_t payload_length; +} frame_header; + +static NSString *const SRWebSocketAppendToSecKeyString = @"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + +static inline int32_t validate_dispatch_data_partial_string(NSData *data); +static inline dispatch_queue_t log_queue(); +static inline void SRFastLog(NSString *format, ...); + +@interface NSData (SRWebSocket) + +- (NSString *)stringBySHA1ThenBase64Encoding; + +@end + + +@interface NSString (SRWebSocket) + +- (NSString *)stringBySHA1ThenBase64Encoding; + +@end + + +@interface NSURL (SRWebSocket) + +// The origin isn't really applicable for a native application. +// So instead, just map ws -> http and wss -> https. +- (NSString *)SR_origin; + +@end + + +@interface _SRRunLoopThread : NSThread + +@property (nonatomic, readonly) NSRunLoop *runLoop; + +@end + + +static NSString *newSHA1String(const char *bytes, size_t length) { + uint8_t md[CC_SHA1_DIGEST_LENGTH]; + + CC_SHA1(bytes, length, md); + + size_t buffer_size = ((sizeof(md) * 3 + 2) / 2); + + char *buffer = (char *)malloc(buffer_size); + + int len = b64_ntop(md, CC_SHA1_DIGEST_LENGTH, buffer, buffer_size); + if (len == -1) { + free(buffer); + return nil; + } else{ + return [[NSString alloc] initWithBytesNoCopy:buffer length:len encoding:NSASCIIStringEncoding freeWhenDone:YES]; + } +} + +@implementation NSData (SRWebSocket) + +- (NSString *)stringBySHA1ThenBase64Encoding; +{ + return newSHA1String(self.bytes, self.length); +} + +@end + + +@implementation NSString (SRWebSocket) + +- (NSString *)stringBySHA1ThenBase64Encoding; +{ + return newSHA1String(self.UTF8String, self.length); +} + +@end + +NSString *const SRWebSocketErrorDomain = @"SRWebSocketErrorDomain"; + +// Returns number of bytes consumed. Returning 0 means you didn't match. +// Sends bytes to callback handler; +typedef size_t (^stream_scanner)(NSData *collected_data); + +typedef void (^data_callback)(SRWebSocket *webSocket, NSData *data); + +@interface SRIOConsumer : NSObject { + stream_scanner _scanner; + data_callback _handler; + size_t _bytesNeeded; + BOOL _readToCurrentFrame; + BOOL _unmaskBytes; +} +@property (nonatomic, copy, readonly) stream_scanner consumer; +@property (nonatomic, copy, readonly) data_callback handler; +@property (nonatomic, assign) size_t bytesNeeded; +@property (nonatomic, assign, readonly) BOOL readToCurrentFrame; +@property (nonatomic, assign, readonly) BOOL unmaskBytes; + +@end + +// This class is not thread-safe, and is expected to always be run on the same queue. +@interface SRIOConsumerPool : NSObject + +- (id)initWithBufferCapacity:(NSUInteger)poolSize; + +- (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; +- (void)returnConsumer:(SRIOConsumer *)consumer; + +@end + +@interface SRWebSocket () + +- (void)_writeData:(NSData *)data; +- (void)_closeWithProtocolError:(NSString *)message; +- (void)_failWithError:(NSError *)error; + +- (void)_disconnect; + +- (void)_readFrameNew; +- (void)_readFrameContinue; + +- (void)_pumpScanner; + +- (void)_pumpWriting; + +- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback; +- (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; +- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength; +- (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data_callback)dataHandler; +- (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler; + +- (void)_sendFrameWithOpcode:(SROpCode)opcode data:(id)data; + +- (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage; +- (void)_SR_commonInit; + +- (void)_initializeStreams; +- (void)_connect; + +@property (nonatomic) SRReadyState readyState; + +@property (nonatomic) NSOperationQueue *delegateOperationQueue; +@property (nonatomic) dispatch_queue_t delegateDispatchQueue; + +@end + + +@implementation SRWebSocket { + NSInteger _webSocketVersion; + + NSOperationQueue *_delegateOperationQueue; + dispatch_queue_t _delegateDispatchQueue; + + dispatch_queue_t _workQueue; + NSMutableArray *_consumers; + + NSInputStream *_inputStream; + NSOutputStream *_outputStream; + + NSMutableData *_readBuffer; + NSUInteger _readBufferOffset; + + NSMutableData *_outputBuffer; + NSUInteger _outputBufferOffset; + + uint8_t _currentFrameOpcode; + size_t _currentFrameCount; + size_t _readOpCount; + uint32_t _currentStringScanPosition; + NSMutableData *_currentFrameData; + + NSString *_closeReason; + + NSString *_secKey; + + BOOL _pinnedCertFound; + + uint8_t _currentReadMaskKey[4]; + size_t _currentReadMaskOffset; + + BOOL _consumerStopped; + + BOOL _closeWhenFinishedWriting; + BOOL _failed; + + BOOL _secure; + NSURLRequest *_urlRequest; + + CFHTTPMessageRef _receivedHTTPHeaders; + + BOOL _sentClose; + BOOL _didFail; + int _closeCode; + + BOOL _isPumping; + + NSMutableSet *_scheduledRunloops; + + // We use this to retain ourselves. + __strong SRWebSocket *_selfRetain; + + NSArray *_requestedProtocols; + SRIOConsumerPool *_consumerPool; +} + +@synthesize delegate = _delegate; +@synthesize url = _url; +@synthesize readyState = _readyState; +@synthesize protocol = _protocol; + +static __strong NSData *CRLFCRLF; + ++ (void)initialize; +{ + CRLFCRLF = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4]; +} + +- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols; +{ + self = [super init]; + if (self) { + assert(request.URL); + _url = request.URL; + _urlRequest = request; + + _requestedProtocols = [protocols copy]; + + [self _SR_commonInit]; + } + + return self; +} + +- (id)initWithURLRequest:(NSURLRequest *)request; +{ + return [self initWithURLRequest:request protocols:nil]; +} + +- (id)initWithURL:(NSURL *)url; +{ + return [self initWithURL:url protocols:nil]; +} + +- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; +{ + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; + return [self initWithURLRequest:request protocols:protocols]; +} + +- (void)_SR_commonInit; +{ + + NSString *scheme = _url.scheme.lowercaseString; + assert([scheme isEqualToString:@"ws"] || [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]); + + if ([scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]) { + _secure = YES; + } + + _readyState = SR_CONNECTING; + _consumerStopped = YES; + _webSocketVersion = 13; + + _workQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); + + // Going to set a specific on the queue so we can validate we're on the work queue + dispatch_queue_set_specific(_workQueue, (__bridge void *)self, maybe_bridge(_workQueue), NULL); + + _delegateDispatchQueue = dispatch_get_main_queue(); + sr_dispatch_retain(_delegateDispatchQueue); + + _readBuffer = [[NSMutableData alloc] init]; + _outputBuffer = [[NSMutableData alloc] init]; + + _currentFrameData = [[NSMutableData alloc] init]; + + _consumers = [[NSMutableArray alloc] init]; + + _consumerPool = [[SRIOConsumerPool alloc] init]; + + _scheduledRunloops = [[NSMutableSet alloc] init]; + + [self _initializeStreams]; + + // default handlers +} + +- (void)assertOnWorkQueue; +{ + assert(dispatch_get_specific((__bridge void *)self) == maybe_bridge(_workQueue)); +} + +- (void)dealloc +{ + _inputStream.delegate = nil; + _outputStream.delegate = nil; + + [_inputStream close]; + [_outputStream close]; + + sr_dispatch_release(_workQueue); + _workQueue = NULL; + + if (_receivedHTTPHeaders) { + CFRelease(_receivedHTTPHeaders); + _receivedHTTPHeaders = NULL; + } + + if (_delegateDispatchQueue) { + sr_dispatch_release(_delegateDispatchQueue); + _delegateDispatchQueue = NULL; + } +} + +#ifndef NDEBUG + +- (void)setReadyState:(SRReadyState)aReadyState; +{ + [self willChangeValueForKey:@"readyState"]; + assert(aReadyState > _readyState); + _readyState = aReadyState; + [self didChangeValueForKey:@"readyState"]; +} + +#endif + +- (void)open; +{ + assert(_url); + NSAssert(_readyState == SR_CONNECTING, @"Cannot call -(void)open on SRWebSocket more than once"); + + _selfRetain = self; + + [self _connect]; +} + +// Calls block on delegate queue +- (void)_performDelegateBlock:(dispatch_block_t)block; +{ + if (_delegateOperationQueue) { + [_delegateOperationQueue addOperationWithBlock:block]; + } else { + assert(_delegateDispatchQueue); + dispatch_async(_delegateDispatchQueue, block); + } +} + +- (void)setDelegateDispatchQueue:(dispatch_queue_t)queue; +{ + if (queue) { + sr_dispatch_retain(queue); + } + + if (_delegateDispatchQueue) { + sr_dispatch_release(_delegateDispatchQueue); + } + + _delegateDispatchQueue = queue; +} + +- (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage; +{ + NSString *acceptHeader = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(httpMessage, CFSTR("Sec-WebSocket-Accept"))); + + if (acceptHeader == nil) { + return NO; + } + + NSString *concattedString = [_secKey stringByAppendingString:SRWebSocketAppendToSecKeyString]; + NSString *expectedAccept = [concattedString stringBySHA1ThenBase64Encoding]; + + return [acceptHeader isEqualToString:expectedAccept]; +} + +- (void)_HTTPHeadersDidFinish; +{ + NSInteger responseCode = CFHTTPMessageGetResponseStatusCode(_receivedHTTPHeaders); + + if (responseCode >= 400) { + SRFastLog(@"Request failed with response code %d", responseCode); + [self _failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:2132 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"received bad response code from server %ld", (long)responseCode] forKey:NSLocalizedDescriptionKey]]]; + return; + + } + + if(![self _checkHandshake:_receivedHTTPHeaders]) { + [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Invalid Sec-WebSocket-Accept response"] forKey:NSLocalizedDescriptionKey]]]; + return; + } + + NSString *negotiatedProtocol = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(_receivedHTTPHeaders, CFSTR("Sec-WebSocket-Protocol"))); + if (negotiatedProtocol) { + // Make sure we requested the protocol + if ([_requestedProtocols indexOfObject:negotiatedProtocol] == NSNotFound) { + [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Server specified Sec-WebSocket-Protocol that wasn't requested"] forKey:NSLocalizedDescriptionKey]]]; + return; + } + + _protocol = negotiatedProtocol; + } + + self.readyState = SR_OPEN; + + if (!_didFail) { + [self _readFrameNew]; + } + + [self _performDelegateBlock:^{ + if ([self.delegate respondsToSelector:@selector(webSocketDidOpen:)]) { + [self.delegate webSocketDidOpen:self]; + }; + }]; +} + + +- (void)_readHTTPHeader; +{ + if (_receivedHTTPHeaders == NULL) { + _receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO); + } + + [self _readUntilHeaderCompleteWithCallback:^(SRWebSocket *self, NSData *data) { + CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length); + + if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) { + SRFastLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders))); + [self _HTTPHeadersDidFinish]; + } else { + [self _readHTTPHeader]; + } + }]; +} + +- (void)didConnect +{ + SRFastLog(@"Connected"); + CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (__bridge CFURLRef)_url, kCFHTTPVersion1_1); + + // Set host first so it defaults + CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Host"), (__bridge CFStringRef)(_url.port ? [NSString stringWithFormat:@"%@:%@", _url.host, _url.port] : _url.host)); + + NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16]; + SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes); + _secKey = [keyBytes SR_stringByBase64Encoding]; + assert([_secKey length] == 24); + + CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Upgrade"), CFSTR("websocket")); + CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Connection"), CFSTR("Upgrade")); + CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Key"), (__bridge CFStringRef)_secKey); + CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Version"), (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", (long)_webSocketVersion]); + + CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Origin"), (__bridge CFStringRef)_url.SR_origin); + + if (_requestedProtocols) { + CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Protocol"), (__bridge CFStringRef)[_requestedProtocols componentsJoinedByString:@", "]); + } + + [_urlRequest.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)key, (__bridge CFStringRef)obj); + }]; + + NSData *message = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(request)); + + CFRelease(request); + + [self _writeData:message]; + [self _readHTTPHeader]; +} + +- (void)_initializeStreams; +{ + NSInteger port = _url.port.integerValue; + if (port == 0) { + if (!_secure) { + port = 80; + } else { + port = 443; + } + } + NSString *host = _url.host; + + CFReadStreamRef readStream = NULL; + CFWriteStreamRef writeStream = NULL; + + CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream); + + _outputStream = CFBridgingRelease(writeStream); + _inputStream = CFBridgingRelease(readStream); + + + if (_secure) { + NSMutableDictionary *SSLOptions = [[NSMutableDictionary alloc] init]; + + [_outputStream setProperty:(__bridge id)kCFStreamSocketSecurityLevelNegotiatedSSL forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel]; + + // If we're using pinned certs, don't validate the certificate chain + if ([_urlRequest SR_SSLPinnedCertificates].count) { + [SSLOptions setValue:[NSNumber numberWithBool:NO] forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain]; + } + +#if DEBUG + [SSLOptions setValue:[NSNumber numberWithBool:NO] forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain]; + NSLog(@"SocketRocket: In debug mode. Allowing connection to any root cert"); +#endif + + [_outputStream setProperty:SSLOptions + forKey:(__bridge id)kCFStreamPropertySSLSettings]; + } + + _inputStream.delegate = self; + _outputStream.delegate = self; +} + +- (void)_connect; +{ + if (!_scheduledRunloops.count) { + [self scheduleInRunLoop:[NSRunLoop SR_networkRunLoop] forMode:NSDefaultRunLoopMode]; + } + + + [_outputStream open]; + [_inputStream open]; +} + +- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; +{ + [_outputStream scheduleInRunLoop:aRunLoop forMode:mode]; + [_inputStream scheduleInRunLoop:aRunLoop forMode:mode]; + + [_scheduledRunloops addObject:@[aRunLoop, mode]]; +} + +- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; +{ + [_outputStream removeFromRunLoop:aRunLoop forMode:mode]; + [_inputStream removeFromRunLoop:aRunLoop forMode:mode]; + + [_scheduledRunloops removeObject:@[aRunLoop, mode]]; +} + +- (void)close; +{ + [self closeWithCode:-1 reason:nil]; +} + +- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; +{ + assert(code); + dispatch_async(_workQueue, ^{ + if (self.readyState == SR_CLOSING || self.readyState == SR_CLOSED) { + return; + } + + BOOL wasConnecting = self.readyState == SR_CONNECTING; + + self.readyState = SR_CLOSING; + + SRFastLog(@"Closing with code %d reason %@", code, reason); + + if (wasConnecting) { + [self _disconnect]; + return; + } + + size_t maxMsgSize = [reason maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + NSMutableData *mutablePayload = [[NSMutableData alloc] initWithLength:sizeof(uint16_t) + maxMsgSize]; + NSData *payload = mutablePayload; + + ((uint16_t *)mutablePayload.mutableBytes)[0] = EndianU16_BtoN(code); + + if (reason) { + NSRange remainingRange = {0}; + + NSUInteger usedLength = 0; + + BOOL success = [reason getBytes:(char *)mutablePayload.mutableBytes + sizeof(uint16_t) maxLength:payload.length - sizeof(uint16_t) usedLength:&usedLength encoding:NSUTF8StringEncoding options:NSStringEncodingConversionExternalRepresentation range:NSMakeRange(0, reason.length) remainingRange:&remainingRange]; + + assert(success); + assert(remainingRange.length == 0); + + if (usedLength != maxMsgSize) { + payload = [payload subdataWithRange:NSMakeRange(0, usedLength + sizeof(uint16_t))]; + } + } + + + [self _sendFrameWithOpcode:SROpCodeConnectionClose data:payload]; + }); +} + +- (void)_closeWithProtocolError:(NSString *)message; +{ + // Need to shunt this on the _callbackQueue first to see if they received any messages + [self _performDelegateBlock:^{ + [self closeWithCode:SRStatusCodeProtocolError reason:message]; + dispatch_async(_workQueue, ^{ + [self _disconnect]; + }); + }]; +} + +- (void)_failWithError:(NSError *)error; +{ + dispatch_async(_workQueue, ^{ + if (self.readyState != SR_CLOSED) { + _failed = YES; + [self _performDelegateBlock:^{ + if ([self.delegate respondsToSelector:@selector(webSocket:didFailWithError:)]) { + [self.delegate webSocket:self didFailWithError:error]; + } + }]; + + self.readyState = SR_CLOSED; + _selfRetain = nil; + + SRFastLog(@"Failing with error %@", error.localizedDescription); + + [self _disconnect]; + } + }); +} + +- (void)_writeData:(NSData *)data; +{ + [self assertOnWorkQueue]; + + if (_closeWhenFinishedWriting) { + return; + } + [_outputBuffer appendData:data]; + [self _pumpWriting]; +} +- (void)send:(id)data; +{ + NSAssert(self.readyState != SR_CONNECTING, @"Invalid State: Cannot call send: until connection is open"); + // TODO: maybe not copy this for performance + data = [data copy]; + dispatch_async(_workQueue, ^{ + if ([data isKindOfClass:[NSString class]]) { + [self _sendFrameWithOpcode:SROpCodeTextFrame data:[(NSString *)data dataUsingEncoding:NSUTF8StringEncoding]]; + } else if ([data isKindOfClass:[NSData class]]) { + [self _sendFrameWithOpcode:SROpCodeBinaryFrame data:data]; + } else if (data == nil) { + [self _sendFrameWithOpcode:SROpCodeTextFrame data:data]; + } else { + assert(NO); + } + }); +} + +- (void)handlePing:(NSData *)pingData; +{ + // Need to pingpong this off _callbackQueue first to make sure messages happen in order + [self _performDelegateBlock:^{ + dispatch_async(_workQueue, ^{ + [self _sendFrameWithOpcode:SROpCodePong data:pingData]; + }); + }]; +} + +- (void)handlePong; +{ + // NOOP +} + +- (void)_handleMessage:(id)message +{ + SRFastLog(@"Received message"); + [self _performDelegateBlock:^{ + [self.delegate webSocket:self didReceiveMessage:message]; + }]; +} + + +static inline BOOL closeCodeIsValid(int closeCode) { + if (closeCode < 1000) { + return NO; + } + + if (closeCode >= 1000 && closeCode <= 1011) { + if (closeCode == 1004 || + closeCode == 1005 || + closeCode == 1006) { + return NO; + } + return YES; + } + + if (closeCode >= 3000 && closeCode <= 3999) { + return YES; + } + + if (closeCode >= 4000 && closeCode <= 4999) { + return YES; + } + + return NO; +} + +// Note from RFC: +// +// If there is a body, the first two +// bytes of the body MUST be a 2-byte unsigned integer (in network byte +// order) representing a status code with value /code/ defined in +// Section 7.4. Following the 2-byte integer the body MAY contain UTF-8 +// encoded data with value /reason/, the interpretation of which is not +// defined by this specification. + +- (void)handleCloseWithData:(NSData *)data; +{ + size_t dataSize = data.length; + __block uint16_t closeCode = 0; + + SRFastLog(@"Received close frame"); + + if (dataSize == 1) { + // TODO handle error + [self _closeWithProtocolError:@"Payload for close must be larger than 2 bytes"]; + return; + } else if (dataSize >= 2) { + [data getBytes:&closeCode length:sizeof(closeCode)]; + _closeCode = EndianU16_BtoN(closeCode); + if (!closeCodeIsValid(_closeCode)) { + [self _closeWithProtocolError:[NSString stringWithFormat:@"Cannot have close code of %d", _closeCode]]; + return; + } + if (dataSize > 2) { + _closeReason = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(2, dataSize - 2)] encoding:NSUTF8StringEncoding]; + if (!_closeReason) { + [self _closeWithProtocolError:@"Close reason MUST be valid UTF-8"]; + return; + } + } + } else { + _closeCode = SRStatusNoStatusReceived; + } + + [self assertOnWorkQueue]; + + if (self.readyState == SR_OPEN) { + [self closeWithCode:1000 reason:nil]; + } + dispatch_async(_workQueue, ^{ + [self _disconnect]; + }); +} + +- (void)_disconnect; +{ + [self assertOnWorkQueue]; + SRFastLog(@"Trying to disconnect"); + _closeWhenFinishedWriting = YES; + [self _pumpWriting]; +} + +- (void)_handleFrameWithData:(NSData *)frameData opCode:(NSInteger)opcode; +{ + // Check that the current data is valid UTF8 + + BOOL isControlFrame = (opcode == SROpCodePing || opcode == SROpCodePong || opcode == SROpCodeConnectionClose); + if (!isControlFrame) { + [self _readFrameNew]; + } else { + dispatch_async(_workQueue, ^{ + [self _readFrameContinue]; + }); + } + + switch (opcode) { + case SROpCodeTextFrame: { + NSString *str = [[NSString alloc] initWithData:frameData encoding:NSUTF8StringEncoding]; + if (str == nil && frameData) { + [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"]; + dispatch_async(_workQueue, ^{ + [self _disconnect]; + }); + + return; + } + [self _handleMessage:str]; + break; + } + case SROpCodeBinaryFrame: + [self _handleMessage:[frameData copy]]; + break; + case SROpCodeConnectionClose: + [self handleCloseWithData:frameData]; + break; + case SROpCodePing: + [self handlePing:frameData]; + break; + case SROpCodePong: + [self handlePong]; + break; + default: + [self _closeWithProtocolError:[NSString stringWithFormat:@"Unknown opcode %ld", (long)opcode]]; + // TODO: Handle invalid opcode + break; + } +} + +- (void)_handleFrameHeader:(frame_header)frame_header curData:(NSData *)curData; +{ + assert(frame_header.opcode != 0); + + if (self.readyState != SR_OPEN) { + return; + } + + + BOOL isControlFrame = (frame_header.opcode == SROpCodePing || frame_header.opcode == SROpCodePong || frame_header.opcode == SROpCodeConnectionClose); + + if (isControlFrame && !frame_header.fin) { + [self _closeWithProtocolError:@"Fragmented control frames not allowed"]; + return; + } + + if (isControlFrame && frame_header.payload_length >= 126) { + [self _closeWithProtocolError:@"Control frames cannot have payloads larger than 126 bytes"]; + return; + } + + if (!isControlFrame) { + _currentFrameOpcode = frame_header.opcode; + _currentFrameCount += 1; + } + + if (frame_header.payload_length == 0) { + if (isControlFrame) { + [self _handleFrameWithData:curData opCode:frame_header.opcode]; + } else { + if (frame_header.fin) { + [self _handleFrameWithData:_currentFrameData opCode:frame_header.opcode]; + } else { + // TODO add assert that opcode is not a control; + [self _readFrameContinue]; + } + } + } else { + [self _addConsumerWithDataLength:frame_header.payload_length callback:^(SRWebSocket *self, NSData *newData) { + if (isControlFrame) { + [self _handleFrameWithData:newData opCode:frame_header.opcode]; + } else { + if (frame_header.fin) { + [self _handleFrameWithData:self->_currentFrameData opCode:frame_header.opcode]; + } else { + // TODO add assert that opcode is not a control; + [self _readFrameContinue]; + } + + } + } readToCurrentFrame:!isControlFrame unmaskBytes:frame_header.masked]; + } +} + +/* From RFC: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-------+-+-------------+-------------------------------+ + |F|R|R|R| opcode|M| Payload len | Extended payload length | + |I|S|S|S| (4) |A| (7) | (16/64) | + |N|V|V|V| |S| | (if payload len==126/127) | + | |1|2|3| |K| | | + +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + | Extended payload length continued, if payload len == 127 | + + - - - - - - - - - - - - - - - +-------------------------------+ + | |Masking-key, if MASK set to 1 | + +-------------------------------+-------------------------------+ + | Masking-key (continued) | Payload Data | + +-------------------------------- - - - - - - - - - - - - - - - + + : Payload Data continued ... : + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + | Payload Data continued ... | + +---------------------------------------------------------------+ + */ + +static const uint8_t SRFinMask = 0x80; +static const uint8_t SROpCodeMask = 0x0F; +static const uint8_t SRRsvMask = 0x70; +static const uint8_t SRMaskMask = 0x80; +static const uint8_t SRPayloadLenMask = 0x7F; + + +- (void)_readFrameContinue; +{ + assert((_currentFrameCount == 0 && _currentFrameOpcode == 0) || (_currentFrameCount > 0 && _currentFrameOpcode > 0)); + + [self _addConsumerWithDataLength:2 callback:^(SRWebSocket *self, NSData *data) { + __block frame_header header = {0}; + + const uint8_t *headerBuffer = data.bytes; + assert(data.length >= 2); + + if (headerBuffer[0] & SRRsvMask) { + [self _closeWithProtocolError:@"Server used RSV bits"]; + return; + } + + uint8_t receivedOpcode = (SROpCodeMask & headerBuffer[0]); + + BOOL isControlFrame = (receivedOpcode == SROpCodePing || receivedOpcode == SROpCodePong || receivedOpcode == SROpCodeConnectionClose); + + if (!isControlFrame && receivedOpcode != 0 && self->_currentFrameCount > 0) { + [self _closeWithProtocolError:@"all data frames after the initial data frame must have opcode 0"]; + return; + } + + if (receivedOpcode == 0 && self->_currentFrameCount == 0) { + [self _closeWithProtocolError:@"cannot continue a message"]; + return; + } + + header.opcode = receivedOpcode == 0 ? self->_currentFrameOpcode : receivedOpcode; + + header.fin = !!(SRFinMask & headerBuffer[0]); + + + header.masked = !!(SRMaskMask & headerBuffer[1]); + header.payload_length = SRPayloadLenMask & headerBuffer[1]; + + headerBuffer = NULL; + + if (header.masked) { + [self _closeWithProtocolError:@"Client must receive unmasked data"]; + } + + size_t extra_bytes_needed = header.masked ? sizeof(_currentReadMaskKey) : 0; + + if (header.payload_length == 126) { + extra_bytes_needed += sizeof(uint16_t); + } else if (header.payload_length == 127) { + extra_bytes_needed += sizeof(uint64_t); + } + + if (extra_bytes_needed == 0) { + [self _handleFrameHeader:header curData:self->_currentFrameData]; + } else { + [self _addConsumerWithDataLength:extra_bytes_needed callback:^(SRWebSocket *self, NSData *data) { + size_t mapped_size = data.length; + const void *mapped_buffer = data.bytes; + size_t offset = 0; + + if (header.payload_length == 126) { + assert(mapped_size >= sizeof(uint16_t)); + uint16_t newLen = EndianU16_BtoN(*(uint16_t *)(mapped_buffer)); + header.payload_length = newLen; + offset += sizeof(uint16_t); + } else if (header.payload_length == 127) { + assert(mapped_size >= sizeof(uint64_t)); + header.payload_length = EndianU64_BtoN(*(uint64_t *)(mapped_buffer)); + offset += sizeof(uint64_t); + } else { + assert(header.payload_length < 126 && header.payload_length >= 0); + } + + + if (header.masked) { + assert(mapped_size >= sizeof(_currentReadMaskOffset) + offset); + memcpy(self->_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(self->_currentReadMaskKey)); + } + + [self _handleFrameHeader:header curData:self->_currentFrameData]; + } readToCurrentFrame:NO unmaskBytes:NO]; + } + } readToCurrentFrame:NO unmaskBytes:NO]; +} + +- (void)_readFrameNew; +{ + dispatch_async(_workQueue, ^{ + [_currentFrameData setLength:0]; + + _currentFrameOpcode = 0; + _currentFrameCount = 0; + _readOpCount = 0; + _currentStringScanPosition = 0; + + [self _readFrameContinue]; + }); +} + +- (void)_pumpWriting; +{ + [self assertOnWorkQueue]; + + NSUInteger dataLength = _outputBuffer.length; + if (dataLength - _outputBufferOffset > 0 && _outputStream.hasSpaceAvailable) { + NSInteger bytesWritten = [_outputStream write:_outputBuffer.bytes + _outputBufferOffset maxLength:dataLength - _outputBufferOffset]; + if (bytesWritten == -1) { + [self _failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:2145 userInfo:[NSDictionary dictionaryWithObject:@"Error writing to stream" forKey:NSLocalizedDescriptionKey]]]; + return; + } + + _outputBufferOffset += bytesWritten; + + if (_outputBufferOffset > 4096 && _outputBufferOffset > (_outputBuffer.length >> 1)) { + _outputBuffer = [[NSMutableData alloc] initWithBytes:(char *)_outputBuffer.bytes + _outputBufferOffset length:_outputBuffer.length - _outputBufferOffset]; + _outputBufferOffset = 0; + } + } + + if (_closeWhenFinishedWriting && + _outputBuffer.length - _outputBufferOffset == 0 && + (_inputStream.streamStatus != NSStreamStatusNotOpen && + _inputStream.streamStatus != NSStreamStatusClosed) && + !_sentClose) { + _sentClose = YES; + + [_outputStream close]; + [_inputStream close]; + + + for (NSArray *runLoop in [_scheduledRunloops copy]) { + [self unscheduleFromRunLoop:[runLoop objectAtIndex:0] forMode:[runLoop objectAtIndex:1]]; + } + + if (!_failed) { + [self _performDelegateBlock:^{ + if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { + [self.delegate webSocket:self didCloseWithCode:_closeCode reason:_closeReason wasClean:YES]; + } + }]; + } + + _selfRetain = nil; + } +} + +- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback; +{ + [self assertOnWorkQueue]; + [self _addConsumerWithScanner:consumer callback:callback dataLength:0]; +} + +- (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; +{ + [self assertOnWorkQueue]; + assert(dataLength); + + [_consumers addObject:[_consumerPool consumerWithScanner:nil handler:callback bytesNeeded:dataLength readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]]; + [self _pumpScanner]; +} + +- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength; +{ + [self assertOnWorkQueue]; + [_consumers addObject:[_consumerPool consumerWithScanner:consumer handler:callback bytesNeeded:dataLength readToCurrentFrame:NO unmaskBytes:NO]]; + [self _pumpScanner]; +} + + +static const char CRLFCRLFBytes[] = {'\r', '\n', '\r', '\n'}; + +- (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler; +{ + [self _readUntilBytes:CRLFCRLFBytes length:sizeof(CRLFCRLFBytes) callback:dataHandler]; +} + +- (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data_callback)dataHandler; +{ + // TODO optimize so this can continue from where we last searched + stream_scanner consumer = ^size_t(NSData *data) { + __block size_t found_size = 0; + __block size_t match_count = 0; + + size_t size = data.length; + const unsigned char *buffer = data.bytes; + for (size_t i = 0; i < size; i++ ) { + if (((const unsigned char *)buffer)[i] == ((const unsigned char *)bytes)[match_count]) { + match_count += 1; + if (match_count == length) { + found_size = i + 1; + break; + } + } else { + match_count = 0; + } + } + return found_size; + }; + [self _addConsumerWithScanner:consumer callback:dataHandler]; +} + + +// Returns true if did work +- (BOOL)_innerPumpScanner { + + BOOL didWork = NO; + + if (self.readyState >= SR_CLOSING) { + return didWork; + } + + if (!_consumers.count) { + return didWork; + } + + size_t curSize = _readBuffer.length - _readBufferOffset; + if (!curSize) { + return didWork; + } + + SRIOConsumer *consumer = [_consumers objectAtIndex:0]; + + size_t bytesNeeded = consumer.bytesNeeded; + + size_t foundSize = 0; + if (consumer.consumer) { + NSData *tempView = [NSData dataWithBytesNoCopy:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset freeWhenDone:NO]; + foundSize = consumer.consumer(tempView); + } else { + assert(consumer.bytesNeeded); + if (curSize >= bytesNeeded) { + foundSize = bytesNeeded; + } else if (consumer.readToCurrentFrame) { + foundSize = curSize; + } + } + + NSData *slice = nil; + if (consumer.readToCurrentFrame || foundSize) { + NSRange sliceRange = NSMakeRange(_readBufferOffset, foundSize); + slice = [_readBuffer subdataWithRange:sliceRange]; + + _readBufferOffset += foundSize; + + if (_readBufferOffset > 4096 && _readBufferOffset > (_readBuffer.length >> 1)) { + _readBuffer = [[NSMutableData alloc] initWithBytes:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset]; _readBufferOffset = 0; + } + + if (consumer.unmaskBytes) { + NSMutableData *mutableSlice = [slice mutableCopy]; + + NSUInteger len = mutableSlice.length; + uint8_t *bytes = mutableSlice.mutableBytes; + + for (NSUInteger i = 0; i < len; i++) { + bytes[i] = bytes[i] ^ _currentReadMaskKey[_currentReadMaskOffset % sizeof(_currentReadMaskKey)]; + _currentReadMaskOffset += 1; + } + + slice = mutableSlice; + } + + if (consumer.readToCurrentFrame) { + [_currentFrameData appendData:slice]; + + _readOpCount += 1; + + if (_currentFrameOpcode == SROpCodeTextFrame) { + // Validate UTF8 stuff. + size_t currentDataSize = _currentFrameData.length; + if (_currentFrameOpcode == SROpCodeTextFrame && currentDataSize > 0) { + // TODO: Optimize the crap out of this. Don't really have to copy all the data each time + + size_t scanSize = currentDataSize - _currentStringScanPosition; + + NSData *scan_data = [_currentFrameData subdataWithRange:NSMakeRange(_currentStringScanPosition, scanSize)]; + int32_t valid_utf8_size = validate_dispatch_data_partial_string(scan_data); + + if (valid_utf8_size == -1) { + [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"]; + dispatch_async(_workQueue, ^{ + [self _disconnect]; + }); + return didWork; + } else { + _currentStringScanPosition += valid_utf8_size; + } + } + + } + + consumer.bytesNeeded -= foundSize; + + if (consumer.bytesNeeded == 0) { + [_consumers removeObjectAtIndex:0]; + consumer.handler(self, nil); + [_consumerPool returnConsumer:consumer]; + didWork = YES; + } + } else if (foundSize) { + [_consumers removeObjectAtIndex:0]; + consumer.handler(self, slice); + [_consumerPool returnConsumer:consumer]; + didWork = YES; + } + } + return didWork; +} + +-(void)_pumpScanner; +{ + [self assertOnWorkQueue]; + + if (!_isPumping) { + _isPumping = YES; + } else { + return; + } + + while ([self _innerPumpScanner]) { + + } + + _isPumping = NO; +} + +//#define NOMASK + +static const size_t SRFrameHeaderOverhead = 32; + +- (void)_sendFrameWithOpcode:(SROpCode)opcode data:(id)data; +{ + [self assertOnWorkQueue]; + + NSAssert(data == nil || [data isKindOfClass:[NSData class]] || [data isKindOfClass:[NSString class]], @"Function expects nil, NSString or NSData"); + + size_t payloadLength = [data isKindOfClass:[NSString class]] ? [(NSString *)data lengthOfBytesUsingEncoding:NSUTF8StringEncoding] : [data length]; + + NSMutableData *frame = [[NSMutableData alloc] initWithLength:payloadLength + SRFrameHeaderOverhead]; + if (!frame) { + [self closeWithCode:SRStatusCodeMessageTooBig reason:@"Message too big"]; + return; + } + uint8_t *frame_buffer = (uint8_t *)[frame mutableBytes]; + + // set fin + frame_buffer[0] = SRFinMask | opcode; + + BOOL useMask = YES; +#ifdef NOMASK + useMask = NO; +#endif + + if (useMask) { + // set the mask and header + frame_buffer[1] |= SRMaskMask; + } + + size_t frame_buffer_size = 2; + + const uint8_t *unmasked_payload = NULL; + if ([data isKindOfClass:[NSData class]]) { + unmasked_payload = (uint8_t *)[data bytes]; + } else if ([data isKindOfClass:[NSString class]]) { + unmasked_payload = (const uint8_t *)[data UTF8String]; + } else { + assert(NO); + } + + if (payloadLength < 126) { + frame_buffer[1] |= payloadLength; + } else if (payloadLength <= UINT16_MAX) { + frame_buffer[1] |= 126; + *((uint16_t *)(frame_buffer + frame_buffer_size)) = EndianU16_BtoN((uint16_t)payloadLength); + frame_buffer_size += sizeof(uint16_t); + } else { + frame_buffer[1] |= 127; + *((uint64_t *)(frame_buffer + frame_buffer_size)) = EndianU64_BtoN((uint64_t)payloadLength); + frame_buffer_size += sizeof(uint64_t); + } + + if (!useMask) { + for (size_t i = 0; i < payloadLength; i++) { + frame_buffer[frame_buffer_size] = unmasked_payload[i]; + frame_buffer_size += 1; + } + } else { + uint8_t *mask_key = frame_buffer + frame_buffer_size; + SecRandomCopyBytes(kSecRandomDefault, sizeof(uint32_t), (uint8_t *)mask_key); + frame_buffer_size += sizeof(uint32_t); + + // TODO: could probably optimize this with SIMD + for (size_t i = 0; i < payloadLength; i++) { + frame_buffer[frame_buffer_size] = unmasked_payload[i] ^ mask_key[i % sizeof(uint32_t)]; + frame_buffer_size += 1; + } + } + + assert(frame_buffer_size <= [frame length]); + frame.length = frame_buffer_size; + + [self _writeData:frame]; +} + +- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode; +{ + if (_secure && !_pinnedCertFound && (eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) { + + NSArray *sslCerts = [_urlRequest SR_SSLPinnedCertificates]; + if (sslCerts) { + SecTrustRef secTrust = (__bridge SecTrustRef)[aStream propertyForKey:(__bridge id)kCFStreamPropertySSLPeerTrust]; + if (secTrust) { + NSInteger numCerts = SecTrustGetCertificateCount(secTrust); + for (NSInteger i = 0; i < numCerts && !_pinnedCertFound; i++) { + SecCertificateRef cert = SecTrustGetCertificateAtIndex(secTrust, i); + NSData *certData = CFBridgingRelease(SecCertificateCopyData(cert)); + + for (id ref in sslCerts) { + SecCertificateRef trustedCert = (__bridge SecCertificateRef)ref; + NSData *trustedCertData = CFBridgingRelease(SecCertificateCopyData(trustedCert)); + + if ([trustedCertData isEqualToData:certData]) { + _pinnedCertFound = YES; + break; + } + } + } + } + + if (!_pinnedCertFound) { + dispatch_async(_workQueue, ^{ + [self _failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:23556 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Invalid server cert"] forKey:NSLocalizedDescriptionKey]]]; + }); + return; + } + } + } + + dispatch_async(_workQueue, ^{ + switch (eventCode) { + case NSStreamEventOpenCompleted: { + SRFastLog(@"NSStreamEventOpenCompleted %@", aStream); + if (self.readyState >= SR_CLOSING) { + return; + } + assert(_readBuffer); + + if (self.readyState == SR_CONNECTING && aStream == _inputStream) { + [self didConnect]; + } + [self _pumpWriting]; + [self _pumpScanner]; + break; + } + + case NSStreamEventErrorOccurred: { + SRFastLog(@"NSStreamEventErrorOccurred %@ %@", aStream, [[aStream streamError] copy]); + /// TODO specify error better! + [self _failWithError:aStream.streamError]; + _readBufferOffset = 0; + [_readBuffer setLength:0]; + break; + + } + + case NSStreamEventEndEncountered: { + [self _pumpScanner]; + SRFastLog(@"NSStreamEventEndEncountered %@", aStream); + if (aStream.streamError) { + [self _failWithError:aStream.streamError]; + } else { + if (self.readyState != SR_CLOSED) { + self.readyState = SR_CLOSED; + _selfRetain = nil; + } + + if (!_sentClose && !_failed) { + _sentClose = YES; + // If we get closed in this state it's probably not clean because we should be sending this when we send messages + [self _performDelegateBlock:^{ + if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { + [self.delegate webSocket:self didCloseWithCode:0 reason:@"Stream end encountered" wasClean:NO]; + } + }]; + } + } + + break; + } + + case NSStreamEventHasBytesAvailable: { + SRFastLog(@"NSStreamEventHasBytesAvailable %@", aStream); + const int bufferSize = 2048; + uint8_t buffer[bufferSize]; + + while (_inputStream.hasBytesAvailable) { + int bytes_read = [_inputStream read:buffer maxLength:bufferSize]; + + if (bytes_read > 0) { + [_readBuffer appendBytes:buffer length:bytes_read]; + } else if (bytes_read < 0) { + [self _failWithError:_inputStream.streamError]; + } + + if (bytes_read != bufferSize) { + break; + } + }; + [self _pumpScanner]; + break; + } + + case NSStreamEventHasSpaceAvailable: { + SRFastLog(@"NSStreamEventHasSpaceAvailable %@", aStream); + [self _pumpWriting]; + break; + } + + default: + SRFastLog(@"(default) %@", aStream); + break; + } + }); +} + +@end + + +@implementation SRIOConsumer + +@synthesize bytesNeeded = _bytesNeeded; +@synthesize consumer = _scanner; +@synthesize handler = _handler; +@synthesize readToCurrentFrame = _readToCurrentFrame; +@synthesize unmaskBytes = _unmaskBytes; + +- (void)setupWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; +{ + _scanner = [scanner copy]; + _handler = [handler copy]; + _bytesNeeded = bytesNeeded; + _readToCurrentFrame = readToCurrentFrame; + _unmaskBytes = unmaskBytes; + assert(_scanner || _bytesNeeded); +} + + +@end + + +@implementation SRIOConsumerPool { + NSUInteger _poolSize; + NSMutableArray *_bufferedConsumers; +} + +- (id)initWithBufferCapacity:(NSUInteger)poolSize; +{ + self = [super init]; + if (self) { + _poolSize = poolSize; + _bufferedConsumers = [[NSMutableArray alloc] initWithCapacity:poolSize]; + } + return self; +} + +- (id)init +{ + return [self initWithBufferCapacity:8]; +} + +- (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; +{ + SRIOConsumer *consumer = nil; + if (_bufferedConsumers.count) { + consumer = [_bufferedConsumers lastObject]; + [_bufferedConsumers removeLastObject]; + } else { + consumer = [[SRIOConsumer alloc] init]; + } + + [consumer setupWithScanner:scanner handler:handler bytesNeeded:bytesNeeded readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]; + + return consumer; +} + +- (void)returnConsumer:(SRIOConsumer *)consumer; +{ + if (_bufferedConsumers.count < _poolSize) { + [_bufferedConsumers addObject:consumer]; + } +} + +@end + + +@implementation NSURLRequest (CertificateAdditions) + +- (NSArray *)SR_SSLPinnedCertificates; +{ + return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:self]; +} + +@end + +@implementation NSMutableURLRequest (CertificateAdditions) + +- (NSArray *)SR_SSLPinnedCertificates; +{ + return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:self]; +} + +- (void)setSR_SSLPinnedCertificates:(NSArray *)SR_SSLPinnedCertificates; +{ + [NSURLProtocol setProperty:SR_SSLPinnedCertificates forKey:@"SR_SSLPinnedCertificates" inRequest:self]; +} + +@end + +@implementation NSURL (SRWebSocket) + +- (NSString *)SR_origin; +{ + NSString *scheme = [self.scheme lowercaseString]; + + if ([scheme isEqualToString:@"wss"]) { + scheme = @"https"; + } else if ([scheme isEqualToString:@"ws"]) { + scheme = @"http"; + } + + if (self.port) { + return [NSString stringWithFormat:@"%@://%@:%@/", scheme, self.host, self.port]; + } else { + return [NSString stringWithFormat:@"%@://%@/", scheme, self.host]; + } +} + +@end + +static inline dispatch_queue_t log_queue() { + static dispatch_queue_t queue = 0; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + queue = dispatch_queue_create("fast log queue", DISPATCH_QUEUE_SERIAL); + }); + + return queue; +} + +//#define SR_ENABLE_LOG + +static inline void SRFastLog(NSString *format, ...) { +#ifdef SR_ENABLE_LOG + __block va_list arg_list; + va_start (arg_list, format); + + NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list]; + + va_end(arg_list); + + NSLog(@"[SR] %@", formattedString); +#endif +} + + +#ifdef HAS_ICU + +static inline int32_t validate_dispatch_data_partial_string(NSData *data) { + const void * contents = [data bytes]; + long size = [data length]; + + const uint8_t *str = (const uint8_t *)contents; + + UChar32 codepoint = 1; + int32_t offset = 0; + int32_t lastOffset = 0; + while(offset < size && codepoint > 0) { + lastOffset = offset; + U8_NEXT(str, offset, size, codepoint); + } + + if (codepoint == -1) { + // Check to see if the last byte is valid or whether it was just continuing + if (!U8_IS_LEAD(str[lastOffset]) || U8_COUNT_TRAIL_BYTES(str[lastOffset]) + lastOffset < (int32_t)size) { + + size = -1; + } else { + uint8_t leadByte = str[lastOffset]; + U8_MASK_LEAD_BYTE(leadByte, U8_COUNT_TRAIL_BYTES(leadByte)); + + for (int i = lastOffset + 1; i < offset; i++) { + if (U8_IS_SINGLE(str[i]) || U8_IS_LEAD(str[i]) || !U8_IS_TRAIL(str[i])) { + size = -1; + } + } + + if (size != -1) { + size = lastOffset; + } + } + } + + if (size != -1 && ![[NSString alloc] initWithBytesNoCopy:(char *)[data bytes] length:size encoding:NSUTF8StringEncoding freeWhenDone:NO]) { + size = -1; + } + + return size; +} + +#else + +// This is a hack, and probably not optimal +static inline int32_t validate_dispatch_data_partial_string(NSData *data) { + static const int maxCodepointSize = 3; + + for (int i = 0; i < maxCodepointSize; i++) { + NSString *str = [[NSString alloc] initWithBytesNoCopy:(char *)data.bytes length:data.length - i encoding:NSUTF8StringEncoding freeWhenDone:NO]; + if (str) { + return data.length - i; + } + } + + return -1; +} + +#endif + +static _SRRunLoopThread *networkThread = nil; +static NSRunLoop *networkRunLoop = nil; + +@implementation NSRunLoop (SRWebSocket) + ++ (NSRunLoop *)SR_networkRunLoop { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + networkThread = [[_SRRunLoopThread alloc] init]; + networkThread.name = @"com.squareup.SocketRocket.NetworkThread"; + [networkThread start]; + networkRunLoop = networkThread.runLoop; + }); + + return networkRunLoop; +} + +@end + + +@implementation _SRRunLoopThread { + dispatch_group_t _waitGroup; +} + +@synthesize runLoop = _runLoop; + +- (void)dealloc +{ + sr_dispatch_release(_waitGroup); +} + +- (id)init +{ + self = [super init]; + if (self) { + _waitGroup = dispatch_group_create(); + dispatch_group_enter(_waitGroup); + } + return self; +} + +- (void)main; +{ + @autoreleasepool { + _runLoop = [NSRunLoop currentRunLoop]; + dispatch_group_leave(_waitGroup); + + NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate distantFuture] interval:0.0 target:nil selector:nil userInfo:nil repeats:NO]; + [_runLoop addTimer:timer forMode:NSDefaultRunLoopMode]; + + while ([_runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) { + + } + assert(NO); + } +} + +- (NSRunLoop *)runLoop; +{ + dispatch_group_wait(_waitGroup, DISPATCH_TIME_FOREVER); + return _runLoop; +} + +@end diff --git a/socketIOexample/socketio.objc/SocketRocket/SocketRocket-Prefix.pch b/socketIOexample/socketio.objc/SocketRocket/SocketRocket-Prefix.pch new file mode 100755 index 0000000..8c32c82 --- /dev/null +++ b/socketIOexample/socketio.objc/SocketRocket/SocketRocket-Prefix.pch @@ -0,0 +1,27 @@ +// +// Copyright 2012 Square Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __OBJC__ + #import +#endif + +#ifdef __cplusplus +} +#endif diff --git a/socketIOexample/socketio.objc/SocketRocket/base64.c b/socketIOexample/socketio.objc/SocketRocket/base64.c new file mode 100755 index 0000000..1d76d16 --- /dev/null +++ b/socketIOexample/socketio.objc/SocketRocket/base64.c @@ -0,0 +1,314 @@ +/* $OpenBSD: base64.c,v 1.5 2006/10/21 09:55:03 otto Exp $ */ + +/* + * Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* OPENBSD ORIGINAL: lib/libc/net/base64.c */ + + +#if (!defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP)) || (!defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON)) + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "base64.h" + +static const char Base64[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +#if !defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP) +int +b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) +{ + size_t datalength = 0; + u_char input[3]; + u_char output[4]; + u_int i; + + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (datalength); +} +#endif /* !defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP) */ + +#if !defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON) + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int +b64_pton(char const *src, u_char *target, size_t targsize) +{ + u_int tarindex, state; + int ch; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if (tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = ((pos - Base64) & 0x0f) + << 4 ; + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if (tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = ((pos - Base64) & 0x03) + << 6; + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for (; ch != '\0'; ch = *src++) + if (!isspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for (; ch != '\0'; ch = *src++) + if (!isspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} + +#endif /* !defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON) */ +#endif diff --git a/socketIOexample/socketio.objc/SocketRocket/base64.h b/socketIOexample/socketio.objc/SocketRocket/base64.h new file mode 100755 index 0000000..c345e2e --- /dev/null +++ b/socketIOexample/socketio.objc/SocketRocket/base64.h @@ -0,0 +1,34 @@ +// Copyright 2012 Square Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + + +#ifndef SocketRocket_base64_h +#define SocketRocket_base64_h + +#include + +extern int +b64_ntop(u_char const *src, + size_t srclength, + char *target, + size_t targsize); + +extern int +b64_pton(char const *src, + u_char *target, + size_t targsize); + + +#endif diff --git a/socketio.xcodeproj/project.pbxproj b/socketio.xcodeproj/project.pbxproj deleted file mode 100644 index d7f8c71..0000000 --- a/socketio.xcodeproj/project.pbxproj +++ /dev/null @@ -1,546 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - ADB69183142F5DF10095DB55 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ADB69182142F5DF10095DB55 /* UIKit.framework */; }; - ADB69185142F5DF10095DB55 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ADB69184142F5DF10095DB55 /* Foundation.framework */; }; - ADB69187142F5DF10095DB55 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ADB69186142F5DF10095DB55 /* CoreGraphics.framework */; }; - ADB6918D142F5DF10095DB55 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = ADB6918B142F5DF10095DB55 /* InfoPlist.strings */; }; - ADB6918F142F5DF10095DB55 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB6918E142F5DF10095DB55 /* main.m */; }; - ADB69193142F5DF10095DB55 /* socketioAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB69192142F5DF10095DB55 /* socketioAppDelegate.m */; }; - ADB69196142F5DF10095DB55 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = ADB69194142F5DF10095DB55 /* MainWindow.xib */; }; - ADB69199142F5DF10095DB55 /* socketioViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB69198142F5DF10095DB55 /* socketioViewController.m */; }; - ADB6919C142F5DF10095DB55 /* socketioViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = ADB6919A142F5DF10095DB55 /* socketioViewController.xib */; }; - ADB6925B142F68730095DB55 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ADB6925A142F68730095DB55 /* CFNetwork.framework */; }; - ADB6925D142F68860095DB55 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ADB6925C142F68860095DB55 /* SystemConfiguration.framework */; }; - ADB69276142F69120095DB55 /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = ADB69275142F69120095DB55 /* libxml2.dylib */; }; - ADB692D9142F6E0F0095DB55 /* NSObject+SBJson.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB692C1142F6E0F0095DB55 /* NSObject+SBJson.m */; }; - ADB692DA142F6E0F0095DB55 /* SBJsonParser.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB692C4142F6E0F0095DB55 /* SBJsonParser.m */; }; - ADB692DB142F6E0F0095DB55 /* SBJsonStreamParser.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB692C6142F6E0F0095DB55 /* SBJsonStreamParser.m */; }; - ADB692DC142F6E0F0095DB55 /* SBJsonStreamParserAccumulator.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB692C8142F6E0F0095DB55 /* SBJsonStreamParserAccumulator.m */; }; - ADB692DD142F6E0F0095DB55 /* SBJsonStreamParserAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB692CA142F6E0F0095DB55 /* SBJsonStreamParserAdapter.m */; }; - ADB692DE142F6E0F0095DB55 /* SBJsonStreamParserState.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB692CC142F6E0F0095DB55 /* SBJsonStreamParserState.m */; }; - ADB692DF142F6E0F0095DB55 /* SBJsonStreamWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB692CE142F6E0F0095DB55 /* SBJsonStreamWriter.m */; }; - ADB692E0142F6E0F0095DB55 /* SBJsonStreamWriterAccumulator.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB692D0142F6E0F0095DB55 /* SBJsonStreamWriterAccumulator.m */; }; - ADB692E1142F6E0F0095DB55 /* SBJsonStreamWriterState.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB692D2142F6E0F0095DB55 /* SBJsonStreamWriterState.m */; }; - ADB692E2142F6E0F0095DB55 /* SBJsonTokeniser.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB692D4142F6E0F0095DB55 /* SBJsonTokeniser.m */; }; - ADB692E3142F6E0F0095DB55 /* SBJsonUTF8Stream.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB692D6142F6E0F0095DB55 /* SBJsonUTF8Stream.m */; }; - ADB692E4142F6E0F0095DB55 /* SBJsonWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB692D8142F6E0F0095DB55 /* SBJsonWriter.m */; }; - ADB692E7142F6E4E0095DB55 /* SocketIO.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB692E6142F6E4E0095DB55 /* SocketIO.m */; }; - ADB692F0142F6EB00095DB55 /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB692EF142F6EB00095DB55 /* Reachability.m */; }; - ADB6937D142F6EE80095DB55 /* ASIAuthenticationDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB6936E142F6EE80095DB55 /* ASIAuthenticationDialog.m */; }; - ADB6937E142F6EE80095DB55 /* ASIInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB69370142F6EE80095DB55 /* ASIInputStream.m */; }; - ADB6937F142F6EE80095DB55 /* ASIFormDataRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB69372142F6EE80095DB55 /* ASIFormDataRequest.m */; }; - ADB69380142F6EE80095DB55 /* ASIHTTPRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB69374142F6EE80095DB55 /* ASIHTTPRequest.m */; }; - ADB69381142F6EE80095DB55 /* ASINetworkQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB69376142F6EE80095DB55 /* ASINetworkQueue.m */; }; - ADB69382142F6EE80095DB55 /* ASIDownloadCache.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB69378142F6EE80095DB55 /* ASIDownloadCache.m */; }; - ADB69383142F6EE80095DB55 /* ASIDataDecompressor.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB6937A142F6EE80095DB55 /* ASIDataDecompressor.m */; }; - ADB69384142F6EE80095DB55 /* ASIDataCompressor.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB6937C142F6EE80095DB55 /* ASIDataCompressor.m */; }; - ADB69387142F6F230095DB55 /* ASIWebPageRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB69386142F6F230095DB55 /* ASIWebPageRequest.m */; }; - ADB69391142F6F750095DB55 /* AsyncSocket Documentation.html in Resources */ = {isa = PBXBuildFile; fileRef = ADB69389142F6F750095DB55 /* AsyncSocket Documentation.html */; }; - ADB69392142F6F750095DB55 /* AsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB6938B142F6F750095DB55 /* AsyncSocket.m */; }; - ADB69393142F6F750095DB55 /* AsyncUdpSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB6938D142F6F750095DB55 /* AsyncUdpSocket.m */; }; - ADB69394142F6F750095DB55 /* changes.txt in Resources */ = {isa = PBXBuildFile; fileRef = ADB6938E142F6F750095DB55 /* changes.txt */; }; - ADB69395142F6F750095DB55 /* WebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB69390142F6F750095DB55 /* WebSocket.m */; }; - ADB69398142F6F910095DB55 /* RegexKitLite.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB69397142F6F910095DB55 /* RegexKitLite.m */; }; - ADB6939A142F70360095DB55 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ADB69399142F70360095DB55 /* MobileCoreServices.framework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - ADB6917E142F5DF10095DB55 /* socketio.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = socketio.app; sourceTree = BUILT_PRODUCTS_DIR; }; - ADB69182142F5DF10095DB55 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; - ADB69184142F5DF10095DB55 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; - ADB69186142F5DF10095DB55 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; - ADB6918A142F5DF10095DB55 /* socketio-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "socketio-Info.plist"; sourceTree = ""; }; - ADB6918C142F5DF10095DB55 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - ADB6918E142F5DF10095DB55 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - ADB69190142F5DF10095DB55 /* socketio-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "socketio-Prefix.pch"; sourceTree = ""; }; - ADB69191142F5DF10095DB55 /* socketioAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = socketioAppDelegate.h; sourceTree = ""; }; - ADB69192142F5DF10095DB55 /* socketioAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = socketioAppDelegate.m; sourceTree = ""; }; - ADB69195142F5DF10095DB55 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainWindow.xib; sourceTree = ""; }; - ADB69197142F5DF10095DB55 /* socketioViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = socketioViewController.h; sourceTree = ""; }; - ADB69198142F5DF10095DB55 /* socketioViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = socketioViewController.m; sourceTree = ""; }; - ADB6919B142F5DF10095DB55 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/socketioViewController.xib; sourceTree = ""; }; - ADB6925A142F68730095DB55 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; - ADB6925C142F68860095DB55 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; - ADB69275142F69120095DB55 /* libxml2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.dylib; path = usr/lib/libxml2.dylib; sourceTree = SDKROOT; }; - ADB692BD142F6D400095DB55 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; - ADB692C0142F6E0F0095DB55 /* NSObject+SBJson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+SBJson.h"; sourceTree = ""; }; - ADB692C1142F6E0F0095DB55 /* NSObject+SBJson.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+SBJson.m"; sourceTree = ""; }; - ADB692C2142F6E0F0095DB55 /* SBJson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJson.h; sourceTree = ""; }; - ADB692C3142F6E0F0095DB55 /* SBJsonParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonParser.h; sourceTree = ""; }; - ADB692C4142F6E0F0095DB55 /* SBJsonParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonParser.m; sourceTree = ""; }; - ADB692C5142F6E0F0095DB55 /* SBJsonStreamParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParser.h; sourceTree = ""; }; - ADB692C6142F6E0F0095DB55 /* SBJsonStreamParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParser.m; sourceTree = ""; }; - ADB692C7142F6E0F0095DB55 /* SBJsonStreamParserAccumulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParserAccumulator.h; sourceTree = ""; }; - ADB692C8142F6E0F0095DB55 /* SBJsonStreamParserAccumulator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParserAccumulator.m; sourceTree = ""; }; - ADB692C9142F6E0F0095DB55 /* SBJsonStreamParserAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParserAdapter.h; sourceTree = ""; }; - ADB692CA142F6E0F0095DB55 /* SBJsonStreamParserAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParserAdapter.m; sourceTree = ""; }; - ADB692CB142F6E0F0095DB55 /* SBJsonStreamParserState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParserState.h; sourceTree = ""; }; - ADB692CC142F6E0F0095DB55 /* SBJsonStreamParserState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParserState.m; sourceTree = ""; }; - ADB692CD142F6E0F0095DB55 /* SBJsonStreamWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamWriter.h; sourceTree = ""; }; - ADB692CE142F6E0F0095DB55 /* SBJsonStreamWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamWriter.m; sourceTree = ""; }; - ADB692CF142F6E0F0095DB55 /* SBJsonStreamWriterAccumulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamWriterAccumulator.h; sourceTree = ""; }; - ADB692D0142F6E0F0095DB55 /* SBJsonStreamWriterAccumulator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamWriterAccumulator.m; sourceTree = ""; }; - ADB692D1142F6E0F0095DB55 /* SBJsonStreamWriterState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamWriterState.h; sourceTree = ""; }; - ADB692D2142F6E0F0095DB55 /* SBJsonStreamWriterState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamWriterState.m; sourceTree = ""; }; - ADB692D3142F6E0F0095DB55 /* SBJsonTokeniser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonTokeniser.h; sourceTree = ""; }; - ADB692D4142F6E0F0095DB55 /* SBJsonTokeniser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonTokeniser.m; sourceTree = ""; }; - ADB692D5142F6E0F0095DB55 /* SBJsonUTF8Stream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonUTF8Stream.h; sourceTree = ""; }; - ADB692D6142F6E0F0095DB55 /* SBJsonUTF8Stream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonUTF8Stream.m; sourceTree = ""; }; - ADB692D7142F6E0F0095DB55 /* SBJsonWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonWriter.h; sourceTree = ""; }; - ADB692D8142F6E0F0095DB55 /* SBJsonWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonWriter.m; sourceTree = ""; }; - ADB692E5142F6E4E0095DB55 /* SocketIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SocketIO.h; sourceTree = ""; }; - ADB692E6142F6E4E0095DB55 /* SocketIO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SocketIO.m; sourceTree = ""; }; - ADB692EE142F6EB00095DB55 /* Reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = ""; }; - ADB692EF142F6EB00095DB55 /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = ""; }; - ADB69369142F6EE80095DB55 /* ASIHTTPRequestConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIHTTPRequestConfig.h; sourceTree = ""; }; - ADB6936A142F6EE80095DB55 /* ASICacheDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASICacheDelegate.h; sourceTree = ""; }; - ADB6936B142F6EE80095DB55 /* ASIHTTPRequestDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIHTTPRequestDelegate.h; sourceTree = ""; }; - ADB6936C142F6EE80095DB55 /* ASIProgressDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIProgressDelegate.h; sourceTree = ""; }; - ADB6936D142F6EE80095DB55 /* ASIAuthenticationDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIAuthenticationDialog.h; sourceTree = ""; }; - ADB6936E142F6EE80095DB55 /* ASIAuthenticationDialog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASIAuthenticationDialog.m; sourceTree = ""; }; - ADB6936F142F6EE80095DB55 /* ASIInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIInputStream.h; sourceTree = ""; }; - ADB69370142F6EE80095DB55 /* ASIInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASIInputStream.m; sourceTree = ""; }; - ADB69371142F6EE80095DB55 /* ASIFormDataRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIFormDataRequest.h; sourceTree = ""; }; - ADB69372142F6EE80095DB55 /* ASIFormDataRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASIFormDataRequest.m; sourceTree = ""; }; - ADB69373142F6EE80095DB55 /* ASIHTTPRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIHTTPRequest.h; sourceTree = ""; }; - ADB69374142F6EE80095DB55 /* ASIHTTPRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASIHTTPRequest.m; sourceTree = ""; }; - ADB69375142F6EE80095DB55 /* ASINetworkQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASINetworkQueue.h; sourceTree = ""; }; - ADB69376142F6EE80095DB55 /* ASINetworkQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASINetworkQueue.m; sourceTree = ""; }; - ADB69377142F6EE80095DB55 /* ASIDownloadCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIDownloadCache.h; sourceTree = ""; }; - ADB69378142F6EE80095DB55 /* ASIDownloadCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASIDownloadCache.m; sourceTree = ""; }; - ADB69379142F6EE80095DB55 /* ASIDataDecompressor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIDataDecompressor.h; sourceTree = ""; }; - ADB6937A142F6EE80095DB55 /* ASIDataDecompressor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASIDataDecompressor.m; sourceTree = ""; }; - ADB6937B142F6EE80095DB55 /* ASIDataCompressor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIDataCompressor.h; sourceTree = ""; }; - ADB6937C142F6EE80095DB55 /* ASIDataCompressor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASIDataCompressor.m; sourceTree = ""; }; - ADB69385142F6F230095DB55 /* ASIWebPageRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIWebPageRequest.h; sourceTree = ""; }; - ADB69386142F6F230095DB55 /* ASIWebPageRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASIWebPageRequest.m; sourceTree = ""; }; - ADB69389142F6F750095DB55 /* AsyncSocket Documentation.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "AsyncSocket Documentation.html"; sourceTree = ""; }; - ADB6938A142F6F750095DB55 /* AsyncSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AsyncSocket.h; sourceTree = ""; }; - ADB6938B142F6F750095DB55 /* AsyncSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AsyncSocket.m; sourceTree = ""; }; - ADB6938C142F6F750095DB55 /* AsyncUdpSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AsyncUdpSocket.h; sourceTree = ""; }; - ADB6938D142F6F750095DB55 /* AsyncUdpSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AsyncUdpSocket.m; sourceTree = ""; }; - ADB6938E142F6F750095DB55 /* changes.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = changes.txt; sourceTree = ""; }; - ADB6938F142F6F750095DB55 /* WebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebSocket.h; sourceTree = ""; }; - ADB69390142F6F750095DB55 /* WebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebSocket.m; sourceTree = ""; }; - ADB69396142F6F910095DB55 /* RegexKitLite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegexKitLite.h; sourceTree = ""; }; - ADB69397142F6F910095DB55 /* RegexKitLite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RegexKitLite.m; sourceTree = ""; }; - ADB69399142F70360095DB55 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - ADB6917B142F5DF10095DB55 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ADB6939A142F70360095DB55 /* MobileCoreServices.framework in Frameworks */, - ADB69185142F5DF10095DB55 /* Foundation.framework in Frameworks */, - ADB69276142F69120095DB55 /* libxml2.dylib in Frameworks */, - ADB6925D142F68860095DB55 /* SystemConfiguration.framework in Frameworks */, - ADB6925B142F68730095DB55 /* CFNetwork.framework in Frameworks */, - ADB69183142F5DF10095DB55 /* UIKit.framework in Frameworks */, - ADB69187142F5DF10095DB55 /* CoreGraphics.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - ADB69173142F5DF00095DB55 = { - isa = PBXGroup; - children = ( - ADB69399142F70360095DB55 /* MobileCoreServices.framework */, - ADB692BE142F6E020095DB55 /* Library */, - ADB692BD142F6D400095DB55 /* Foundation.framework */, - ADB69275142F69120095DB55 /* libxml2.dylib */, - ADB6925C142F68860095DB55 /* SystemConfiguration.framework */, - ADB6925A142F68730095DB55 /* CFNetwork.framework */, - ADB69188142F5DF10095DB55 /* socketio */, - ADB69181142F5DF10095DB55 /* Frameworks */, - ADB6917F142F5DF10095DB55 /* Products */, - ); - sourceTree = ""; - }; - ADB6917F142F5DF10095DB55 /* Products */ = { - isa = PBXGroup; - children = ( - ADB6917E142F5DF10095DB55 /* socketio.app */, - ); - name = Products; - sourceTree = ""; - }; - ADB69181142F5DF10095DB55 /* Frameworks */ = { - isa = PBXGroup; - children = ( - ADB69182142F5DF10095DB55 /* UIKit.framework */, - ADB69184142F5DF10095DB55 /* Foundation.framework */, - ADB69186142F5DF10095DB55 /* CoreGraphics.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - ADB69188142F5DF10095DB55 /* socketio */ = { - isa = PBXGroup; - children = ( - ADB69191142F5DF10095DB55 /* socketioAppDelegate.h */, - ADB69192142F5DF10095DB55 /* socketioAppDelegate.m */, - ADB69194142F5DF10095DB55 /* MainWindow.xib */, - ADB69197142F5DF10095DB55 /* socketioViewController.h */, - ADB69198142F5DF10095DB55 /* socketioViewController.m */, - ADB6919A142F5DF10095DB55 /* socketioViewController.xib */, - ADB69189142F5DF10095DB55 /* Supporting Files */, - ); - path = socketio; - sourceTree = ""; - }; - ADB69189142F5DF10095DB55 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - ADB6918A142F5DF10095DB55 /* socketio-Info.plist */, - ADB6918B142F5DF10095DB55 /* InfoPlist.strings */, - ADB6918E142F5DF10095DB55 /* main.m */, - ADB69190142F5DF10095DB55 /* socketio-Prefix.pch */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - ADB692BE142F6E020095DB55 /* Library */ = { - isa = PBXGroup; - children = ( - ADB69396142F6F910095DB55 /* RegexKitLite.h */, - ADB69397142F6F910095DB55 /* RegexKitLite.m */, - ADB69388142F6F750095DB55 /* AsyncSocket */, - ADB6938F142F6F750095DB55 /* WebSocket.h */, - ADB69390142F6F750095DB55 /* WebSocket.m */, - ADB692F1142F6ECB0095DB55 /* ASIHTTP */, - ADB692EE142F6EB00095DB55 /* Reachability.h */, - ADB692EF142F6EB00095DB55 /* Reachability.m */, - ADB692E5142F6E4E0095DB55 /* SocketIO.h */, - ADB692E6142F6E4E0095DB55 /* SocketIO.m */, - ADB692BF142F6E0F0095DB55 /* JSON */, - ); - name = Library; - sourceTree = ""; - }; - ADB692BF142F6E0F0095DB55 /* JSON */ = { - isa = PBXGroup; - children = ( - ADB692C0142F6E0F0095DB55 /* NSObject+SBJson.h */, - ADB692C1142F6E0F0095DB55 /* NSObject+SBJson.m */, - ADB692C2142F6E0F0095DB55 /* SBJson.h */, - ADB692C3142F6E0F0095DB55 /* SBJsonParser.h */, - ADB692C4142F6E0F0095DB55 /* SBJsonParser.m */, - ADB692C5142F6E0F0095DB55 /* SBJsonStreamParser.h */, - ADB692C6142F6E0F0095DB55 /* SBJsonStreamParser.m */, - ADB692C7142F6E0F0095DB55 /* SBJsonStreamParserAccumulator.h */, - ADB692C8142F6E0F0095DB55 /* SBJsonStreamParserAccumulator.m */, - ADB692C9142F6E0F0095DB55 /* SBJsonStreamParserAdapter.h */, - ADB692CA142F6E0F0095DB55 /* SBJsonStreamParserAdapter.m */, - ADB692CB142F6E0F0095DB55 /* SBJsonStreamParserState.h */, - ADB692CC142F6E0F0095DB55 /* SBJsonStreamParserState.m */, - ADB692CD142F6E0F0095DB55 /* SBJsonStreamWriter.h */, - ADB692CE142F6E0F0095DB55 /* SBJsonStreamWriter.m */, - ADB692CF142F6E0F0095DB55 /* SBJsonStreamWriterAccumulator.h */, - ADB692D0142F6E0F0095DB55 /* SBJsonStreamWriterAccumulator.m */, - ADB692D1142F6E0F0095DB55 /* SBJsonStreamWriterState.h */, - ADB692D2142F6E0F0095DB55 /* SBJsonStreamWriterState.m */, - ADB692D3142F6E0F0095DB55 /* SBJsonTokeniser.h */, - ADB692D4142F6E0F0095DB55 /* SBJsonTokeniser.m */, - ADB692D5142F6E0F0095DB55 /* SBJsonUTF8Stream.h */, - ADB692D6142F6E0F0095DB55 /* SBJsonUTF8Stream.m */, - ADB692D7142F6E0F0095DB55 /* SBJsonWriter.h */, - ADB692D8142F6E0F0095DB55 /* SBJsonWriter.m */, - ); - path = JSON; - sourceTree = ""; - }; - ADB692F1142F6ECB0095DB55 /* ASIHTTP */ = { - isa = PBXGroup; - children = ( - ADB69385142F6F230095DB55 /* ASIWebPageRequest.h */, - ADB69386142F6F230095DB55 /* ASIWebPageRequest.m */, - ADB69369142F6EE80095DB55 /* ASIHTTPRequestConfig.h */, - ADB6936A142F6EE80095DB55 /* ASICacheDelegate.h */, - ADB6936B142F6EE80095DB55 /* ASIHTTPRequestDelegate.h */, - ADB6936C142F6EE80095DB55 /* ASIProgressDelegate.h */, - ADB6936D142F6EE80095DB55 /* ASIAuthenticationDialog.h */, - ADB6936E142F6EE80095DB55 /* ASIAuthenticationDialog.m */, - ADB6936F142F6EE80095DB55 /* ASIInputStream.h */, - ADB69370142F6EE80095DB55 /* ASIInputStream.m */, - ADB69371142F6EE80095DB55 /* ASIFormDataRequest.h */, - ADB69372142F6EE80095DB55 /* ASIFormDataRequest.m */, - ADB69373142F6EE80095DB55 /* ASIHTTPRequest.h */, - ADB69374142F6EE80095DB55 /* ASIHTTPRequest.m */, - ADB69375142F6EE80095DB55 /* ASINetworkQueue.h */, - ADB69376142F6EE80095DB55 /* ASINetworkQueue.m */, - ADB69377142F6EE80095DB55 /* ASIDownloadCache.h */, - ADB69378142F6EE80095DB55 /* ASIDownloadCache.m */, - ADB69379142F6EE80095DB55 /* ASIDataDecompressor.h */, - ADB6937A142F6EE80095DB55 /* ASIDataDecompressor.m */, - ADB6937B142F6EE80095DB55 /* ASIDataCompressor.h */, - ADB6937C142F6EE80095DB55 /* ASIDataCompressor.m */, - ); - path = ASIHTTP; - sourceTree = ""; - }; - ADB69388142F6F750095DB55 /* AsyncSocket */ = { - isa = PBXGroup; - children = ( - ADB69389142F6F750095DB55 /* AsyncSocket Documentation.html */, - ADB6938A142F6F750095DB55 /* AsyncSocket.h */, - ADB6938B142F6F750095DB55 /* AsyncSocket.m */, - ADB6938C142F6F750095DB55 /* AsyncUdpSocket.h */, - ADB6938D142F6F750095DB55 /* AsyncUdpSocket.m */, - ADB6938E142F6F750095DB55 /* changes.txt */, - ); - path = AsyncSocket; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - ADB6917D142F5DF10095DB55 /* socketio */ = { - isa = PBXNativeTarget; - buildConfigurationList = ADB6919F142F5DF10095DB55 /* Build configuration list for PBXNativeTarget "socketio" */; - buildPhases = ( - ADB6917A142F5DF10095DB55 /* Sources */, - ADB6917B142F5DF10095DB55 /* Frameworks */, - ADB6917C142F5DF10095DB55 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = socketio; - productName = socketio; - productReference = ADB6917E142F5DF10095DB55 /* socketio.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - ADB69175142F5DF00095DB55 /* Project object */ = { - isa = PBXProject; - buildConfigurationList = ADB69178142F5DF00095DB55 /* Build configuration list for PBXProject "socketio" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = ADB69173142F5DF00095DB55; - productRefGroup = ADB6917F142F5DF10095DB55 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - ADB6917D142F5DF10095DB55 /* socketio */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - ADB6917C142F5DF10095DB55 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ADB6918D142F5DF10095DB55 /* InfoPlist.strings in Resources */, - ADB69196142F5DF10095DB55 /* MainWindow.xib in Resources */, - ADB6919C142F5DF10095DB55 /* socketioViewController.xib in Resources */, - ADB69391142F6F750095DB55 /* AsyncSocket Documentation.html in Resources */, - ADB69394142F6F750095DB55 /* changes.txt in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - ADB6917A142F5DF10095DB55 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ADB6918F142F5DF10095DB55 /* main.m in Sources */, - ADB69193142F5DF10095DB55 /* socketioAppDelegate.m in Sources */, - ADB69199142F5DF10095DB55 /* socketioViewController.m in Sources */, - ADB692D9142F6E0F0095DB55 /* NSObject+SBJson.m in Sources */, - ADB692DA142F6E0F0095DB55 /* SBJsonParser.m in Sources */, - ADB692DB142F6E0F0095DB55 /* SBJsonStreamParser.m in Sources */, - ADB692DC142F6E0F0095DB55 /* SBJsonStreamParserAccumulator.m in Sources */, - ADB692DD142F6E0F0095DB55 /* SBJsonStreamParserAdapter.m in Sources */, - ADB692DE142F6E0F0095DB55 /* SBJsonStreamParserState.m in Sources */, - ADB692DF142F6E0F0095DB55 /* SBJsonStreamWriter.m in Sources */, - ADB692E0142F6E0F0095DB55 /* SBJsonStreamWriterAccumulator.m in Sources */, - ADB692E1142F6E0F0095DB55 /* SBJsonStreamWriterState.m in Sources */, - ADB692E2142F6E0F0095DB55 /* SBJsonTokeniser.m in Sources */, - ADB692E3142F6E0F0095DB55 /* SBJsonUTF8Stream.m in Sources */, - ADB692E4142F6E0F0095DB55 /* SBJsonWriter.m in Sources */, - ADB692E7142F6E4E0095DB55 /* SocketIO.m in Sources */, - ADB692F0142F6EB00095DB55 /* Reachability.m in Sources */, - ADB6937D142F6EE80095DB55 /* ASIAuthenticationDialog.m in Sources */, - ADB6937E142F6EE80095DB55 /* ASIInputStream.m in Sources */, - ADB6937F142F6EE80095DB55 /* ASIFormDataRequest.m in Sources */, - ADB69380142F6EE80095DB55 /* ASIHTTPRequest.m in Sources */, - ADB69381142F6EE80095DB55 /* ASINetworkQueue.m in Sources */, - ADB69382142F6EE80095DB55 /* ASIDownloadCache.m in Sources */, - ADB69383142F6EE80095DB55 /* ASIDataDecompressor.m in Sources */, - ADB69384142F6EE80095DB55 /* ASIDataCompressor.m in Sources */, - ADB69387142F6F230095DB55 /* ASIWebPageRequest.m in Sources */, - ADB69392142F6F750095DB55 /* AsyncSocket.m in Sources */, - ADB69393142F6F750095DB55 /* AsyncUdpSocket.m in Sources */, - ADB69395142F6F750095DB55 /* WebSocket.m in Sources */, - ADB69398142F6F910095DB55 /* RegexKitLite.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - ADB6918B142F5DF10095DB55 /* InfoPlist.strings */ = { - isa = PBXVariantGroup; - children = ( - ADB6918C142F5DF10095DB55 /* en */, - ); - name = InfoPlist.strings; - sourceTree = ""; - }; - ADB69194142F5DF10095DB55 /* MainWindow.xib */ = { - isa = PBXVariantGroup; - children = ( - ADB69195142F5DF10095DB55 /* en */, - ); - name = MainWindow.xib; - sourceTree = ""; - }; - ADB6919A142F5DF10095DB55 /* socketioViewController.xib */ = { - isa = PBXVariantGroup; - children = ( - ADB6919B142F5DF10095DB55 /* en */, - ); - name = socketioViewController.xib; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - ADB6919D142F5DF10095DB55 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_32_BIT)"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_VERSION = com.apple.compilers.llvmgcc42; - GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 4.3; - SDKROOT = iphoneos; - }; - name = Debug; - }; - ADB6919E142F5DF10095DB55 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_32_BIT)"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_VERSION = com.apple.compilers.llvmgcc42; - GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 4.3; - OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; - SDKROOT = iphoneos; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - ADB691A0142F5DF10095DB55 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "\"$(SRCROOT)/socketio\"", - ); - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "socketio/socketio-Prefix.pch"; - HEADER_SEARCH_PATHS = /usr/include/libxml2; - INFOPLIST_FILE = "socketio/socketio-Info.plist"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "\"$(SRCROOT)/socketio/simulator\"", - "\"$(SRCROOT)/socketio/device\"", - ); - OTHER_LDFLAGS = ( - "-lz", - "-licucore", - ); - PRODUCT_NAME = "$(TARGET_NAME)"; - WRAPPER_EXTENSION = app; - }; - name = Debug; - }; - ADB691A1142F5DF10095DB55 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "\"$(SRCROOT)/socketio\"", - ); - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "socketio/socketio-Prefix.pch"; - HEADER_SEARCH_PATHS = /usr/include/libxml2; - INFOPLIST_FILE = "socketio/socketio-Info.plist"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "\"$(SRCROOT)/socketio/simulator\"", - "\"$(SRCROOT)/socketio/device\"", - ); - OTHER_LDFLAGS = ( - "-lz", - "-licucore", - ); - PRODUCT_NAME = "$(TARGET_NAME)"; - WRAPPER_EXTENSION = app; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - ADB69178142F5DF00095DB55 /* Build configuration list for PBXProject "socketio" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - ADB6919D142F5DF10095DB55 /* Debug */, - ADB6919E142F5DF10095DB55 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - ADB6919F142F5DF10095DB55 /* Build configuration list for PBXNativeTarget "socketio" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - ADB691A0142F5DF10095DB55 /* Debug */, - ADB691A1142F5DF10095DB55 /* Release */, - ); - defaultConfigurationIsVisible = 0; - }; -/* End XCConfigurationList section */ - }; - rootObject = ADB69175142F5DF00095DB55 /* Project object */; -} diff --git a/socketio.xcodeproj/project.xcworkspace/xcuserdata/htainlinshwe.xcuserdatad/UserInterfaceState.xcuserstate b/socketio.xcodeproj/project.xcworkspace/xcuserdata/htainlinshwe.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index d1b08b1b047c942bc2e2c30f8fa8dfcc69b30532..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10416 zcmch7d3;k<_V~T`HA|B;&H9oi!KSuV6bfxA(w40)ZCT6KQVOMnw0UhKX;PE4P%C;A zS6pV?5D}5KAd4X4f{Kd!I;aRLGVY_YDuN*HT7J93PiB0rMT9VrIrO3$wBmW@C1i#xmJO>|&PB3Roc<$wtv;HkyrNrEC(L%x17^HlMlJ0@lpD z>{8}uVb;N}X4kN#>}GZgyOrI>?qHkPLu@nK!X9Q@*&}Qldz3xK9%nn)OYAlFCVQLh zW}mQ6*=Ou?c9ea~zGKJODfS~f&Hl~)%JZ=Hkzn3_)fCPBXrOPPKE6yv(Ehx?!SC(Iv zl~-DvomEhpHzI3T@wn{V5toz>%P%N(=}Z$#%Vr0H3q!5$2B9?2(AFaO!%@UMu@EO@ zZXi~YLTtoN9Ap4FkEDVOE51{TP3 z>BjWOQ{}E77x0IJ0iRC@y2|o%hv(8V*bgdW4|h=_5aG+mK6_(zj@$za04)Y6uE~zarql^@ID< zBkdd`X_nu6S(`A?gRJm2dXYq3DuNd`gi%+|jjcbjszAV3?+#83ReM9;dLJUQ_7Q?4 zmh_v7F?tvoB{DjX3@4Y65hR}!kU}yNw2%xs(1QVtU;^_7G8)-CmK2dG2yvX8754hP;f~^<;I2gD7OwURi=^%Pwi4&g;Az|zr9#Nt?>Kttvex2QrcvH%9adEKO*G(aji>u{U?BA~-}k%|g; zliwR|^9a+^iDvmb`Vq+|Zn%8ItI8^x+P7<6~^kj3P3(t=hY@+uK>1zF?MPpow>sc20Qa}vX?B8q<0H>7)Gp-F8C3$4W+7A4l0Fk4|#+*Ng>4P#&|6hSeRz_|6~ zE#&+z@-}{Eklj#agl1u8l1yOya zVTTAfuA(yR4!4EQ-bZu@60`p^F45@s32GdQ5}`2=6#91UCFIQ87?!(aNt9h0@-g`o z(z?heP~Js8gYhn%c$8|l4;7<0LUR<9C;uW}i#F;jn9xPOfr&Lys}_SeOpb~WM_^JH z`4%Rh1L8ROL4-H~Q@Y4WsQ53oae8!H4f&b;+DGGWFfBU4lYdhZrHE4KTfx%=s({RH zauP|YfJx#5B7=(PN)6ReZFEbT3^St>JT*`wWUh(H6XmH@#35Q{ngUh5R+u^x=kcNve3163NR&A`z5lzsDW)h|W*6G}6@E zeh@k%PL&Fc?lxbzRH$!jlDw_6cWAnpRfNK5Wo8PkK_P?>ZqeI_MkxZ`Bn121As$;q z5*-(JdY{grb4entrSoVVoljjTS*<~D3wkSw2+{2di^EeRag(J<@O#@r=a?}B1A(v< zY={RH!J!cdEQYoZshif*2I@gH0&OG})J2=Am)4=P@bt!<#gSAjm<$Tc%d1>z`Jxq`#hdxUGNZM+))}3w;R$I zRrNW*h$hfQbTI^aXa~KVcEY9LgBBd8OX!v4#N2*(7Q&Q>)u&g}Yg{_f3dUH4JEDt= z&^X=g4R+I|q~(9WL~$H}g)pi2h2DfO{yv){Iho7pE!|>lv6tS4Wc>|g<92$-oUpe= z!0582wKjTjvt~}gTMofx;%0ZzyXo>dn45$mlmUj$3mVb677iWiL$>>x1EKKHSs@hJ zq0M1rj?e24H7^o|mIfC2eF3*8G&E`&v%G;&W^Q(F-$38PnW3P>ofY(6w7@a17{T8| zSJ0J_fJ@?(1iFgekMR+PbJ1IkN<=qZE%NVNQ_{*cw9BO%h*uDI&$0W67u{~!EpGRh z;p5e$4%I~b@1^UJzXT<_9XU+YC^wrR&_(A#qeRpe`mjrPArjRmXmPQ3yh?V{ts+f- z6C$CN)}=Rb`A_yOw!w4s zd2xfCa77n=0hUx%pkPI3HS`tw2Bh`SSLti?b+{6)0_dS{qOJZ1T#XhQCE=ntbYgrV zRAP?gZ;CwjMbDU^VE7jg+Zza;IX+$pv&T7aC1=@lQLVsg?jD9Z}W4Ik|k!)v_LqCd^@HG92{tUOmZSXhImP9Ne8YVP< z(o27n#VF4Fz0YLD87aveMVYN(i3}r_wM@m-Ov94kPPhy1hUIHnGSiU^W`KKO1;(#v zKCIIvzZX4QRn*vFIpvJ0-dti_vpeX<{3kfm?Qat5CU`wUvCkLHjxx_!6ipoBSEUy} zF~)oB_^5fcox_HY80^f+D;Tk)rZaa~zL*NQbQkn_7cmEuZZt~akje;`$Dy;u^D+k; zz|Nz0(7IYQ!9l@?ehw=Z_+0M}39t$-11VQ!=d*Oo7Vz%6SwBmavVpKV zE*;6w$HXLmcwQ_3=GkDD3z_R!78}BbvTT+EYoH6dp$FEkW5ZY;8;+kNU>!UN8}M@z znrBI+%hBbB#NcO6wL9n)U4Cdb1{tA%Pnat~NHrQUQi=sUeKAb*VtF=}6{F&_B3R!= zMvIQv*0-VPB%c9cvj9Ph^xlK#@UgT2vFXD~?SFmXa9EoAsba*Jj zBT~aEv68^5*euu#Tcf}lR>Nxh$C_*&tAj1@a9tc#=|!Dq#Hg&E332NhSra@0+iH91 zl7iFD*0{))XFk>v7jLsx7VM)n1dm5)Z5L@>#1_L7@KlV}PIhG+&Z`)}c6f3Q@>~+J z^dj$qIE)&09lJ4Z%}s0>JPkXf%%^`$nO-#Sg~(dGl1`N50u<$(>|8Og&B@C~Nh%nQ zsZ>d)NwgFI&m(W2@NxY&GnJ=ivo-5nh6q;T3ol zUW3;+kqm}c$$HpYwvIi>){EIGYV;du-#>)?@EQC8{}yvpl;{$Sco((?y#8<~QqMzU z??EDaW9fe8GE^Q2qEevEw@4>LhalAnON3^04}su3sUTR{&@6}*Nt5(Cv(1myuCUi1 zi7{Qe>?!V0cwC^R)h8gTm@|`>m7rdFA`7v?hS5VHh^2|-sCG;EkOZrg*~aUBFLlMO ztr3Nq=&$hly?wMIL2>WBvJ~U-b{28FU%|>Q_B6a1n|PL?JMCuAv7PW1?CNGOuovMS z*nG++R*DW5Yn4Kz-q(x$9}(koQ;eFid1Ia^tU%la+9wN5gaVC}s*a_Iz#ZJOUVoE7`c`5dd`N}>^_%r)Wyy;)q zukcU!xSRdX{(w*5Q>mI&DztkWglK3iRlcOLNVE~gDjL@ET)F~Jd)O0!&GNQ32mC^z zyWW$V?HT6IuOD_vYTM? zT5E55Ec%!D-KxA{pU`VJIXz^o!|1lp+C7dm3=J@|4R^S7gZ?{UiMwH8rGS~QShL4y^}j<)I3iW3^rDCf zCS-Q|d=JOr zMEcm{(&`4{%w)ug{N#$bVy;9CL_8R@!VlB*Y+ik|QA!z?Ad`PeSKk&ELcRSmthGxl0S+|4=2W&dGzfx6@a$FT4MvmY zT>Uh4x0#0okab&Au+NoPJ+-nN3l0d0_2NMAebs>TQk`k%r{lP&UHDb$y<9*NI}8^G^9!)>+>c2fi8bAY z7oT%Hl21<-I?zM)_Nm_+J*G-5^8m#TwH{l7>DZah#eP*8_M&HE3#E|+$Ymr%+Q?Pp zdaUc;iv|1DWDRzn*J00jBi5;(A}^9R$h+8E{wMi_d_xXlKbc}t(M%mQ6}!npv5Q74CQo5~t%*cJ@DjkQB6{VnY63b`pUq!yRHX@qa*yIZFgGy+|o>We-<} z8aSVGiLx%%ZzAsG@Uh>$@T4<{1~8UYETo+b26BJ1RRR`$Mjj>*eRk)8&KZ z1@e*dB6*3tR6bEYSzaNpmb>JBd7FHR{CfE+d5`=N`J?iu<xIro2UYt8%$=h4Nlyw{op=o$_Jj zBg$>coyr%K?<(I@?p1!E+^5{H{8;&^@>}IG<#FW?%2UeI%Ab|LCMG5hNi0g7o!FkZ zGV#sCFA~2?{66uA#8ZhsCH|sPtE?)UYJe(Lb-pS?m8B|FjZ%$O6{|{B<*I3_d8+v; zx2i$asA^U%Rb8vPU3I5wxoU;#KGprIO{&eRt*ULR$5q=^FRNZvy`|cvdPlWa^?_=i z>TA^j)nV09)iKp^)gP)q)mUXyYt=fnQEgUR)i!myI#WGZJw%@nRUK4^)g9_i^%C{<>O0kUt5>R5saLCe)a%si)jQNLt9PsSsJ~Kw zqdurUtUjv#PW`?5g!-f=K_k~FH7bopqt)m%28~IRs>#=sYi4MgG|M!rHQO{VX+G8b zl!WdrNta|uG9_gtO;4Ja)Q}WNTAXxw(iKTpCPC6QN%tgeN_skJSJI)RBT3&T9ZNc% z^n*4-o24D9y+oU@E!2+EmT9MJXKJgob=oVmS8La3H)2xD?Rk~_jjc%@P zo=(s;=`PjzbZxo^bZd3*==SOU&@=r&{c!yVeW8AoeyqM&@71^K7wbFqOZ1?>Mt_6; zF8y--{rU&=UHTsVCjDmp!}>?`pXg5+GzPQ5Vz3z;hKme?426bKhB1aBLy4i(P;QuD zm~EJ2s5R6XTn4wH!5|odhFcBy8nzhTG#oUXG$tD}j29RO8ZR~uHV!e4Fcuh#j3vfW zW4UpfafY$dIL|oWxWMQ&E;P0n+l@<&*BNgx-eg>ETw&}o_88Y0*Bdt)A2mL1+-`iz zxWo8?@pqHTlxrGk8gH6z>M&hzy2G^6w92&Fw8pg2wAJ*uX@}`$)2pU8OmCUKG#xV? zH~nV%!}O<_nmMz~tTP+TCNpognr-I6=1a_D%oEM^=79M!bJ*N&?l51#5`Jnl*`Iz~*`3Li#JmuxQl2`G`yq-7mY5c|f2)=+H z!x!--{CIvMKbfD+FW_7F0N=`A#$U+;zm&g@zmZ?Y-^t(2ujE(ptNDlcE&QYW^= zEekCzmVo6lOUQDgWtrs`%WamwTkf#jWm#@nVOeQeWqHi9+j7M6lT~XiwpLpG)*G!4 zST|cAwr;aNX5DUm%KEDHb?YwcJJ#LS53KvF`>hA8hpb1e-&%jP{**F1#go#Q(wuT> zN+_i*<)f5uQVymZPx&F`RLbd;pKa8}*e4lx>IYdE1M&mu>rO-`fY- zbM0g8#r6sIN%krBsrFg++4ed1TKjGGjrN!9|FFMp|Iq%C{h#(v?4Q|>+P|}(vY)p9 zZ2#3k9h^hvP&l$3^Bv0^cRKEN+~?Tf*yPyec+ByH<4MO(#|w^^9IrUucIeH#OrM{8+R_dNUe&@fO zUpo&v4?BNwo^t-;{LT4Cnkr3`mYk+fv!yxGu1dQ-ZF$-%DF$Fn`sO;~A|;Zx`u_nZ C*6At$ diff --git a/socketio.xcodeproj/project.xcworkspace/xcuserdata/saturngod.xcuserdatad/UserInterfaceState.xcuserstate b/socketio.xcodeproj/project.xcworkspace/xcuserdata/saturngod.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index ad672d6c3afc0f42b6abd6411593f1f75f9492ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23362 zcmc(H33!u56Y%bPrFYswFOD{8+N5{WBTZU*t3%QSjhKyzl@)6mJwi#eX(wlC~5-|L6DneiV}IJ3BKwJ3BiwJNr&+ zZ*A%5RI3joj2Ofs4)I8kG)TsckDF_AbhOyp#>d&G&9XIhc1(z~I+|v-Ky!Uur@cKC z;p;Z<3SlCU1o@yLXebIpsVEJlBQ?rEnJ5coBMr(yTBJiIs1%hU3o1tyXbc*MCZTC) zI&z>6)QP&#T(k%+MoZ8#bRD`LtwgKP?dT4)4&9CJMVrtz^ZIF0 zVw#zD=4!^lEMb;1gb|sWnbpiK%o^q%W+QVivx9k%d5GD`>}H;2o@3r%_A+lWN0_6` zG3Ge)1#^ly&3wuH!u*^0okc8Wc~)TkSbtW^2D3xiFm^aQf|aoeY#N)%=CFBe5o=~G zY&BcUj%6pYjjWBG$=IUFuV+`Xx3aghcd{GVZuUNQ2fLGfoPC;omVKFh zjeU#V$L?p}W)HF_*bmqb*^k(d*)P~r>}mEp_IvgR_DA+Nj^lVv;3S+6=gawVL%A?6 zoQvS(Tr8*Hlw1y{<#b#wm&c9biZ}yj8Ivv6a%3EV_(D%Z$0an0OJu7#V!wQ&xv zgPX_A=N55mxLdik+-=Odz^cMdy;#KdzyQO+s!@8J;%Mm zy~@48z01AF9pDafhqw>854o?nbKKY5H{5ydTORWa&+;74^8zp7eRy9!fDh(}^27My z{0Lso$MZ>iK3~8W@lw{5AYS zei6TzU&1ftm+>q38~By{YW{ZqZoY@#!f)jt;&<{7^H1?l^Uv__@bB{P@dx;W{2~4@ ze}q5EALGB|&+upYulRHP*Zep9kNi*kZ~UKvMDP)Og%BZB7$QUo(SlM?3F(4b$Pu(c zflw%z1hY^r)CgmQTA@LhDYOW)gxNx?Fh^(;>_WS6jj&JV#EuC1%J^$_w!e#jpM zpgL)$<(1J@RK`aQ^rvd$TPK=DDJ! zGtFpkvRmuzT}?ABZJo9m4(H(0`m@*vs4-aU>hAnEtHxoc`&V68J00`DZv9{ygO&XV z4EA2DfqH7F}f>Dg=YH6BXX}8aIVeDnXE1=P+nkc$a6^ZCZ zHDow_P>bqNJsOJ|Cc%1e*ji!Gb8U2uOoNFe!$>4S2LWt6nt&!wfaZ=)2j~rnY7Fk0 z-qLEzOHXgLH(6U}+B-Va$98}+q<2_5yBuvZ?9J&WhrNB8eSUhcXwqA1X4>0qsqUX0 z_NLi3P_T5*7h79eYdgr%)6&pln+NmU>9Du9+8k-~Tc#aElhG736*W$DE$24t9NT!; zR~zV+w5o1&SEqGatF6w-=oSFI3R%Zj)wzDdmujb<;lk5|n#Wevf!_5X8?w5-dc`#p z%^`}ds0GbJvr#JnUAia^R|GiO4#+s?OU+C)yt^ zN7oW%H(EjBhy2<5$A#+#bmLSHCcVR91td41n~)XM#rcyep#kLF)dU*gWXU^s-odO! zw-QAUx&^Hvi6p59twpzyWRe1g)h(uayJvVVxzyM@S}5P6t?gjFsB+ty>+S88wz;-e zcc%_7g}cytqS(CIJ)C>!a5j?EV8IL0jkZvU_K@^$w3TGg2d%TQ`x;-J&c~925c941U-r#Lywb8l0~wKhU9ERPok&L)94wrn`nuF7>R>` zHYN14E4{0}!)ZS%!78V9fZyFY7j&aBIAsvf8ix(+epg4m)iJ}?YmAL_lN=n}tG2vb;5Qsqr=5wV{r0+fUlVe=BF3hE1y@;zv!&JN_5@m zHIfUf$<$C^N5@F@-_mKD)8U@bzozp&{!)a1L z=#!dzJrb~9q=Z~eN{RWutf&h86Y1>>AAPLaJ?wDwBl?9XwxFNT&*%avBNkG=1@!bH z`VDlnf>buR=DWeAV2#1CSCoNAm&UtygI=j%3{t@i8SNp8c^p6#+p&Np*a!P!KkQGc zNHwV;V@NHj+l~Wq5SBXs3nlerEE(tgZ+w69V1C*m@jC zl)C^d9xJg5C*VY!gp+X!PQ__B9jkE$&csh+bCjaB;5W;gI?3asn{Y^kbf9A~L71DyIQ zlfKr}2wiJI7G{qmYG|J@Z=cb6V_9Q$WBr605865k66FN}9;$21RikVAkEwxzT}dI_ zEKZ<6(Oz8MQBN3csIF`@n=JKi*vS+&4cAzkW?N}6v1pN{s-(K6(o$F7=+ZWrFqjXc zxvs9dwm&nCv~LU^3+7c;?+i6ODq3%b{{3cS8U>CV0PF-StFH5~W1~Yj-m6Z*nWu-==EITa| zIyv>L)YfKmMGlJ=!6T=(80#DLl@@)SrOLxc8%2=)cMw!nH-gfSuC8h{R8|`+Jc@BO z#gPAZFqo_KhDy*|*9`W~sHM8fEt5`)sb~PEN^9GUF6#`NYZS&Zv$3MF)?7cfwh9(& zNL7x`tv) z7=Ue@)j^kVhl{mwdSD5YX(YbVzsxh0uQdx{H zpZF%ctcSY5&R_UiyrMC<*Q|rxr}5xGUug@z4quO~{|OA7cJM(4cJ^+8H{q4k#q~C< z#_NdUK70#agKx!a@oo5ad1xcBW1 z+hS9z9kwvK&2|8m%rvzZk>PSgZW{V4KQdnaNIClo+}AbH(6NT5M}W9jT=(JoJs|?# zfgdDGy75C~F(gjthAA^Z>cg=>Hm;>}rmV7Mn!`%>TGt%!!jC}E{|R~q@9y95Jbr;TG%Qr7$kdCyJzv4^5XD3IRs1jf8h#z`!EfNb_)Yv4 z-iP<&x5>5SI&uTKiL4^4$r`ej+)mb!yU5)S;dk+S_y9hL58=c32tJCB;p6yye1hCV zwvva)Bjj=NG7{w$pA|XmHD&enHR8{niAicx^ z25KJ7_NJHEyV{yv$-n9D*7P!)6>>El>5wn6c3S%gkdZjKzSYaMKjnJ*WnBM1XU9s* zsJoO7d#7o%Io0*cqf$(ela1TFY=lxa=Kb?*fKH9GO{)Rlpw@PE7x->e%lfyZ^_kQ# zCtr7X`HG@^&G_f}aw{67k4+FH&wv=IFCQbEeB9~f!xx`R#x)lTZjI&2*sc89aMuN0D;3~lf-ltvk{{TCUcEEpk~;}Q!! zjR|Kmm>fu~GMP*klg((zMshFNL^f|_w2TfZm^{)=df;?p3pffjj+QyF6T2OudUqZK z;<*8j8d^G9Anj|MX?0j3=jx~h2i7*F3^J_x)>cbL2WVO zlE%}$IXgRJ@%V*V+MIsdG-F~cbPQ&ugehgp$TqT_+(+);%9Jw|Fox0O0kQ+e@ZkT) zF_i!C#<&;vO`v1QP-o^Wp13eGTboS>DZv!0UXJJO=odQG9J5-~Wh@i@glB zwhvF8%z{BhdkwP?cv?)JAWs60rvOL!5BnS6`*hMmZEhAFq*?4TJJ@ zBXbkrTScBBy8+*`l&5QNdHu!Y$qO^IxjI0Yqs^dF)9UDt%sw2hW$qjl<6X>p=O~{i zFStk9s@0r0=~SdFZC@0dnQeoj*v{MsOg%teA}<4qS1400T-&apEvr%m} z7vh?h47EmW$n@Z0Stp*OgWxIALw|$8kgChc$Vtu4 zHfrF2Ix8bJ!>rR8bU9j+F30S}}c0x2@LwRi6(o}4pHUihOg*l0*KvoYl45p;%?v$4?n6>30QHl9^77vWgIHox84 z)?DwfwskZ)TG~4&!11|levP$rCP6;rQ*w$N1o~_uo5UuwDQv3qJksKZp<6Q?Sya_| zS}JXAGobGm6U6K zm1-F|Gf=9PAk|A$aA!eqhrNOu!={2rzH$qso(dw>JFnx|2_7mF$=7ZwQz(_Gc7=z^b>xDZ%8iuDP2`t>R8|9(KuYCTpmM@XTU(~lzw_s`W~MbSfNx$t zpJ!k6NaH0Dvu<&`O2zRn5p%BNx3AdO**)wV>|PY_S`eP%3L5%Ubi9&Gz!&@jYB zqRuvh9tlA>5qVjc-h%^|#`MQl-_qG?b1B<$_GhBp%3ff9VSi;Wia1=vBSkD1alDu7 z-#J9N{)4^5{wd-J5l4zRY72)shGRtxt7*81N5FB?Wi0e#65<^=C+u7R8QQ-FG{7lN zovj^$C#V-|3{L!OXcrCVk*30RE{@O`*;r@#*E0kzfD0aM2q9c32U8F&;usOj6ve^L z19DWGC-a>BdT~T?BL~9~&Bbss5yy&HA>uei(RVH!O-L5cAX%@8;^N8Y)gjE}Y3}M{ z%&;6UjLQFeRmVFeKGNV}!$-u$Cud}9dn*ck{rm$0gCGN}?}P%QXvS z=oA!H{EjgWfI{sNI0nwT)1g4ygiD;|*)@1Po&<&2b8tHpWZ#5uhVttt@YDEtmv9;E z`b0&&pS>UWMX#nNaOn_Wuzzq#Tr&G7m&$=zfn}={u?kkMh!aGdxSdnuKrWNZf*OTD z5hsy)5hsf{70MLqp*-QQ7hU~=cCefO0SLT9YFz&s69ua4T+sTTA!)+J38^UdagF&P zmydk80&#fY+(;J*)2>+eVL+-mMY?jdd`_b_~e=uHAsBH{)Sj}x&~#2cJH zw}VHk@1T|bR(I8YN2PUvy$h=QgJJM>Gwt)L;1U6xy4W3)paDiwX`N?@*baR0oZZLR?I9=#GsOYl+v(XO)TvLF8u0b$RhFtjJt-QlM-k8h?}&Jch^PLYn$!6;&OO(Q?+ADNGJGeT_^whEx4JfHH*`4nG4}7u$2+#~xr{8tzXXiP%n!PdkCz zGa`lvel9gWy-N`;rGP$D4{K~&hjp&4*&RcdIE!GQ=*Zkg?-sz&XgK9z+w9B>+M1me z$OapnNZUL;pen>=&s;ZNu8fg}<#oo_^;5ZK%^| zsHx4!)WG?SCdX|uct74>#8+>2`e}S19|Tt(pi2)gbyu+TS{6POEDH}&8dSYFF9q=7 zM7f2JV87+TmFjUTjpNpmZ zC@B4vLa9@Mh(!VhZFys`0%YdVQO^{*3x!-44O}xe2ozYA4o{3v73Fdmxb#G`7p{N@ ziW~<8qU*yAL7nx>(YWWj);`af)1}9kBL{_~xLn&_+@Q}rWyGZpdslnMLEZ$Z=KZ`G z!Zzn2sS)ZOU`Cg@l%kX`gR5~)l1|OAAZwrB?#>k=zSadlny-SD?8#2MJ8t2t`5Nl` zU;d~MI`90}@#7%Q=j-{gJZwGSPh2nJ8@BM{>1>MlMmmkIjS|*J(`@$%H>75qNeFrc z2lkwqVBMRm9b>v|T{f3QsKn@2P~FzLz*+SPr_r#CsmX^F&XtXr$If{UFMM25TTU5Wn{&@6sud|3I6Qt?hMF@D5%iOTgrS9f9xH zy1%34{PjfXX}`(c4lp;vcdy{r@M}FgF%M$B$89UtQIm8he;2=A#2ZDtS;Sl1y#n}q z_)Yz{N`5onE#iAcylD_01FlUK_up&z?fe4+5bWR|6fxlF>F-Y{im!ipK;K9B#|QL% zf`3xPTSdIhMcm_>P>19);^F+W{A*O%p5veAU*KQlU*ccpU*TWn|0QB@zUe4}Rs6I~r#mWAei-D9Le@<2_P3)Q03OYq)E8ZCJk{yRBbc6O;?O*$EeNg;BH{q>tX}79fMP4=RIP%G(>$TuDQ{D2fFG zMWGk*{%*l2;^6PCJB>;DZ*5tQN)Ktd|1RsM0`}l$3%Qw#P8oHScPdqlhDjZ3DX4# z5g^w1K*Xm-{1v!=B0eYLuP4(Dh@MHiHj_wK0+;H4|HVDY#iesx)GKfXivUG^W}4iF zfZpvJ5Atld`QzNlKUClgaa(?UDH}yntJc{!)4~wrc~FB$RonlfwPBT&qe%^h(8qZ zDd(|Zx+e(gH!hQ%;!N}83p^f&d*T+8_ASB^VW~hw{IQ5niuhA_BrFq_^HCG~(gVxa z<@Cjb>xAoF$9vvMa5K0BuK+##$R{1~xExRX*J>}rFn=XwDgp#-q3{Y2&Ew7*LA2pcH6zYXtR z0Rh6j!WL3=dGVgGRoI3U0&KBgiuerOJ8+4hF1$pyfR_c>Vt)|v&t8$gCcFV=y-zAc*eifz@}r14Cr@I08@2A#J|86K42486i*(wRUH@Jr`IgpjD!mx3UGkDP54OoSolOZ z$-_GRO~n5e@$Vx31H6ZAV4=SdPVwUf&;f~vFNqB3_KhOLT<$SU@N72YdiTpdE`pch zc>zk#x&>HIfBxU>Qa=m7&~6t*26rRaFQLTXFT4ifcj1zEM*b8T_eqL`ff8~` ze~Fu|0EtB6*I%*{e@TGI@FF7&bR6cL>_01#5XsO1eZwT-BI6@6zWrSXN4vALlQm(VWYF>e3?_OR@hQ&_Rxi zCF3O%#yXt~=ZSF?ibcurVz4|^#9a+9Z@QLS39oIs7t-R}xcj&VAlLl}yt3&n?tAVM zkN7Y?f{%jNE{%i)m7G_=tCv)KBD{F17+$($=F9jBz6xHtRL?i?;~|z`1)<>!5Muoy zNQESL{Soz{THuvObA|cvvZFDOTK%3kNJM=d)kljllq1Dh5JSN4fBifllv+B%Ke)CuJ-Hj>-6jL zTjEFjmiw*nyWa11zjc1={WkdB>$ll&r{6BWNBti6d*1Iw|B?Pl{we-i|6Ko3{ssO; z{(ApP|0@3)|62cg{|0~2{}KOB0;B;20p$Ue0o4Ix0_p=A0%iqt1at*l6R;>?Nx;g0 zn*-Jc+#awlU_-#hfK35A16~ezCE%@qLjlJFP6d1!@O8lXfbRl+4)`VDVjvS39jFRS z3`_}33oHt>1l9#M23iA~0&Rga0`~;I8~9=1$APB;zYIJZcrNgVz@Gvy1pXTMXOIw- z9F!eY5L6Uo2r>nY391XaDrj0zbI|mlnL)FHT7wn@Eeu*5v@}QzS{}3_Xh+bVpl_r@ zq*>Al(n->0>2zs}bhflpI#;?zx>mYDx>5R$^gZc8>0#+n>2c`^>4(yfr6;AINxzVu zmY$J*CH-1@UizK%2kB4J3({Yuze#@&=7S}{3Beh`S;2FImj;W$_Xj^3{CMzz;1j_g z244!1g!qPJhKvd+2pJnPHRP(0MIkFft`F%B*%9(k$lD=DLym{~hK7a?4b2HH3e|^B z4z-2O2o*zb3SAYtH}pX0p&`CQppaxp#gO_T4MXl2a_^AMLp~dFZpb%7)kAZKjv6|B zsD0?wLpKcFI&}NcLqk6t`svWG!u-NQ!ZN~2!|KBt!p4P-53_~M2%8%=KkS;YMPW%(skUl)F7_+8=M;akGD zhuf$#Ptz3My!muIpUUxTO)3Z*c{Ol zu{C0Q#IA@(BcmgeBU2;OBQqlNBJ(5HMsAAij@%NtE%M>WN1}wNAyHvb5m8Z5u~BhR zw?^F?wK=LMYHQTas9nQG3`-oAJS=rs`Y_$FykQ52eKPFRVV@5>HSGMb?}kqpK7IJi z;j@Of4(}R1Z}|7aFO5JWm=WBFz!B0B3r1Ww;)W47jaW6}juCf`I6vawBmNli=SV!# zZ)Cv8r6X63Ts?Bl$h9LkjNBN_M~6lajfT>kXnC|EIzBorS{ zM?W3?a`c}T08awPYahsz`7a=AhtFIUME z<(cwqd5&Bs&yySFqvZ|qCb>=CCU2KJrJ8?kT3?u&g}!6>AP5XBHhn4(-UPBB3-NijvSR>TytoB%i{h5ViE+#0HpSf^w!;~YGBb6~qxl*B2D>IeZ$^vDP(x9wXj#1Vt z8B<)6Y$Z`HQ!ZDoP+qURQMpcem-24q7UeeO{mLhmPb;5QKCgU9`HJ!_ z<$mS6$^**N$}`Hd%5%zZl;0|US6)&fm9NTQ6{H%j8mW@0VpZ`fl}fG3RB2RNRi&y* zRjnGMs#A?sO;a_irmO6#t5uyUQMFuko$3bFO4ZG(b*j5m8&n%rkExzeJ*j$HwOjR^ zYOm@o)qd3x)iKox)t9QXs;^bws(w)YoWLY-36ccggwTYc3Cj|0Nw_uP_Jnl_>l5xt z*p_f#!j6QU2`?rbOE{76QNqcD&lATaPE4GfXiK~{ab@C;#FrD_PJB18-Ou2qj!k5^ApPgPshP3m^FL*1pG zr~X3yz4}M>1@*5PRT+~qres`|F)d?v#-5D58T&Hc&dkd!&9r1zW>#hH$vl*KB=dOY zi7Zprn5??2hOF^f4`)4>^+MK5S+8Whll5NKr&(WQeVO%D);C$-W&N0SA?sqcUv^-2 zaCT^RSawAAuHubFJoj%}tt{ zHET4tY1V1hYj$XMY97%%u6atcTl2i;CC#gv4>adBzvU$56y&t#EX}z&XLZg4IZx$0 zlk;58i#f04yq0r3=hK|ebI#I(@0>rhjF#8>Xalvu+96uGHe0LF7HTWBqqVi# zvD)$4DcY;FP1;uN0_{TWb=n)XtF*Uh*J|(3Zq{zmZr47beMq}YdrgGDYtgmp>^g_8OE+J)PvZdN_vjwfy{EgB8=I@oZOCoSU7out z_mx$AQ`f=#AjJlW~m>-!RpRdYK z%g@Nq&e!G_=Iir~`R4qq^5^B>ng4A58~JbMf0+MC{%858^3UX-%Rit0eg04RzZCct z1QbXMLJPtQA`6BUj4X&Lh%G26s4TduptE3A!G?le1^Wt47W`6(3YkK-FrYA~P+AyS zIIM7ZVO*iIP*tcd%q+|*EG{$@nhVPcD+;R$#}!T}ysEINaC%`&;oQRcg^LSG;qt;8 z3Rf1cF6=JcQTTM>?!p%fUnzX8@Xf;gh3^)ARCv1Z$HJcre=Gc>2o>=~K1KdT(M77F z?4q0^OHpM}by026*rM@8lZvJmS&N#B<`pd{T2!>OXj##Uq8o}<7OgJ2wP*Mqp`Ye5pUZ)?WFVvUn;T=NyG5UJFRo|qash_29)nBde z)X&qe(682S&~McD=(p;(>mSrVtbbJhf_{(wkp77Nxc-FxBmGJJS^YWvclsao7YwX{ zH~1L>3_*q=hHyiaA=Z#?$S~v>bcQ@b5nQV>8>$SG3{wnO8JY~!4KocZ3~LRy8}2mR zZP;koY}ji!VmM|vVfe^!((t+AjNxa)FNRA-Y~+j*V~{b#IMf(nG#V!vJB*8rON^rN zTH~$8+l)QNZN~eJ4;mjfK5Bfz__T44@lE4?n|GR@ zGVeA&Z+_jp*Sybs*!-#abMtBQ8T0ogl9GUu*plRuhLTAo*OlB>a!1L%C7VlnO1744 zFWFV{Sjm$mPnSGf@~@J2OWrTJP#RO3QkqtpQJPh{x%7e32TLC=eYA9M>51 zDm_{HdFj`s=SzPqy-<3wOepgy3oHvR8&VcoHoPplETJr;tgx)O%v@GhR#8?{R#(?ahs%$apDRCK{(bpR<-e5wR>4;A6#*5}iqML%is%Y?MO;N{ zg}NfEBEO=j!cb9BF}`AA#gvN1iuo1SR4l4kTCuERWyQ@EYbtK5xTj)I#lDK4D=t;W fRVG$eS6VBlSN7KKFz^lp`bRR+_38e%vhDu>72RMq diff --git a/socketio.xcodeproj/xcuserdata/saturngod.xcuserdatad/xcschemes/socketio.xcscheme b/socketio.xcodeproj/xcuserdata/saturngod.xcuserdatad/xcschemes/socketio.xcscheme deleted file mode 100644 index ed2432a..0000000 --- a/socketio.xcodeproj/xcuserdata/saturngod.xcuserdatad/xcschemes/socketio.xcscheme +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/socketio.xcodeproj/xcuserdata/saturngod.xcuserdatad/xcschemes/xcschememanagement.plist b/socketio.xcodeproj/xcuserdata/saturngod.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 3251531..0000000 --- a/socketio.xcodeproj/xcuserdata/saturngod.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - SchemeUserState - - socketio.xcscheme - - orderHint - 0 - - - SuppressBuildableAutocreation - - ADB6917D142F5DF10095DB55 - - primary - - - - - diff --git a/socketio/Classes-iOS/GHUNIT/GHAsyncTestCase.h b/socketio/Classes-iOS/GHUNIT/GHAsyncTestCase.h deleted file mode 100644 index e998f9b..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHAsyncTestCase.h +++ /dev/null @@ -1,163 +0,0 @@ -// -// GHAsyncTestCase.h -// GHUnit -// -// Created by Gabriel Handford on 4/8/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import "GHTestCase.h" - -/*! - Common wait statuses to use with waitForStatus:timeout:. - */ -enum { - kGHUnitWaitStatusUnknown = 0, // Unknown wait status - kGHUnitWaitStatusSuccess, // Wait status success - kGHUnitWaitStatusFailure, // Wait status failure - kGHUnitWaitStatusCancelled // Wait status cancelled -}; - -/*! - Asynchronous test case with wait and notify. - - If notify occurs before wait has started (if it was a synchronous call), this test - case will still work. - - Be sure to call prepare before the asynchronous method (otherwise an exception will raise). - - @interface MyAsyncTest : GHAsyncTestCase { } - @end - - @implementation MyAsyncTest - - - (void)testSuccess { - // Prepare for asynchronous call - [self prepare]; - - // Do asynchronous task here - [self performSelector:@selector(_succeed) withObject:nil afterDelay:0.1]; - - // Wait for notify - [self waitForStatus:kGHUnitWaitStatusSuccess timeout:1.0]; - } - - - (void)_succeed { - // Notify the wait. Notice the forSelector points to the test above. - // This is so that stray notifies don't error or falsely succeed other tests. - // To ignore the check, forSelector can be NULL. - [self notify:kGHUnitWaitStatusSuccess forSelector:@selector(testSuccess)]; - } - - @end - - */ -@interface GHAsyncTestCase : GHTestCase { - - NSInteger waitForStatus_; - NSInteger notifiedStatus_; - - BOOL prepared_; // Whether prepared was called before waitForStatus:timeout: - NSRecursiveLock *lock_; // Lock to synchronize on - SEL waitSelector_; // The selector we are waiting on - - NSArray *_runLoopModes; -} - -/*! - Run loop modes to run while waiting; - Defaults to NSDefaultRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode - */ -@property (retain, nonatomic) NSArray *runLoopModes; - -/*! - Prepare before calling the asynchronous method. - */ -- (void)prepare; - -/*! - Prepare and specify the selector we will use in notify. - - @param selector Selector - */ -- (void)prepare:(SEL)selector; - -/*! - Wait for notification of status or timeout. - - Be sure to prepare before calling your asynchronous method. - For example, - - - (void)testFoo { - [self prepare]; - - // Do asynchronous task here - - [self waitForStatus:kGHUnitWaitStatusSuccess timeout:1.0]; - } - - @param status kGHUnitWaitStatusSuccess, kGHUnitWaitStatusFailure or custom status - @param timeout Timeout in seconds - */ -- (void)waitForStatus:(NSInteger)status timeout:(NSTimeInterval)timeout; - -/*! - @param status kGHUnitWaitStatusSuccess, kGHUnitWaitStatusFailure or custom status - @param timeout Timeout in seconds - @deprecated Use waitForTimeout: - */ -- (void)waitFor:(NSInteger)status timeout:(NSTimeInterval)timeout; - -/*! - Wait for timeout to occur. - Fails if we did _NOT_ timeout. - - @param timeout Timeout - */ -- (void)waitForTimeout:(NSTimeInterval)timeout; - -/*! - Notify waiting of status for test selector. - - @param status Status, for example, kGHUnitWaitStatusSuccess - @param selector If not NULL, then will verify this selector is where we are waiting. This prevents stray asynchronous callbacks to fail a later test. - */ -- (void)notify:(NSInteger)status forSelector:(SEL)selector; - -/*! - Notify waiting of status for any selector. - - @param status Status, for example, kGHUnitWaitStatusSuccess - */ -- (void)notify:(NSInteger)status; - -/*! - Run the run loops for the specified interval. - - @param interval Interval - @author Adapted from Robert Palmer, pauseForTimeout - */ -- (void)runForInterval:(NSTimeInterval)interval; - -@end diff --git a/socketio/Classes-iOS/GHUNIT/GHAsyncTestCase.m b/socketio/Classes-iOS/GHUNIT/GHAsyncTestCase.m deleted file mode 100644 index 033569e..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHAsyncTestCase.m +++ /dev/null @@ -1,186 +0,0 @@ -// -// GHAsyncTestCase.m -// GHUnit -// -// Created by Gabriel Handford on 4/8/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import "GHAsyncTestCase.h" -#import - -typedef enum { - kGHUnitAsyncErrorNone, - kGHUnitAsyncErrorUnprepared, - kGHUnitAsyncErrorTimedOut, - kGHUnitAsyncErrorInvalidStatus -} GHUnitAsyncError; - -@implementation GHAsyncTestCase - -@synthesize runLoopModes=_runLoopModes; - -- (void)dealloc { - [_runLoopModes release]; - _runLoopModes = nil; - [super dealloc]; -} - -// Internal GHUnit setUp -- (void)_setUp { - lock_ = [[NSRecursiveLock alloc] init]; - prepared_ = NO; - notifiedStatus_ = kGHUnitWaitStatusUnknown; -} - -// Internal GHUnit tear down -- (void)_tearDown { - waitSelector_ = NULL; - if (prepared_) [lock_ unlock]; // If we prepared but never waited we need to unlock - [lock_ release]; - lock_ = nil; -} - -- (void)prepare { - [self prepare:self.currentSelector]; -} - -- (void)prepare:(SEL)selector { - [lock_ lock]; - prepared_ = YES; - waitSelector_ = selector; - notifiedStatus_ = kGHUnitWaitStatusUnknown; -} - -- (GHUnitAsyncError)_waitFor:(NSInteger)status timeout:(NSTimeInterval)timeout { - if (!prepared_) { - return kGHUnitAsyncErrorUnprepared; - } - prepared_ = NO; - - waitForStatus_ = status; - - if (!_runLoopModes) - _runLoopModes = [[NSArray arrayWithObjects:NSDefaultRunLoopMode, NSRunLoopCommonModes, nil] retain]; - - NSTimeInterval checkEveryInterval = 0.05; - NSDate *runUntilDate = [NSDate dateWithTimeIntervalSinceNow:timeout]; - BOOL timedOut = NO; - NSInteger runIndex = 0; - while(notifiedStatus_ == kGHUnitWaitStatusUnknown) { - - NSString *mode = [_runLoopModes objectAtIndex:(runIndex++ % [_runLoopModes count])]; - - [lock_ unlock]; - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (!mode || ![[NSRunLoop currentRunLoop] runMode:mode beforeDate:[NSDate dateWithTimeIntervalSinceNow:checkEveryInterval]]) - // If there were no run loop sources or timers then we should sleep for the interval - [NSThread sleepForTimeInterval:checkEveryInterval]; - [pool release]; - [lock_ lock]; - - // If current date is after the run until date - if ([runUntilDate compare:[NSDate date]] == NSOrderedAscending) { - timedOut = YES; - break; - } - } - [lock_ unlock]; - - if (timedOut) { - return kGHUnitAsyncErrorTimedOut; - } else if (waitForStatus_ != notifiedStatus_) { - return kGHUnitAsyncErrorInvalidStatus; - } - - return kGHUnitAsyncErrorNone; -} - -- (void)waitFor:(NSInteger)status timeout:(NSTimeInterval)timeout { - [NSException raise:NSDestinationInvalidException format:@"Deprecated; Use waitForStatus:timeout:"]; -} - -- (void)waitForStatus:(NSInteger)status timeout:(NSTimeInterval)timeout { - GHUnitAsyncError error = [self _waitFor:status timeout:timeout]; - if (error == kGHUnitAsyncErrorTimedOut) { - GHFail(@"Request timed out"); - } else if (error == kGHUnitAsyncErrorInvalidStatus) { - GHFail(@"Request finished with the wrong status: %d != %d", status, notifiedStatus_); - } else if (error == kGHUnitAsyncErrorUnprepared) { - GHFail(@"Call prepare before calling asynchronous method and waitForStatus:timeout:"); - } -} - -- (void)waitForTimeout:(NSTimeInterval)timeout { - GHUnitAsyncError error = [self _waitFor:-1 timeout:timeout]; - if (error != kGHUnitAsyncErrorTimedOut) { - GHFail(@"Request should have timed out"); - } -} - -// Similar to _waitFor:timeout: but just runs the loops -// From Robert Palmer, pauseForTimeout -- (void)runForInterval:(NSTimeInterval)interval { - NSTimeInterval checkEveryInterval = 0.05; - NSDate *runUntilDate = [NSDate dateWithTimeIntervalSinceNow:interval]; - - if (!_runLoopModes) - _runLoopModes = [[NSArray arrayWithObjects:NSDefaultRunLoopMode, NSRunLoopCommonModes, nil] retain]; - - NSInteger runIndex = 0; - - while ([runUntilDate compare:[NSDate date]] == NSOrderedDescending) { - NSString *mode = [_runLoopModes objectAtIndex:(runIndex++ % [_runLoopModes count])]; - - [lock_ unlock]; - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (!mode || ![[NSRunLoop currentRunLoop] runMode:mode beforeDate:[NSDate dateWithTimeIntervalSinceNow:checkEveryInterval]]) - // If there were no run loop sources or timers then we should sleep for the interval - [NSThread sleepForTimeInterval:checkEveryInterval]; - [pool release]; - [lock_ lock]; - } -} - -- (void)notify:(NSInteger)status { - [self notify:status forSelector:NULL]; -} - -- (void)notify:(NSInteger)status forSelector:(SEL)selector { - // Note: If this is called from a stray thread or delayed call, we may not be in an autorelease pool - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - // Make sure the notify is for the currently waiting test - if (selector != NULL && !sel_isEqual(waitSelector_, selector)) { - NSLog(@"Warning: Notified from %@ but we were waiting for %@", NSStringFromSelector(selector), NSStringFromSelector(waitSelector_)); - } else { - [lock_ lock]; - notifiedStatus_ = status; - [lock_ unlock]; - } - - [pool release]; -} - -@end diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/GHTest+JUnitXML.h b/socketio/Classes-iOS/GHUNIT/GHTest/GHTest+JUnitXML.h deleted file mode 100644 index 135740c..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/GHTest+JUnitXML.h +++ /dev/null @@ -1,44 +0,0 @@ -// -// GHTest+JUnitXML.h -// GHUnit -// -// Created by Gabriel Handford on 6/4/10. -// Copyright 2010. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -#import "GHTest.h" - -@interface GHTest(JUnitXML) - -/*! - Return test results in JUnit XML format for external parsing use - (such as a Continuous Integration system like Jenkins). - */ -- (NSString *)JUnitXML; - -@end - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/GHTest+JUnitXML.m b/socketio/Classes-iOS/GHUNIT/GHTest/GHTest+JUnitXML.m deleted file mode 100644 index cd5497b..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/GHTest+JUnitXML.m +++ /dev/null @@ -1,48 +0,0 @@ -// -// GHTest+JUnitXML.m -// GHUnit -// -// Created by Gabriel Handford on 6/4/10. -// Copyright 2010. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import "GHTest+JUnitXML.h" -#import "GHTesting.h" -#import "GTMNSString+XML.h" - -//! @cond DEV - -@implementation GHTest(JUnitXML) - -- (NSString *)JUnitXML { - return [NSString stringWithFormat: - @"%@", - self.name, [self class], self.interval, - (self.exception ? [NSString stringWithFormat:@"%@", [[self.exception description] gtm_stringBySanitizingAndEscapingForXML], - [[GHTesting descriptionForException:self.exception] gtm_stringBySanitizingAndEscapingForXML]] : @"")]; -} - -@end - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/GHTest.h b/socketio/Classes-iOS/GHUNIT/GHTest/GHTest.h deleted file mode 100644 index f22c24a..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/GHTest.h +++ /dev/null @@ -1,274 +0,0 @@ -// -// GHTest.h -// GHUnit -// -// Created by Gabriel Handford on 1/18/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import - - -/*! - Test status. - */ -typedef enum { - GHTestStatusNone = 0, - GHTestStatusRunning, //! Test is running - GHTestStatusCancelling, //! Test is being cancelled - GHTestStatusCancelled, //! Test was cancelled - GHTestStatusSucceeded, //! Test finished and succeeded - GHTestStatusErrored, //! Test finished and errored -} GHTestStatus; - -enum { - GHTestOptionReraiseExceptions = 1 << 0, // Allows exceptions to be raised (so you can trigger the debugger) - GHTestOptionForceSetUpTearDownClass = 1 << 1, // Runs setUpClass/tearDownClass for this (each) test; Used when re-running a single test in a group -}; -typedef NSInteger GHTestOptions; - -/*! - Generate string from GHTestStatus - @param status - */ -extern NSString* NSStringFromGHTestStatus(GHTestStatus status); - -/*! - Check if test is running (or trying to cancel). - */ -extern BOOL GHTestStatusIsRunning(GHTestStatus status); - -/*! - Check if test has succeeded, errored or cancelled. - */ -extern BOOL GHTestStatusEnded(GHTestStatus status); - -/*! - Test stats. - */ -typedef struct { - NSInteger succeedCount; // Number of succeeded tests - NSInteger failureCount; // Number of failed tests - NSInteger cancelCount; // Number of aborted tests - NSInteger testCount; // Total number of tests -} GHTestStats; - -/*! - Create GHTestStats. - */ -extern GHTestStats GHTestStatsMake(NSInteger succeedCount, NSInteger failureCount, NSInteger cancelCount, NSInteger testCount); - -extern const GHTestStats GHTestStatsEmpty; - -/*! - Description from test stats. - */ -extern NSString *NSStringFromGHTestStats(GHTestStats stats); - -@protocol GHTestDelegate; - -/*! - The base interface for a runnable test. - - A runnable with a unique identifier, display name, stats, timer, delegate, log and error handling. - */ -@protocol GHTest - -/*! - Unique identifier for test. - */ -@property (readonly, nonatomic) NSString *identifier; - -/*! - Name (readable) for test. - */ -@property (readonly, nonatomic) NSString *name; - -/*! - How long the test took to run. Defaults to -1, if not run. - */ -@property (assign, nonatomic) NSTimeInterval interval; - -/*! - Test status. - */ -@property (assign, nonatomic) GHTestStatus status; - -/*! - Test stats. - */ -@property (readonly, nonatomic) GHTestStats stats; - -/*! - Exception that occurred. - */ -@property (retain, nonatomic) NSException *exception; - -/*! - Whether test is disabled. - */ -@property (assign, nonatomic, getter=isDisabled) BOOL disabled; - -/*! - Whether test is hidden. - */ -@property (assign, nonatomic, getter=isHidden) BOOL hidden; - -/*! - Delegate for test. - */ -@property (assign, nonatomic) id delegate; // weak - -/*! - Run the test. - @param options Options - */ -- (void)run:(GHTestOptions)options; - -/*! - @result Messages logged during this test run - */ -- (NSArray *)log; - -/*! - Reset the test. - */ -- (void)reset; - -/*! - Cancel the test. - */ -- (void)cancel; - -/*! - @result The number of disabled tests - */ -- (NSInteger)disabledCount; - -@end - -/*! - Test delegate for notification when a test starts and ends. - */ -@protocol GHTestDelegate - -/*! - Test started. - @param test Test - @param source If tests are nested, than source corresponds to the originator of the delegate call - */ -- (void)testDidStart:(id)test source:(id)source; - -/*! - Test updated. - @param test Test - @param source If tests are nested, than source corresponds to the originator of the delegate call - */ -- (void)testDidUpdate:(id)test source:(id)source; - -/*! - Test ended. - @param test Test - @param source If tests are nested, than source corresponds to the originator of the delegate call - */ -- (void)testDidEnd:(id)test source:(id)source; - -/*! - Test logged a message. - @param test Test - @param didLog Message - @param source If tests are nested, than source corresponds to the originator of the delegate call - */ -- (void)test:(id)test didLog:(NSString *)didLog source:(id)source; - -@end - -/*! - Delegate which is notified of log messages from inside a test case. - */ -@protocol GHTestCaseLogWriter - -/*! - Log message. - @param message Message - @param testCase Test case - */ -- (void)log:(NSString *)message testCase:(id)testCase; - -@end - -/*! - Default test implementation with a target/selector pair. - - - Tests a target and selector - - Notifies a test delegate - - Keeps track of status, running time and failures - - Stores any test specific logging - - */ -@interface GHTest : NSObject { - - NSObject *delegate_; // weak - - id target_; - SEL selector_; - - NSString *identifier_; - NSString *name_; - GHTestStatus status_; - NSTimeInterval interval_; - BOOL disabled_; - BOOL hidden_; - NSException *exception_; // If failed - - NSMutableArray *log_; - -} - -@property (readonly, nonatomic) id target; -@property (readonly, nonatomic) SEL selector; -@property (readonly, nonatomic) NSArray *log; - -/*! - Create test with identifier, name. - @param identifier Unique identifier - @param name Name - */ -- (id)initWithIdentifier:(NSString *)identifier name:(NSString *)name; - -/*! - Create test with target/selector. - @param target Target (usually a test case) - @param selector Selector (usually a test method) - */ -- (id)initWithTarget:(id)target selector:(SEL)selector; - -/*! - Create autoreleased test with target/selector. - @param target Target (usually a test case) - @param selector Selector (usually a test method) - */ -+ (id)testWithTarget:(id)target selector:(SEL)selector; - -@end diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/GHTest.m b/socketio/Classes-iOS/GHUNIT/GHTest/GHTest.m deleted file mode 100644 index e448c6f..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/GHTest.m +++ /dev/null @@ -1,277 +0,0 @@ -// -// GHTest.m -// GHUnit -// -// Created by Gabriel Handford on 1/18/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -#import "GHTest.h" -#import "GHTest+JUnitXML.h" - -#import "GHTesting.h" -#import "GHTestCase.h" - -@interface GHTest () -- (void)_setLogWriter:(id)logWriter; -@end - -NSString* NSStringFromGHTestStatus(GHTestStatus status) { - switch(status) { - case GHTestStatusNone: return NSLocalizedString(@"Waiting", nil); - case GHTestStatusRunning: return NSLocalizedString(@"Running", nil); - case GHTestStatusCancelling: return NSLocalizedString(@"Cancelling", nil); - case GHTestStatusSucceeded: return NSLocalizedString(@"Succeeded", nil); - case GHTestStatusErrored: return NSLocalizedString(@"Errored", nil); - case GHTestStatusCancelled: return NSLocalizedString(@"Cancelled", nil); - - default: return NSLocalizedString(@"Unknown", nil); - } -} - -GHTestStats GHTestStatsMake(NSInteger succeedCount, NSInteger failureCount, NSInteger cancelCount, NSInteger testCount) { - GHTestStats stats; - stats.succeedCount = succeedCount; - stats.failureCount = failureCount; - stats.cancelCount = cancelCount; - stats.testCount = testCount; - return stats; -} - -const GHTestStats GHTestStatsEmpty = {0, 0, 0, 0}; - -NSString *NSStringFromGHTestStats(GHTestStats stats) { - return [NSString stringWithFormat:@"%d/%d/%d/%d", stats.succeedCount, stats.failureCount, - stats.cancelCount, stats.testCount]; -} - -BOOL GHTestStatusIsRunning(GHTestStatus status) { - return (status == GHTestStatusRunning || status == GHTestStatusCancelling); -} - -BOOL GHTestStatusEnded(GHTestStatus status) { - return (status == GHTestStatusSucceeded - || status == GHTestStatusErrored - || status == GHTestStatusCancelled); -} - -@implementation GHTest - -@synthesize delegate=delegate_, target=target_, selector=selector_, name=name_, interval=interval_, -exception=exception_, status=status_, log=log_, identifier=identifier_, disabled=disabled_, hidden=hidden_; - -- (id)initWithIdentifier:(NSString *)identifier name:(NSString *)name { - if ((self = [self init])) { - identifier_ = [identifier retain]; - name_ = [name retain]; - interval_ = -1; - status_ = GHTestStatusNone; - } - return self; -} - -- (id)initWithTarget:(id)target selector:(SEL)selector { - NSString *name = NSStringFromSelector(selector); - NSString *identifier = [NSString stringWithFormat:@"%@/%@", NSStringFromClass([target class]), name]; - if ((self = [self initWithIdentifier:identifier name:name])) { - target_ = [target retain]; - selector_ = selector; - } - return self; -} - -+ (id)testWithTarget:(id)target selector:(SEL)selector { - return [[[self alloc] initWithTarget:target selector:selector] autorelease]; -} - -- (void)dealloc { - [identifier_ release]; - [name_ release]; - [target_ release]; - [exception_ release]; - [log_ release]; - [super dealloc]; -} - -- (BOOL)isEqual:(id)test { - return ((test == self) || - ([test conformsToProtocol:@protocol(GHTest)] && - [self.identifier isEqual:[test identifier]])); -} - -- (NSString *)description { - return [NSString stringWithFormat:@"%@ %@", self.identifier, [super description]]; -} - -- (GHTestStats)stats { - switch(status_) { - case GHTestStatusSucceeded: return GHTestStatsMake(1, 0, 0, 1); - case GHTestStatusErrored: return GHTestStatsMake(0, 1, 0, 1); - case GHTestStatusCancelled: return GHTestStatsMake(0, 0, 1, 1); - default: - return GHTestStatsMake(0, 0, 0, 1); - } -} - -- (void)reset { - status_ = GHTestStatusNone; - interval_ = 0; - [exception_ release]; - exception_ = nil; - [delegate_ testDidUpdate:self source:self]; -} - -- (void)cancel { - if (status_ == GHTestStatusRunning) { - status_ = GHTestStatusCancelling; - // TODO(gabe): Call cancel on target if available? - } else { - status_ = GHTestStatusCancelled; - } - [delegate_ testDidUpdate:self source:self]; -} - -- (void)setDisabled:(BOOL)disabled { - disabled_ = disabled; - [delegate_ testDidUpdate:self source:self]; -} - -- (void)setHidden:(BOOL)hidden { - hidden_ = hidden; - [delegate_ testDidUpdate:self source:self]; -} - -- (NSInteger)disabledCount { - return (disabled_ || hidden_ ? 1 : 0); -} - -- (void)setException:(NSException *)exception { - [exception retain]; - [exception_ release]; - exception_ = exception; - status_ = GHTestStatusErrored; - [delegate_ testDidUpdate:self source:self]; -} - -- (void)setUpClass { - // Set up class - @try { - if ([target_ respondsToSelector:@selector(setUpClass)]) { - [target_ setUpClass]; - } - } @catch(NSException *exception) { - // If we fail in the setUpClass, then we will cancel all the child tests (below) - exception_ = [exception retain]; - status_ = GHTestStatusErrored; - } -} - -- (void)tearDownClass { - // Tear down class - @try { - if ([target_ respondsToSelector:@selector(tearDownClass)]) - [target_ tearDownClass]; - } @catch(NSException *exception) { - exception_ = [exception retain]; - status_ = GHTestStatusErrored; - } -} - -- (void)run:(GHTestOptions)options { - if (status_ == GHTestStatusCancelled || disabled_ || hidden_) return; - - if ((options & GHTestOptionForceSetUpTearDownClass) == GHTestOptionForceSetUpTearDownClass) { - [self setUpClass]; - if (status_ == GHTestStatusErrored) return; - } - - status_ = GHTestStatusRunning; - - [delegate_ testDidStart:self source:self]; - - [self _setLogWriter:self]; - - BOOL reraiseExceptions = ((options & GHTestOptionReraiseExceptions) == GHTestOptionReraiseExceptions); - [GHTesting runTestWithTarget:target_ selector:selector_ exception:&exception_ interval:&interval_ reraiseExceptions:reraiseExceptions]; - - [self _setLogWriter:nil]; - - if (exception_) { - status_ = GHTestStatusErrored; - } - - if (status_== GHTestStatusCancelling) { - status_ = GHTestStatusCancelled; - } else if (status_ == GHTestStatusRunning) { - status_ = GHTestStatusSucceeded; - } - - if ((options & GHTestOptionForceSetUpTearDownClass) == GHTestOptionForceSetUpTearDownClass) - [self tearDownClass]; - - [delegate_ testDidEnd:self source:self]; -} - -- (void)log:(NSString *)message testCase:(id)testCase { - if (!log_) log_ = [[NSMutableArray array] retain]; - [log_ addObject:message]; - [delegate_ test:self didLog:message source:self]; -} - -#pragma mark Log Writer - -- (void)_setLogWriter:(id)logWriter { - if ([target_ respondsToSelector:@selector(setLogWriter:)]) - [target_ setLogWriter:logWriter]; -} - -#pragma mark NSCoding - -- (void)encodeWithCoder:(NSCoder *)coder { - [coder encodeObject:identifier_ forKey:@"identifier"]; - [coder encodeBool:hidden_ forKey:@"hidden"]; - [coder encodeInteger:status_ forKey:@"status"]; - [coder encodeDouble:interval_ forKey:@"interval"]; -} - -- (id)initWithCoder:(NSCoder *)coder { - GHTest *test = [self initWithIdentifier:[coder decodeObjectForKey:@"identifier"] name:nil]; - test.hidden = [coder decodeBoolForKey:@"hidden"]; - test.status = [coder decodeIntegerForKey:@"status"]; - test.interval = [coder decodeDoubleForKey:@"interval"]; - return test; -} - -#pragma mark NSCopying - -- (id)copyWithZone:(NSZone *)zone { - if (!target_) [NSException raise:NSObjectNotAvailableException format:@"NSCopying unsupported for tests without target/selector pair"]; - return [[GHTest allocWithZone:zone] initWithTarget:target_ selector:selector_]; -} - -@end - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestGroup+JUnitXML.h b/socketio/Classes-iOS/GHUNIT/GHTest/GHTestGroup+JUnitXML.h deleted file mode 100644 index ed4b4e7..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestGroup+JUnitXML.h +++ /dev/null @@ -1,42 +0,0 @@ -// -// GHTestGroup+JUnitXML.h -// GHUnit -// -// Created by Gabriel Handford on 6/4/10. -// Copyright 2010. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -#import "GHTestGroup.h" - -@interface GHTestGroup(JUnitXML) - -- (NSString *)JUnitXML; - -- (BOOL)writeJUnitXMLAtPath:(NSString *)documentsPath error:(NSError **)error; - -@end - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestGroup+JUnitXML.m b/socketio/Classes-iOS/GHUNIT/GHTest/GHTestGroup+JUnitXML.m deleted file mode 100644 index 183d0a5..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestGroup+JUnitXML.m +++ /dev/null @@ -1,64 +0,0 @@ -// -// GHTestGroup+JUnitXML.m -// GHUnit -// -// Created by Gabriel Handford on 6/4/10. -// Copyright 2010. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -#import "GHTestGroup+JUnitXML.h" - - -@implementation GHTestGroup(JUnitXML) - -- (BOOL)writeJUnitXMLAtPath:(NSString *)path error:(NSError **)error { - if (self.stats.testCount > 0) { - - NSString *XMLPath = [path stringByAppendingPathComponent: - [NSString stringWithFormat:@"%@.xml", self.name]]; - - // Attempt to write the XML and return the success status - return [[self JUnitXML] writeToFile:XMLPath atomically:NO encoding:NSUTF8StringEncoding error:error]; - } - return YES; -} - -- (NSString *)JUnitXML { - NSMutableString *JUnitXML = [NSMutableString stringWithFormat: - @"", - self.name, self.stats.testCount, self.stats.failureCount, self.interval]; - - for (id child in self.children) { - if ([child respondsToSelector:@selector(JUnitXML)]) - [JUnitXML appendString:[child JUnitXML]]; - } - [JUnitXML appendString:@""]; - return JUnitXML; -} - -@end - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestGroup.h b/socketio/Classes-iOS/GHUNIT/GHTest/GHTestGroup.h deleted file mode 100644 index 790d393..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestGroup.h +++ /dev/null @@ -1,187 +0,0 @@ -// -// GHTestGroup.h -// -// Created by Gabriel Handford on 1/16/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -#import "GHTest.h" -#import "GHTestCase.h" - -/*! - Interface for a group of tests. - - This group conforms to the GHTest protocol as well (see Composite pattern). - */ -@protocol GHTestGroup - -/*! - Name. - */ -- (NSString *)name; - -/*! - Parent for test group. - */ -- (id)parent; - -/*! - Children for test group. - */ -- (NSArray *)children; - -@end - -/*! - A collection of tests (or test groups). - - A test group is a collection of `id`, that may represent a set of test case methods. - - For example, if you had the following GHTestCase. - - @interface FooTest : GHTestCase {} - - (void)testFoo; - - (void)testBar; - @end - - The GHTestGroup would consist of and array of GHTest: FooTest#testFoo, FooTest#testBar, - each test being a target and selector pair. - - A test group may also consist of a group of groups (since GHTestGroup conforms to GHTest), - and this might represent a GHTestSuite. - */ -@interface GHTestGroup : NSObject { - - NSObject *delegate_; // weak - id parent_; // weak - - NSMutableArray */*of id*/children_; - - NSString *name_; // The name of the test group (usually the class name of the test case - NSTimeInterval interval_; // Total time of child tests - GHTestStatus status_; // Current status of the group (current status of running or completed child tests) - GHTestStats stats_; // Current stats for the group (aggregate of child test stats) - - BOOL didSetUpClass_; - - GHTestOptions options_; - - // Set if test is created from initWithTestCase:delegate: - // Allows use to perform setUpClass and tearDownClass (once per test case run) - id testCase_; - - NSException *exception_; // If exception happens in group setUpClass/tearDownClass -} - -@property (readonly, nonatomic) NSArray */*of id*/children; -@property (assign, nonatomic) id parent; -@property (readonly, nonatomic) id testCase; -@property (assign, nonatomic) GHTestOptions options; - -/*! - Create an empty test group. - @param name The name of the test group - @param delegate Delegate, notifies of test start and end - @result New test group - */ -- (id)initWithName:(NSString *)name delegate:(id)delegate; - -/*! - Create test group from a test case. - @param testCase Test case, could be a subclass of SenTestCase or GHTestCase - @param delegate Delegate, notifies of test start and end - @result New test group - */ -- (id)initWithTestCase:(id)testCase delegate:(id)delegate; - -/*! - Create test group from a single test. - @param testCase Test case, could be a subclass of SenTestCase or GHTestCase - @param selector Test to run - @param delegate Delegate, notifies of test start and end - */ -- (id)initWithTestCase:(id)testCase selector:(SEL)selector delegate:(id)delegate; - -/*! - Create test group from a test case. - @param testCase Test case, could be a subclass of SenTestCase or GHTestCase - @param delegate Delegate, notifies of test start and end - @result New test group - */ -+ (GHTestGroup *)testGroupFromTestCase:(id)testCase delegate:(id)delegate; - -/*! - Add a test case (or test group) to this test group. - @param testCase Test case, could be a subclass of SenTestCase or GHTestCase - */ -- (void)addTestCase:(id)testCase; - -/*! - Add a test group to this test group. - @param testGroup Test group to add - */ -- (void)addTestGroup:(GHTestGroup *)testGroup; - -/*! - Add tests to this group. - @param tests Tests to add - */ -- (void)addTests:(NSArray */*of id*/)tests; - -/*! - Add test to this group. - @param test Test to add - */ -- (void)addTest:(id)test; - -/*! - Whether the test group should run on the main thread. - Call passes to test case instance if enabled. - */ -- (BOOL)shouldRunOnMainThread; - -/*! - @result YES if we have any enabled chilren, NO if all children have been disabled. - */ -- (BOOL)hasEnabledChildren; - -/*! - Get list of failed tests. - @result Failed tests - */ -- (NSArray */*of id*/)failedTests; - -/*! - Run in operation queue. - Tests from the group are added and will block until they have completed. - @param operationQueue If nil, then runs as is - @param options Options - */ -- (void)runInOperationQueue:(NSOperationQueue *)operationQueue options:(GHTestOptions)options; - -@end - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestGroup.m b/socketio/Classes-iOS/GHUNIT/GHTest/GHTestGroup.m deleted file mode 100644 index 72e173b..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestGroup.m +++ /dev/null @@ -1,396 +0,0 @@ -// -// GHTestGroup.m -// -// Created by Gabriel Handford on 1/16/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -#import "GHTestGroup.h" -#import "GHTestCase.h" -#import "GHTestOperation.h" - -#import "GHTesting.h" -#import "GTMStackTrace.h" - -#import "GHTestGroup+JUnitXML.h" - -@interface GHTestGroup () -- (void)_addTestsFromTestCase:(id)testCase; -- (void)_reset; -@end - -@implementation GHTestGroup - -@synthesize stats=stats_, parent=parent_, children=children_, delegate=delegate_, interval=interval_, -status=status_, testCase=testCase_, exception=exception_, options=options_; - -- (id)initWithName:(NSString *)name delegate:(id)delegate { - if ((self = [super init])) { - name_ = [name retain]; - children_ = [[NSMutableArray array] retain]; - delegate_ = delegate; - } - return self; -} - -- (id)initWithTestCase:(id)testCase delegate:(id)delegate { - if ([self initWithName:NSStringFromClass([testCase class]) delegate:delegate]) { - testCase_ = [testCase retain]; - [self _addTestsFromTestCase:testCase]; - } - return self; -} - -- (id)initWithTestCase:(id)testCase selector:(SEL)selector delegate:(id)delegate { - if ([self initWithName:NSStringFromClass([testCase class]) delegate:delegate]) { - testCase_ = [testCase retain]; - [self addTest:[GHTest testWithTarget:testCase selector:selector]]; - } - return self; -} - -+ (GHTestGroup *)testGroupFromTestCase:(id)testCase delegate:(id)delegate { - return [[[GHTestGroup alloc] initWithTestCase:testCase delegate:delegate] autorelease]; -} - -- (void)dealloc { - for(id test in children_) - [test setDelegate:nil]; - [name_ release]; - [children_ release]; - [testCase_ release]; - [super dealloc]; -} - -- (NSString *)description { - return [NSString stringWithFormat:@"%@, %d %0.3f %d/%d (%d failures)", - name_, status_, interval_, stats_.succeedCount, stats_.testCount, stats_.failureCount]; -} - -- (NSString *)name { - return name_; -} - -- (void)_addTestsFromTestCase:(id)testCase { - NSArray *tests = [[GHTesting sharedInstance] loadTestsFromTarget:testCase]; - [self addTests:tests]; -} - -- (void)addTestCase:(id)testCase { - GHTestGroup *testCaseGroup = [[GHTestGroup alloc] initWithTestCase:testCase delegate:self]; - [self addTestGroup:testCaseGroup]; - [testCaseGroup release]; -} - -- (void)addTestGroup:(GHTestGroup *)testGroup { - [self addTest:testGroup]; - [testGroup setParent:self]; -} - -- (void)addTests:(NSArray *)tests { - for(GHTest *test in tests) - [self addTest:test]; -} - -- (void)addTest:(id)test { - [test setDelegate:self]; - stats_.testCount += [test stats].testCount; - [children_ addObject:test]; -} - -- (NSString *)identifier { - return name_; -} - -// Forward up -- (void)test:(id)test didLog:(NSString *)message source:(id)source { - [delegate_ test:self didLog:message source:source]; -} - -- (NSArray *)log { - // Not supported for group (though may be an aggregate of child test logs in the future?) - return nil; -} - -- (void)reset { - [self _reset]; - for(id test in children_) { - [test reset]; - } - [delegate_ testDidUpdate:self source:self]; -} - -- (void)_reset { - status_ = GHTestStatusNone; - stats_ = GHTestStatsMake(0, 0, 0, stats_.testCount); - interval_ = 0; - [exception_ release]; - exception_ = nil; -} - -- (void)_failedTests:(NSMutableArray *)tests testGroup:(id)testGroup { - for(id test in [testGroup children]) { - if ([test conformsToProtocol:@protocol(GHTestGroup)]) - [self _failedTests:tests testGroup:(id)test]; - else if (test.status == GHTestStatusErrored) [tests addObject:test]; - } -} - -- (NSArray */*of id*/)failedTests { - NSMutableArray *tests = [NSMutableArray array]; - [self _failedTests:tests testGroup:self]; - return tests; -} - -- (void)setException:(NSException *)exception { - [exception retain]; - [exception_ release]; - exception_ = exception; - status_ = GHTestStatusErrored; - [delegate_ testDidUpdate:self source:self]; -} - -- (void)cancel { - if (status_ == GHTestStatusRunning) { - status_ = GHTestStatusCancelling; - } else { - for(id test in children_) { - stats_.cancelCount++; - [test cancel]; - } - status_ = GHTestStatusCancelled; - } - [delegate_ testDidUpdate:self source:self]; -} - -- (void)setDisabled:(BOOL)disabled { - for(id test in children_) - [test setDisabled:disabled]; - [delegate_ testDidUpdate:self source:self]; -} - -- (BOOL)isDisabled { - for(id test in children_) - if (![test isDisabled]) return NO; - return YES; -} - -- (void)setHidden:(BOOL)hidden { - for(id test in children_) - [test setHidden:hidden]; - [delegate_ testDidUpdate:self source:self]; -} - -- (BOOL)isHidden { - for(id test in children_) - if (![test isHidden]) return NO; - return YES; -} - -- (NSInteger)disabledCount { - NSInteger disabledCount = 0; - for(id test in children_) { - disabledCount += [test disabledCount]; - } - return disabledCount; -} - -- (void)setUpClass { - if (didSetUpClass_) return; - didSetUpClass_ = YES; - // Set up class (if we have a test case) - @try { - if ([testCase_ respondsToSelector:@selector(setUpClass)]) - [testCase_ setUpClass]; - } @catch(NSException *exception) { - // If we fail in the setUpClass, then we will cancel all the child tests (below) - exception_ = [exception retain]; - status_ = GHTestStatusErrored; - for(id test in children_) { - if (![test isDisabled]) { - stats_.failureCount++; - [test setException:exception_]; - } - } - } -} - -- (void)tearDownClass { - // Tear down class (if we were created from a testCase) - if (status_ != GHTestStatusRunning) return; - @try { - if ([testCase_ respondsToSelector:@selector(tearDownClass)]) - [testCase_ tearDownClass]; - } @catch(NSException *exception) { - exception_ = [exception retain]; - status_ = GHTestStatusErrored; - // We need to reverse any successes in the test run above - // and set the error on all the child tests - // TODO(gabe): Don't I need to ignore disabled tests in this loop? - for(id test in children_) { - if ([test status] == GHTestStatusSucceeded) { - stats_.succeedCount--; - stats_.failureCount++; - } - if (![test isDisabled]) - [test setException:exception_]; - } - } -} - -- (BOOL)hasEnabledChildren { - return (([children_ count] - [self disabledCount]) <= 0); -} - -- (void)_run:(NSOperationQueue *)operationQueue { - if (status_ == GHTestStatusCancelled || [self hasEnabledChildren]) { - return; - } - - didSetUpClass_ = NO; - status_ = GHTestStatusRunning; - [delegate_ testDidStart:self source:self]; - - // Run the tests - for(id test in children_) { - // If we are cancelling mark all child tests cancelled (and update stats) - // If we errored (above), then set the error on the test (and update stats) - // Otherwise run it - if (status_ == GHTestStatusCancelling) { - stats_.cancelCount++; - [test cancel]; - } else if (status_ == GHTestStatusErrored) { - stats_.failureCount++; - [test setException:exception_]; - } else { - if (operationQueue) { - [operationQueue addOperation:[[[GHTestOperation alloc] initWithTest:test options:options_] autorelease]]; - } else { - if (![test isDisabled]) - [self setUpClass]; - - if (status_ == GHTestStatusErrored) break; - [test run:options_]; - } - } - } - [operationQueue waitUntilAllOperationsAreFinished]; - - // Tear down class only if we called setUpClass - if (didSetUpClass_) - [self tearDownClass]; - - if (status_ == GHTestStatusCancelling) { - status_ = GHTestStatusCancelled; - } else if (exception_ || stats_.failureCount > 0) { - status_ = GHTestStatusErrored; - } else { - status_ = GHTestStatusSucceeded; - } - [delegate_ testDidEnd:self source:self]; -} - -- (void)runInOperationQueue:(NSOperationQueue *)operationQueue options:(GHTestOptions)options { - options_ = options; - - NSAssert(!(((options_ & GHTestOptionReraiseExceptions) == GHTestOptionReraiseExceptions) && operationQueue), - @"Can't run in parallel (through operation queue) and also have re-raise exceptions option set"); - - [self _reset]; - [self _run:operationQueue]; -} - -- (BOOL)shouldRunOnMainThread { - if (self.isDisabled) return NO; - if ([testCase_ respondsToSelector:@selector(shouldRunOnMainThread)]) - return [testCase_ shouldRunOnMainThread]; - return NO; -} - -- (void)run:(GHTestOptions)options { - options_ = options; - [self _reset]; - if ([self shouldRunOnMainThread]) { - [self performSelectorOnMainThread:@selector(_run:) withObject:nil waitUntilDone:YES]; - } else { - [self _run:nil]; - } -} - -#pragma mark Delegates (GHTestDelegate) - -- (void)testDidStart:(id)test source:(id)source { - [delegate_ testDidStart:self source:source]; - [delegate_ testDidUpdate:self source:self]; -} - -- (void)testDidUpdate:(id)test source:(id)source { - [delegate_ testDidUpdate:self source:source]; - [delegate_ testDidUpdate:self source:self]; -} - -- (void)testDidEnd:(id)test source:(id)source { - if (source == test) { - if ([test interval] >= 0) - interval_ += [test interval]; - stats_.failureCount += [test stats].failureCount; - stats_.succeedCount += [test stats].succeedCount; - stats_.cancelCount += [test stats].cancelCount; - } - [delegate_ testDidEnd:self source:source]; - [delegate_ testDidUpdate:self source:self]; -} - -#pragma mark NSCoding - -- (void)encodeWithCoder:(NSCoder *)coder { - [coder encodeObject:self.identifier forKey:@"identifier"]; - [coder encodeInteger:self.status forKey:@"status"]; - [coder encodeDouble:self.interval forKey:@"interval"]; -} - -- (id)initWithCoder:(NSCoder *)coder { - GHTestGroup *test = [self initWithName:[coder decodeObjectForKey:@"identifier"] delegate:nil]; - test.status = [coder decodeIntegerForKey:@"status"]; - test.interval = [coder decodeDoubleForKey:@"interval"]; - return test; -} - -#pragma mark NSCopying - -- (id)copyWithZone:(NSZone *)zone { - NSMutableArray *tests = [NSMutableArray arrayWithCapacity:[children_ count]]; - for(id test in children_) { - [tests addObject:[test copyWithZone:zone]]; - } - GHTestGroup *testGroup = [[GHTestGroup alloc] initWithName:name_ delegate:nil]; - [testGroup addTests:tests]; - return testGroup; -} - -@end - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestOperation.h b/socketio/Classes-iOS/GHUNIT/GHTest/GHTestOperation.h deleted file mode 100644 index 3000ed7..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestOperation.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// GHTestOperation.h -// GHUnit -// -// Created by Gabriel Handford on 6/4/10. -// Copyright 2010. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import "GHTest.h" - -/*! - Test for running in the context of an NSOperationQueue. - */ -@interface GHTestOperation : NSOperation { - id test_; - GHTestOptions options_; -} - -/*! - Create operation that wraps a GHTest instance. - @param test Test - @param options Options - */ -- (id)initWithTest:(id)test options:(GHTestOptions)options; - -@end diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestOperation.m b/socketio/Classes-iOS/GHUNIT/GHTest/GHTestOperation.m deleted file mode 100644 index 02301a0..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestOperation.m +++ /dev/null @@ -1,63 +0,0 @@ -// -// GHTestOperation.m -// GHUnit -// -// Created by Gabriel Handford on 6/4/10. -// Copyright 2010. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import "GHTestOperation.h" - -//! @cond DEV - -@implementation GHTestOperation - -- (id)initWithTest:(id)test options:(GHTestOptions)options { - if ((self = [super init])) { - test_ = [test retain]; - options_ = options; - } - return self; -} - -- (void)dealloc { - [test_ release]; - [super dealloc]; -} - -- (void)cancel { - [super cancel]; - [test_ cancel]; -} - -- (void)main { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (!self.isCancelled) - [test_ run:options_]; - [pool release]; -} - -@end - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestRunner.h b/socketio/Classes-iOS/GHUNIT/GHTest/GHTestRunner.h deleted file mode 100644 index d0eee08..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestRunner.h +++ /dev/null @@ -1,221 +0,0 @@ -// -// GHTestRunner.h -// -// Created by Gabriel Handford on 1/16/09. -// Copyright 2008 Gabriel Handford -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -// -// Portions of this file fall under the following license, marked with: -// GTM_BEGIN : GTM_END -// -// Copyright 2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -#import "GHTestGroup.h" -#import "GHTestSuite.h" - -@class GHTestRunner; - -/*! - Notifies about the test run. - Delegates can be guaranteed to be notified on the main thread. - */ -@protocol GHTestRunnerDelegate -@optional - -/*! - Test run started. - @param runner Runner - */ -- (void)testRunnerDidStart:(GHTestRunner *)runner; - -/*! - Test run did start test. - @param runner Runner - @param test Test - */ -- (void)testRunner:(GHTestRunner *)runner didStartTest:(id)test; - -/*! - Test run did update test. - @param runner Runner - @param test Test - */ -- (void)testRunner:(GHTestRunner *)runner didUpdateTest:(id)test; - -/*! - Test run did end test. - @param runner Runner - @param test Test - */ -- (void)testRunner:(GHTestRunner *)runner didEndTest:(id)test; - -/*! - Test run did cancel. - @param runner Runner - */ -- (void)testRunnerDidCancel:(GHTestRunner *)runner; - -/*! - Test run did end. - @param runner Runner - */ -- (void)testRunnerDidEnd:(GHTestRunner *)runner; - -/*! - Test run did log message. - @param runner Runner - @param didLog Message - */ -- (void)testRunner:(GHTestRunner *)runner didLog:(NSString *)didLog; - -/*! - Test run test did log message. - @param runner Runner - @param test Test - @param didLog Message - */ -- (void)testRunner:(GHTestRunner *)runner test:(id)test didLog:(NSString *)didLog; - -@end - -/*! - Runs the tests. - Tests are run a separate thread though delegates are called on the main thread by default. - - For example, - - GHTestRunner *runner = [[GHTestRunner alloc] initWithTest:suite]; - runner.delegate = self; - [runner runTests]; - - */ -@interface GHTestRunner : NSObject { - - id test_; // The test to run; Could be a GHTestGroup (suite), GHTestGroup (test case), or GHTest (target/selector) - - NSObject *delegate_; // weak - - GHTestOptions options_; - - BOOL running_; - BOOL cancelling_; - - NSTimeInterval startInterval_; - - NSOperationQueue *operationQueue_; //! If running a suite in operation queue -} - -@property (retain) id test; -@property (assign) NSObject *delegate; -@property (assign) GHTestOptions options; -@property (readonly) GHTestStats stats; -@property (readonly, getter=isRunning) BOOL running; -@property (readonly, getter=isCancelling) BOOL cancelling; -@property (readonly) NSTimeInterval interval; -@property (retain, nonatomic) NSOperationQueue *operationQueue; -@property (assign, nonatomic, getter=isInParallel) BOOL inParallel; - -/*! - Create runner for test. - @param test Test - */ -- (id)initWithTest:(id)test; - -/*! - Create runner for all tests. - @see [GHTesting loadAllTestCases]. - @result Runner - */ -+ (GHTestRunner *)runnerForAllTests; - -/*! - Create runner for test suite. - @param suite Suite - @result Runner - */ -+ (GHTestRunner *)runnerForSuite:(GHTestSuite *)suite; - -/*! - Create runner for class and method. - @param testClassName Test class name - @param methodName Test method - @result Runner - */ -+ (GHTestRunner *)runnerForTestClassName:(NSString *)testClassName methodName:(NSString *)methodName; - -/*! - Get the runner from the environment. - If the TEST env is set, then we will only run that test case or test method. - */ -+ (GHTestRunner *)runnerFromEnv; - -/*! - Run the test runner. Usually called from the test main. - Reads the TEST environment variable and filters on that; or all tests are run. - @result 0 is success, otherwise the failure count - */ -+ (int)run; - -/*! - Run in the background. - */ -- (void)runInBackground; - -/*! - Start the test runner. - @result 0 is success, otherwise the failure count - */ -- (int)runTests; - -/*! - Cancel test run. - */ -- (void)cancel; - -/*! - Write message to console. - @param message Message to log - */ -- (void)log:(NSString *)message; - -@end - -//! @endcond - diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestRunner.m b/socketio/Classes-iOS/GHUNIT/GHTest/GHTestRunner.m deleted file mode 100644 index c3ed855..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestRunner.m +++ /dev/null @@ -1,291 +0,0 @@ -// -// GHTestRunner.m -// -// Copyright 2008 Gabriel Handford -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -// -// Portions of this file fall under the following license, marked with: -// GTM_BEGIN : GTM_END -// -// Copyright 2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -#import "GHNSObject+Invocation.h" - -#import "GHTestRunner.h" -#import "GHTestSuite.h" -#import "GHTesting.h" - -#import - -@interface GHTestRunner () -- (void)_notifyStart; -- (void)_notifyCancelled; -- (void)_notifyFinished; -- (void)_log:(NSString *)message; -@end - -@implementation GHTestRunner - -#define kGHTestRunnerDelegateProxyWait YES - -@synthesize test=test_, options=options_, delegate=delegate_, running=running_, cancelling=cancelling_, -operationQueue=operationQueue_; - -- (id)initWithTest:(id)test { - if ((self = [self init])) { - test_ = [test retain]; - test_.delegate = self; - } - return self; -} - -- (void)dealloc { - [test_ release]; - [operationQueue_ release]; - [super dealloc]; -} - -+ (GHTestRunner *)runnerForAllTests { - GHTestSuite *suite = [GHTestSuite allTests]; - return [self runnerForSuite:suite]; -} - -+ (GHTestRunner *)runnerForSuite:(GHTestSuite *)suite { - GHTestRunner *runner = [[GHTestRunner alloc] initWithTest:suite]; - return [runner autorelease]; -} - -+ (GHTestRunner *)runnerForTestClassName:(NSString *)testClassName methodName:(NSString *)methodName { - return [self runnerForSuite:[GHTestSuite suiteWithTestCaseClass:NSClassFromString(testClassName) - method:NSSelectorFromString(methodName)]]; -} - -+ (GHTestRunner *)runnerFromEnv { - GHTestSuite *suite = [GHTestSuite suiteFromEnv]; - GHTestRunner *runner = [GHTestRunner runnerForSuite:suite]; - if (getenv("GHUNIT_RERAISE")) runner.options = GHTestOptionReraiseExceptions; - return runner; -} - -+ (int)run { - GHTestRunner *testRunner = [GHTestRunner runnerFromEnv]; - [testRunner runTests]; - return (int)testRunner.stats.failureCount; -} - -- (void)setInParallel:(BOOL)inParallel { - if (inParallel) { - NSOperationQueue *operationQueue = [[[NSOperationQueue alloc] init] autorelease]; - operationQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount; - self.operationQueue = operationQueue; - } else { - self.operationQueue = nil; - } -} - -- (BOOL)isInParallel { - return (!!self.operationQueue); -} - -- (int)runTests { - if (cancelling_ || running_) return -1; - - running_ = YES; - startInterval_ = [NSDate timeIntervalSinceReferenceDate]; - [self _notifyStart]; - - if (operationQueue_ && [test_ respondsToSelector:@selector(runInOperationQueue:options:)]) { - [(id)test_ runInOperationQueue:operationQueue_ options:options_]; - } else { - [test_ run:options_]; - } - return (int)self.stats.failureCount; -} - -- (NSTimeInterval)interval { - return ([NSDate timeIntervalSinceReferenceDate] - startInterval_); -} - -- (void)cancel { - if (cancelling_) return; - cancelling_ = YES; - [operationQueue_ cancelAllOperations]; - [test_ cancel]; -} - -- (void)runInBackground { - [NSThread detachNewThreadSelector:@selector(_runInBackground) toTarget:self withObject:nil]; -} - -- (void)_runInBackground { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - [self runTests]; - [pool release]; -} - -- (GHTestStats)stats { - return [test_ stats]; -} - -- (void)log:(NSString *)message { - fputs([message UTF8String], stderr); - fflush(stderr); -} - -- (void)_log:(NSString *)message { - fputs([message UTF8String], stdout); - fputs("\n", stdout); - fflush(stdout); - - if ([delegate_ respondsToSelector:@selector(testRunner:didLog:)]) - [[delegate_ ghu_proxyOnMainThread:kGHTestRunnerDelegateProxyWait] testRunner:self didLog:message]; -} - -#pragma mark Delegates (GHTest) - -- (void)testDidStart:(id)test source:(id)source { - if (![source conformsToProtocol:@protocol(GHTestGroup)]) { - [self log:[NSString stringWithFormat:@"Starting %@\n", [source identifier]]]; - } - - if ([delegate_ respondsToSelector:@selector(testRunner:didStartTest:)]) - [[delegate_ ghu_proxyOnMainThread:kGHTestRunnerDelegateProxyWait] testRunner:self didStartTest:source]; -} - -- (void)testDidUpdate:(id)test source:(id)source { - if ([delegate_ respondsToSelector:@selector(testRunner:didUpdateTest:)]) - [[delegate_ ghu_proxyOnMainThread:kGHTestRunnerDelegateProxyWait] testRunner:self didUpdateTest:source]; -} - -- (void)testDidEnd:(id)test source:(id)source { - - if ([source status] != GHTestStatusCancelled) { - if (![source conformsToProtocol:@protocol(GHTestGroup)]) { - NSString *message = [NSString stringWithFormat:@" %@ (%0.3fs)\n\n", - ([source stats].failureCount > 0 ? @"FAIL" : @"OK"), [source interval]]; - [self log:message]; - } - - if ([delegate_ respondsToSelector:@selector(testRunner:didEndTest:)]) - [[delegate_ ghu_proxyOnMainThread:kGHTestRunnerDelegateProxyWait] testRunner:self didEndTest:source]; - - } else { - [self log:[NSString stringWithFormat:@"Cancelled\n", [source identifier]]]; - } - - if (cancelling_) { - [self _notifyCancelled]; - } else if (test_ == source && [source status] != GHTestStatusCancelled) { - // If the test associated with this runner ended then notify - [self _notifyFinished]; - } -} - -- (void)test:(id)test didLog:(NSString *)message source:(id)source { - [self _log:[NSString stringWithFormat:@"%@: %@", source, message]]; - if ([delegate_ respondsToSelector:@selector(testRunner:test:didLog:)]) - [[delegate_ ghu_proxyOnMainThread:kGHTestRunnerDelegateProxyWait] testRunner:self test:source didLog:message]; -} - -#pragma mark Notifications (Private) - -- (void)_notifyStart { - NSString *message = [NSString stringWithFormat:@"Test Suite '%@' started.\n", [test_ name]]; - [self log:message]; - - if ([delegate_ respondsToSelector:@selector(testRunnerDidStart:)]) - [[delegate_ ghu_proxyOnMainThread:kGHTestRunnerDelegateProxyWait] testRunnerDidStart:self]; -} - -- (void)_notifyCancelled { - NSString *message = [NSString stringWithFormat:@"Test Suite '%@' cancelled.\n", [test_ name]]; - [self log:message]; - - cancelling_ = NO; - running_ = NO; - - if ([delegate_ respondsToSelector:@selector(testRunnerDidCancel:)]) - [[delegate_ ghu_proxyOnMainThread:kGHTestRunnerDelegateProxyWait] testRunnerDidCancel:self]; -} - -- (void)_notifyFinished { - NSString *message = [NSString stringWithFormat:@"Test Suite '%@' finished.\n" - "Executed %d of %d tests, with %d failures in %0.3f seconds (%d disabled).\n", - [test_ name], - ([test_ stats].succeedCount + [test_ stats].failureCount), - [test_ stats].testCount, - [test_ stats].failureCount, - [test_ interval], - [test_ disabledCount]]; - [self log:message]; - - if ([test_ isKindOfClass:[GHTestGroup class]]) { - GHTestGroup *testGroup = (GHTestGroup *)test_; - [self log:@"\nFailed tests:\n"]; - for(id test in [testGroup failedTests]) { - [self log:[NSString stringWithFormat:@"\t%@\n", [test identifier]]]; - } - [self log:@"\n"]; - } - - if ([test_ isKindOfClass:[GHTestSuite class]]) { - GHTestSuite *testSuite = (GHTestSuite *)test_; - - // Log JUnit XML if environment variable is set - if (getenv("WRITE_JUNIT_XML")) { - NSError *error = nil; - if (![testSuite writeJUnitXML:&error]) { - [self log:[NSString stringWithFormat:@"Error writing JUnit XML: %@\n", [error localizedDescription]]]; - } else { - [self log:@"Wrote JUnit XML successfully.\n"]; - } - } - } - - cancelling_ = NO; - running_ = NO; - - if ([delegate_ respondsToSelector:@selector(testRunnerDidEnd:)]) - [[delegate_ ghu_proxyOnMainThread:kGHTestRunnerDelegateProxyWait] testRunnerDidEnd:self]; -} - -@end - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestSuite.h b/socketio/Classes-iOS/GHUNIT/GHTest/GHTestSuite.h deleted file mode 100644 index 9311614..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestSuite.h +++ /dev/null @@ -1,118 +0,0 @@ -// -// GHTestSuite.h -// GHUnit -// -// Created by Gabriel Handford on 1/25/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -#import "GHTestGroup.h" - -/*! - If set, will run it as a "test filter" like the env variable TEST. - */ -extern NSString *GHUnitTest; - - -/*! - Test suite is an alias for test group. - - A test case is an instance of a test case class with test methods. - A test is a id which represents a target and a selector. - A test group is a collection of tests; A collection of id (GHTest or GHTestGroup). - - For example, if you have 2 test cases, GHTestCase1 (with some test methods) and GHTestCase2 (with some test methods), - your test suite might look like: - -"Tests" (GHTestSuite) - GHTestGroup (collection of tests from GHTestCase1) - - (void)testA1 (GHTest with target GHTestCase1 + testA1) - - (void)testA2 (GHTest with target GHTestCase1 + testA2) - GHTestGroup (collection of tests from GHTestCase2) - - (void)testB1; (GHTest with target GHTestCase2 + testB1) - - (void)testB2; (GHTest with target GHTestCase2 + testB2) - - */ -@interface GHTestSuite : GHTestGroup { } - -/*! - Create test suite with test cases. - @param name Label to give the suite - @param testCases Array of init'ed test case classes - @param delegate Delegate - */ -- (id)initWithName:(NSString *)name testCases:(NSArray *)testCases delegate:(id)delegate; - -/*! - Creates a suite of all tests. - Will load all classes that subclass from GHTestCase, SenTestCase or GTMTestCase (or register test case class). - @result Suite - */ -+ (GHTestSuite *)allTests; - -/*! - Create suite of tests with filter. - This is useful for running a single test or all tests in a single test case. - - For example, - 'GHSlowTest' -- Runs all test method in GHSlowTest - 'GHSlowTest/testSlowA -- Only runs the test method testSlowA in GHSlowTest - - @param testFilter Test filter - @result Suite - */ -+ (GHTestSuite *)suiteWithTestFilter:(NSString *)testFilter; - -/*! - Create suite of tests that start with prefix. - @param prefix If test case class starts with the prefix; If nil or empty string, returns all tests - @param options Compare options - */ -+ (GHTestSuite *)suiteWithPrefix:(NSString *)prefix options:(NSStringCompareOptions)options; - -/*! - Suite for a single test/method. - @param testCaseClass Test case class - @param method Method - @result Suite - */ -+ (GHTestSuite *)suiteWithTestCaseClass:(Class)testCaseClass method:(SEL)method; - -/*! - Return test suite based on environment (TEST=TestFoo/foo) - @result Suite - */ -+ (GHTestSuite *)suiteFromEnv; - -@end - -@interface GHTestSuite (JUnitXML) - -- (BOOL)writeJUnitXML:(NSError **)error; - -@end - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestSuite.m b/socketio/Classes-iOS/GHUNIT/GHTest/GHTestSuite.m deleted file mode 100644 index 32680a9..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/GHTestSuite.m +++ /dev/null @@ -1,167 +0,0 @@ -// -// GHTestSuite.m -// GHUnit -// -// Created by Gabriel Handford on 1/25/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -#import "GHTestSuite.h" - -#import "GHTesting.h" -#import "GHTestGroup+JUnitXML.h" - -NSString *GHUnitTest = NULL; - -@implementation GHTestSuite - -- (id)initWithName:(NSString *)name testCases:(NSArray *)testCases delegate:(id)delegate { - if ((self = [super initWithName:name delegate:delegate])) { - for(id testCase in testCases) { - [self addTestCase:testCase]; - } - } - return self; -} - -+ (GHTestSuite *)allTests { - NSArray *testCases = [[GHTesting sharedInstance] loadAllTestCases]; - GHTestSuite *allTests = [[self alloc] initWithName:@"Tests" testCases:nil delegate:nil]; - for(id testCase in testCases) { - [allTests addTestCase:testCase]; - } - return [allTests autorelease]; -} - -+ (GHTestSuite *)suiteWithTestCaseClass:(Class)testCaseClass method:(SEL)method { - NSString *name = [NSString stringWithFormat:@"%@/%@", NSStringFromClass(testCaseClass), NSStringFromSelector(method)]; - GHTestSuite *testSuite = [[[GHTestSuite alloc] initWithName:name testCases:nil delegate:nil] autorelease]; - id testCase = [[[testCaseClass alloc] init] autorelease]; - if (!testCase) { - NSLog(@"Couldn't instantiate test: %@", NSStringFromClass(testCaseClass)); - return nil; - } - GHTestGroup *group = [[GHTestGroup alloc] initWithTestCase:testCase selector:method delegate:nil]; - [testSuite addTestGroup:group]; - [group release]; - return testSuite; -} - -+ (GHTestSuite *)suiteWithPrefix:(NSString *)prefix options:(NSStringCompareOptions)options { - if (!prefix || [prefix isEqualToString:@""]) return [self allTests]; - - NSArray *testCases = [[GHTesting sharedInstance] loadAllTestCases]; - NSString *name = [NSString stringWithFormat:@"Tests (%@)", prefix]; - GHTestSuite *testSuite = [[self alloc] initWithName:name testCases:nil delegate:nil]; - for(id testCase in testCases) { - NSString *className = NSStringFromClass([testCase class]); - if ([className compare:prefix options:options range:NSMakeRange(0, [prefix length])] == NSOrderedSame) - [testSuite addTestCase:testCase]; - } - return [testSuite autorelease]; - -} - -+ (GHTestSuite *)suiteWithTestFilter:(NSString *)testFilterString { - NSArray *testFilters = [testFilterString componentsSeparatedByString:@","]; - GHTestSuite *testSuite = [[GHTestSuite alloc] initWithName:testFilterString testCases:nil delegate:nil]; - - for(NSString *testFilter in testFilters) { - NSArray *components = [testFilter componentsSeparatedByString:@"/"]; - if ([components count] == 2) { - NSString *testCaseClassName = [components objectAtIndex:0]; - Class testCaseClass = NSClassFromString(testCaseClassName); - id testCase = [[[testCaseClass alloc] init] autorelease]; - if (!testCase) { - NSLog(@"Couldn't find test: %@", testCaseClassName); - continue; - } - NSString *methodName = [components objectAtIndex:1]; - GHTestGroup *group = [[GHTestGroup alloc] initWithTestCase:testCase selector:NSSelectorFromString(methodName) delegate:nil]; - [testSuite addTestGroup:group]; - [group release]; - } else { - Class testCaseClass = NSClassFromString(testFilter); - id testCase = [[[testCaseClass alloc] init] autorelease]; - if (!testCase) { - NSLog(@"Couldn't find test: %@", testFilter); - continue; - } - [testSuite addTestCase:testCase]; - } - } - - return [testSuite autorelease]; -} - -+ (GHTestSuite *)suiteFromEnv { - const char* cTestFilter = getenv("TEST"); - if (cTestFilter) { - NSString *testFilter = [NSString stringWithUTF8String:cTestFilter]; - return [GHTestSuite suiteWithTestFilter:testFilter]; - } else { - if (GHUnitTest != NULL) return [GHTestSuite suiteWithTestFilter:GHUnitTest]; - return [GHTestSuite allTests]; - } -} - -@end - -@implementation GHTestSuite (JUnitXML) - -/* - Override logic to write children individually, as we want each test group's - JUnit XML to be in its own file. - */ -- (BOOL)writeJUnitXML:(NSError **)error { - NSParameterAssert(error); - BOOL allSuccess = YES; - - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSString *tmpDir = NSTemporaryDirectory(); - NSString *resultsDir = [tmpDir stringByAppendingPathComponent:@"test-results"]; - - if (![fileManager fileExistsAtPath:resultsDir]) { - if (![fileManager createDirectoryAtPath:resultsDir withIntermediateDirectories:YES attributes:nil error:error]) { - NSLog (@"Error while creating results directory: %@", [*error localizedDescription]); - return NO; - } - } - - for (id child in self.children) { - if ([child respondsToSelector:@selector(writeJUnitXMLAtPath:error:)]) { - if (![child writeJUnitXMLAtPath:resultsDir error:error]) { - NSLog (@"Error writing JUnit XML: %@", [*error localizedDescription]); - allSuccess = NO; - } - } - } - return allSuccess; -} - -@end - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/GHTesting.h b/socketio/Classes-iOS/GHUNIT/GHTest/GHTesting.h deleted file mode 100644 index f0fed9a..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/GHTesting.h +++ /dev/null @@ -1,158 +0,0 @@ -// -// GHTesting.h -// GHUnit -// -// Created by Gabriel Handford on 1/30/09. -// Copyright 2008 Gabriel Handford -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -// -// Portions of this file fall under the following license, marked with: -// GTM_BEGIN : GTM_END -// -// Copyright 2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -#import -#import "GHUnit.h" - -#ifdef __cplusplus -extern "C" NSString *GHUStackTraceFromException(NSException *e); -#else -extern NSString *GHUStackTraceFromException(NSException *e); -#endif - -// GTM_BEGIN -BOOL isTestFixtureOfClass(Class aClass, Class testCaseClass); -// GTM_END - -/*! - Utility test for loading and running tests. - - Much of this is borrowed from GTM/UnitTesting. - */ -@interface GHTesting : NSObject { - - NSMutableArray/* of NSString*/ *testCaseClassNames_; - -} - -/*! - The shared testing instance. - */ -+ (GHTesting *)sharedInstance; - -/*! - Load all test classes that we can "see". - @result Array of initialized (and autoreleased) test case classes in an autoreleased array. - */ -- (NSArray *)loadAllTestCases; - -/*! - Load tests from target. - @param target Target - @result Array of id - */ -- (NSArray *)loadTestsFromTarget:(id)target; - -/*! - See if class is of a registered test case class. - @param aClass Class - */ -- (BOOL)isTestCaseClass:(Class)aClass; - -/*! - Register test case class. - @param aClass Class - */ -- (void)registerClass:(Class)aClass; - -/*! - Register test case class by name. - @param className Class name (via NSStringFromClass(aClass) - */ -- (void)registerClassName:(NSString *)className; - -/*! - Format test exception. - @param exception Exception - @result Description - */ -+ (NSString *)descriptionForException:(NSException *)exception; - -/*! - Filename for cause of test exception. - @param test Test - @result Filename - */ -+ (NSString *)exceptionFilenameForTest:(id)test; - -/*! - Line number for cause of test exception. - @param test Test - @result Line number - */ -+ (NSInteger)exceptionLineNumberForTest:(id)test; - -/*! - Run test. - @param target Target - @param selector Selector - @param exception Exception, if set, is retained and should be released by the caller. - @param interval Time to run the test - @param reraiseExceptions If YES, will re-raise exceptions - */ -+ (BOOL)runTestWithTarget:(id)target selector:(SEL)selector exception:(NSException **)exception - interval:(NSTimeInterval *)interval reraiseExceptions:(BOOL)reraiseExceptions; - -/*! - Same as normal runTest without catching exceptions. - @param target Target - @param selector Selector - @param exception Exception, if set, is retained and should be released by the caller. - @param interval Time to run the test - */ -+ (BOOL)runTestOrRaiseWithTarget:(id)target selector:(SEL)selector exception:(NSException **)exception interval:(NSTimeInterval *)interval; - -@end - -@protocol GHSenTestCase -- (void)raiseAfterFailure; -@end - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/GHTesting.m b/socketio/Classes-iOS/GHUNIT/GHTest/GHTesting.m deleted file mode 100644 index 024749f..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/GHTesting.m +++ /dev/null @@ -1,340 +0,0 @@ -// -// GHTesting.m -// GHUnit -// -// Created by Gabriel Handford on 1/30/09. -// Copyright 2008 Gabriel Handford -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -// -// Portions of this file fall under the following license, marked with: -// GTM_BEGIN : GTM_END -// -// Copyright 2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -#import "GHTesting.h" -#import "GHTest.h" -#import "GHTestCase.h" -#import "GTMStackTrace.h" - -#import - -NSString *GHUStackTraceFromException(NSException *e) { - return GHU_GTMStackTraceFromException(e); -} - -NSInteger ClassSort(id a, id b, void *context) { - const char *nameA = class_getName([a class]); - const char *nameB = class_getName([b class]); - return strcmp(nameA, nameB); -} - -// GTM_BEGIN -// Used for sorting methods below -static int MethodSort(const void *a, const void *b) { - const char *nameA = sel_getName(method_getName(*(Method*)a)); - const char *nameB = sel_getName(method_getName(*(Method*)b)); - return strcmp(nameA, nameB); -} - -BOOL isTestFixtureOfClass(Class aClass, Class testCaseClass) { - if (testCaseClass == NULL) return NO; - BOOL iscase = NO; - Class superclass; - for (superclass = aClass; - !iscase && superclass; - superclass = class_getSuperclass(superclass)) { - iscase = superclass == testCaseClass ? YES : NO; - } - return iscase; -} -// GTM_END - -@implementation GHTesting - -static GHTesting *gSharedInstance; - -+ (GHTesting *)sharedInstance { - @synchronized(self) { - if (!gSharedInstance) gSharedInstance = [[GHTesting alloc] init]; - } - return gSharedInstance; -} - -- (id)init { - if ((self = [super init])) { - // Default test cases - testCaseClassNames_ = [[NSMutableArray arrayWithObjects: - @"GHTestCase", - @"SenTestCase", - @"GTMTestCase", - nil] retain]; - } - return self; -} - -- (BOOL)isTestCaseClass:(Class)aClass { - for(NSString *className in testCaseClassNames_) { - if (isTestFixtureOfClass(aClass, NSClassFromString(className))) return YES; - } - return NO; -} - -- (void)registerClass:(Class)aClass { - [self registerClassName:NSStringFromClass(aClass)]; -} - -- (void)registerClassName:(NSString *)className { - [testCaseClassNames_ addObject:className]; -} - -+ (NSString *)descriptionForException:(NSException *)exception { - NSNumber *lineNumber = [[exception userInfo] objectForKey:GHTestLineNumberKey]; - NSString *lineDescription = (lineNumber ? [lineNumber description] : @"Unknown"); - NSString *filename = [[[[exception userInfo] objectForKey:GHTestFilenameKey] stringByStandardizingPath] stringByAbbreviatingWithTildeInPath]; - NSString *filenameDescription = (filename ? filename : @"Unknown"); - - return [NSString stringWithFormat:@"\n\tName: %@\n\tFile: %@\n\tLine: %@\n\tReason: %@\n\n%@", - [exception name], - filenameDescription, - lineDescription, - [exception reason], - GHU_GTMStackTraceFromException(exception)]; -} - -+ (NSString *)exceptionFilenameForTest:(id)test { - return [[[[[test exception] userInfo] objectForKey:GHTestFilenameKey] stringByStandardizingPath] stringByAbbreviatingWithTildeInPath]; -} - -+ (NSInteger)exceptionLineNumberForTest:(id)test { - return [[[[test exception] userInfo] objectForKey:GHTestLineNumberKey] integerValue]; -} - - -- (NSArray *)loadAllTestCases { - NSMutableArray *testCases = [NSMutableArray array]; - - int count = objc_getClassList(NULL, 0); - NSMutableData *classData = [NSMutableData dataWithLength:sizeof(Class) * count]; - Class *classes = (Class*)[classData mutableBytes]; - NSAssert(classes, @"Couldn't allocate class list"); - objc_getClassList(classes, count); - - for (int i = 0; i < count; ++i) { - Class currClass = classes[i]; - id testcase = nil; - - if ([self isTestCaseClass:currClass]) { - testcase = [[currClass alloc] init]; - } else { - continue; - } - - [testCases addObject:testcase]; - [testcase release]; - } - - return [testCases sortedArrayUsingFunction:ClassSort context:NULL]; -} - -// GTM_BEGIN - -- (NSArray *)loadTestsFromTarget:(id)target { - NSMutableArray *tests = [NSMutableArray array]; - - unsigned int methodCount; - Method *methods = class_copyMethodList([target class], &methodCount); - if (!methods) { - return nil; - } - // This handles disposing of methods for us even if an - // exception should fly. - [NSData dataWithBytesNoCopy:methods - length:sizeof(Method) * methodCount]; - // Sort our methods so they are called in Alphabetical order just - // because we can. - qsort(methods, methodCount, sizeof(Method), MethodSort); - for (size_t j = 0; j < methodCount; ++j) { - Method currMethod = methods[j]; - SEL sel = method_getName(currMethod); - char *returnType = NULL; - const char *name = sel_getName(sel); - // If it starts with test, takes 2 args (target and sel) and returns - // void run it. - if (strstr(name, "test") == name) { - returnType = method_copyReturnType(currMethod); - if (returnType) { - // This handles disposing of returnType for us even if an - // exception should fly. Length +1 for the terminator, not that - // the length really matters here, as we never reference inside - // the data block. - [NSData dataWithBytesNoCopy:returnType - length:strlen(returnType) + 1]; - } - } - if (returnType // True if name starts with "test" - && strcmp(returnType, @encode(void)) == 0 - && method_getNumberOfArguments(currMethod) == 2) { - - GHTest *test = [GHTest testWithTarget:target selector:sel]; - [tests addObject:test]; - } - } - - return tests; -} - -+ (BOOL)runTestWithTarget:(id)target selector:(SEL)selector exception:(NSException **)exception interval:(NSTimeInterval *)interval - reraiseExceptions:(BOOL)reraiseExceptions { - - // If re-raising, run runTestOrRaise - if (reraiseExceptions) return [self runTestOrRaiseWithTarget:target selector:selector exception:exception interval:interval]; - - NSDate *startDate = [NSDate date]; - NSException *testException = nil; - - @try { - // Wrap things in autorelease pools because they may - // have an STMacro in their dealloc which may get called - // when the pool is cleaned up - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - // We don't log exceptions here, instead we let the person that called - // this log the exception. This ensures they are only logged once but the - // outer layers get the exceptions to report counts, etc. - @try { - // Private setUp internal to GHUnit (in case subclasses fail to call super) - if ([target respondsToSelector:@selector(_setUp)]) - [target performSelector:@selector(_setUp)]; - - if ([target respondsToSelector:@selector(setUp)]) - [target performSelector:@selector(setUp)]; - @try { - if ([target respondsToSelector:@selector(setCurrentSelector:)]) - [target setCurrentSelector:selector]; - - // If this isn't set SenTest macros don't raise - if ([target respondsToSelector:@selector(raiseAfterFailure)]) - [target raiseAfterFailure]; - - // Runs the test - [target performSelector:selector]; - - } @catch (NSException *exception) { - if (!testException) testException = [exception retain]; - } - if ([target respondsToSelector:@selector(setCurrentSelector:)]) - [target setCurrentSelector:NULL]; - - if ([target respondsToSelector:@selector(tearDown)]) - [target performSelector:@selector(tearDown)]; - - // Private tearDown internal to GHUnit (in case subclasses fail to call super) - if ([target respondsToSelector:@selector(_tearDown)]) - [target performSelector:@selector(_tearDown)]; - - } @catch (NSException *exception) { - if (!testException) testException = [exception retain]; - } - [pool release]; - } @catch (NSException *exception) { - if (!testException) testException = [exception retain]; - } - - if (interval) *interval = [[NSDate date] timeIntervalSinceDate:startDate]; - if (exception) *exception = testException; - BOOL passed = (!testException); - - if (testException && [target respondsToSelector:@selector(handleException:)]) { - [target handleException:testException]; - } - - return passed; -} - -+ (BOOL)runTestOrRaiseWithTarget:(id)target selector:(SEL)selector exception:(NSException **)exception interval:(NSTimeInterval *)interval { - - NSDate *startDate = [NSDate date]; - NSException *testException = nil; - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - if ([target respondsToSelector:@selector(_setUp)]) - [target performSelector:@selector(_setUp)]; - - if ([target respondsToSelector:@selector(setUp)]) - [target performSelector:@selector(setUp)]; - - if ([target respondsToSelector:@selector(setCurrentSelector:)]) - [target setCurrentSelector:selector]; - - // If this isn't set SenTest macros don't raise - if ([target respondsToSelector:@selector(raiseAfterFailure)]) - [target raiseAfterFailure]; - - // Runs the test - [target performSelector:selector]; - - if ([target respondsToSelector:@selector(setCurrentSelector:)]) - [target setCurrentSelector:NULL]; - - if ([target respondsToSelector:@selector(tearDown)]) - [target performSelector:@selector(tearDown)]; - - // Private tearDown internal to GHUnit (in case subclasses fail to call super) - if ([target respondsToSelector:@selector(_tearDown)]) - [target performSelector:@selector(_tearDown)]; - - - [pool release]; - - if (interval) *interval = [[NSDate date] timeIntervalSinceDate:startDate]; - if (exception) *exception = testException; - BOOL passed = (!testException); - - if (testException && [target respondsToSelector:@selector(handleException:)]) { - [target handleException:testException]; - } - - return passed; -} - -// GTM_END - -@end - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/NSException+GHTestFailureExceptions.h b/socketio/Classes-iOS/GHUNIT/GHTest/NSException+GHTestFailureExceptions.h deleted file mode 100644 index 214bcc5..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/NSException+GHTestFailureExceptions.h +++ /dev/null @@ -1,97 +0,0 @@ -// -// NSException+GHTestFailureExceptions.h -// -// Created by Johannes Rudolph on 23.09.09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -// -// Portions of this file fall under the following license, marked with: -// GTM_BEGIN : GTM_END -// -// Copyright 2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -extern NSString *const GHTestFilenameKey; -extern NSString *const GHTestLineNumberKey; -extern NSString *const GHTestFailureException; - - -// GTM_BEGIN - -#import - -@interface NSException(GHUTestFailureExceptions) -+ (NSException *)ghu_failureInFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ...; -+ (NSException *)ghu_failureInCondition:(NSString *)condition - isTrue:(BOOL)isTrue - inFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ...; -+ (NSException *)ghu_failureInEqualityBetweenObject:(id)left - andObject:(id)right - inFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ...; -+ (NSException *)ghu_failureInInequalityBetweenObject:(id)left - andObject:(id)right - inFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ...; -+ (NSException *)ghu_failureInEqualityBetweenValue:(NSValue *)left - andValue:(NSValue *)right - withAccuracy:(NSValue *)accuracy - inFile:(NSString *)filename - atLine:(int) ineNumber - withDescription:(NSString *)formatString, ...; -+ (NSException *)ghu_failureInRaise:(NSString *)expression - inFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ...; -+ (NSException *)ghu_failureInRaise:(NSString *)expression - exception:(NSException *)exception - inFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ...; -@end - -// GTM_END - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/NSException+GHTestFailureExceptions.m b/socketio/Classes-iOS/GHUNIT/GHTest/NSException+GHTestFailureExceptions.m deleted file mode 100644 index 312fccc..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/NSException+GHTestFailureExceptions.m +++ /dev/null @@ -1,263 +0,0 @@ -// -// NSException+GHTestFailureExceptions.m -// -// Created by Johannes Rudolph on 23.09.09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -// -// Portions of this file fall under the following license, marked with: -// GTM_BEGIN : GTM_END -// -// Copyright 2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -// GTM_BEGIN - Contains modifications by JR - -#import - -#import "NSException+GHTestFailureExceptions.h" - -#import "NSValue+GHValueFormatter.h" - -NSString *const GHTestFilenameKey = @"GHTestFilenameKey"; -NSString *const GHTestLineNumberKey = @"GHTestLineNumberKey"; -NSString *const GHTestFailureException = @"GHTestFailureException"; - -@interface NSException(GHTestFailureExceptionsPrivateAdditions) -+ (NSException *)ghu_failureInFile:(NSString *)filename - atLine:(int)lineNumber - reason:(NSString *)reason; -@end - -@implementation NSException(GHTestFailureExceptionsPrivateAdditions) -+ (NSException *)ghu_failureInFile:(NSString *)filename - atLine:(int)lineNumber - reason:(NSString *)reason { - NSDictionary *userInfo = - [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithInteger:lineNumber], GHTestLineNumberKey, - filename, GHTestFilenameKey, - nil]; - - return [self exceptionWithName:GHTestFailureException - reason:reason - userInfo:userInfo]; -} -@end - -@implementation NSException(GHTestFailureExceptions) - -+ (NSException *)ghu_failureInFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ... { - - NSString *testDescription = @""; - if (formatString) { - va_list vl; - va_start(vl, formatString); - testDescription = - [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; - va_end(vl); - } - - NSString *reason = testDescription; - - return [self ghu_failureInFile:filename atLine:lineNumber reason:reason]; -} - -+ (NSException *)ghu_failureInCondition:(NSString *)condition - isTrue:(BOOL)isTrue - inFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ... { - - NSString *testDescription = @""; - if (formatString) { - va_list vl; - va_start(vl, formatString); - testDescription = - [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; - va_end(vl); - } - - NSString *reason = [NSString stringWithFormat:@"'%@' should be %s. %@", - condition, isTrue ? "TRUE" : "FALSE", testDescription]; - - return [self ghu_failureInFile:filename atLine:lineNumber reason:reason]; -} - -+ (NSException *)ghu_failureInEqualityBetweenObject:(id)left - andObject:(id)right - inFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ... { - - NSString *testDescription = @""; - if (formatString) { - va_list vl; - va_start(vl, formatString); - testDescription = - [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; - va_end(vl); - } - - NSString *reason = - [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@", - [left description], [right description], testDescription]; - - return [self ghu_failureInFile:filename atLine:lineNumber reason:reason]; -} - -+ (NSException *)ghu_failureInInequalityBetweenObject:(id)left - andObject:(id)right - inFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ... { - - NSString *testDescription = @""; - if (formatString) { - va_list vl; - va_start(vl, formatString); - testDescription = - [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; - va_end(vl); - } - - NSString *reason = - [NSString stringWithFormat:@"'%@' should not be equal to '%@'. %@", - [left description], [right description], testDescription]; - - return [self ghu_failureInFile:filename atLine:lineNumber reason:reason]; -} - -+ (NSException *)ghu_failureInEqualityBetweenValue:(NSValue *)left - andValue:(NSValue *)right - withAccuracy:(NSValue *)accuracy - inFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ... { - - NSString *testDescription = @""; - if (formatString) { - va_list vl; - va_start(vl, formatString); - testDescription = - [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; - va_end(vl); - } - - NSString *reason; - if (!accuracy) { - reason = - [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@", - [left ghu_contentDescription], [right ghu_contentDescription], testDescription]; - } else { - reason = - [NSString stringWithFormat:@"'%@' should be equal to '%@' +/-'%@'. %@", - [left ghu_contentDescription], [right ghu_contentDescription], [accuracy ghu_contentDescription], testDescription]; - } - - return [self ghu_failureInFile:filename atLine:lineNumber reason:reason]; -} - -+ (NSException *)ghu_failureInRaise:(NSString *)expression - inFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ... { - - NSString *testDescription = @""; - if (formatString) { - va_list vl; - va_start(vl, formatString); - testDescription = - [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; - va_end(vl); - } - - NSString *reason = [NSString stringWithFormat:@"'%@' should raise. %@", - expression, testDescription]; - - return [self ghu_failureInFile:filename atLine:lineNumber reason:reason]; -} - -+ (NSException *)ghu_failureInRaise:(NSString *)expression - exception:(NSException *)exception - inFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ... { - - NSString *testDescription = @""; - if (formatString) { - va_list vl; - va_start(vl, formatString); - testDescription = - [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; - va_end(vl); - } - - NSString *reason; - if ([[exception name] isEqualToString:GHTestFailureException]) { - // it's our exception, assume it has the right description on it. - reason = [exception reason]; - } else { - // not one of our exception, use the exceptions reason and our description - reason = [NSString stringWithFormat:@"'%@' raised '%@'. %@", - expression, [exception reason], testDescription]; - } - - return [self ghu_failureInFile:filename atLine:lineNumber reason:reason]; -} - -@end - -NSString *GHComposeString(NSString *formatString, ...) { - NSString *reason = @""; - if (formatString) { - va_list vl; - va_start(vl, formatString); - reason = - [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; - va_end(vl); - } - return reason; -} - -// GTM_END - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/NSValue+GHValueFormatter.h b/socketio/Classes-iOS/GHUNIT/GHTest/NSValue+GHValueFormatter.h deleted file mode 100644 index 268571a..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/NSValue+GHValueFormatter.h +++ /dev/null @@ -1,71 +0,0 @@ -// -// NSValue+GHValueFormatter.h -// -// Created by Johannes Rudolph on 23.9.2009. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -// -// Portions of this file fall under the following license, marked with -// SENTE_BEGIN - SENTE_END -// -// Copyright (c) 1997-2005, Sen:te (Sente SA). All rights reserved. -// -// Use of this source code is governed by the following license: -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// (1) Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// (2) Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -// IN NO EVENT SHALL Sente SA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Note: this license is equivalent to the FreeBSD license. -// -// This notice may not be removed from this file. - -#import - -// SENTE_BEGIN -@interface NSValue(GHValueFormatter) -- (NSString *)ghu_contentDescription; -@end -// SENTE_END - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/GHTest/NSValue+GHValueFormatter.m b/socketio/Classes-iOS/GHUNIT/GHTest/NSValue+GHValueFormatter.m deleted file mode 100644 index 6bd27f8..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTest/NSValue+GHValueFormatter.m +++ /dev/null @@ -1,161 +0,0 @@ -// -// NSValue+GHValueFormatter.m -// -// Created by Johannes Rudolph on 23.9.2009. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -// -// Portions of this file fall under the following license, marked with -// SENTE_BEGIN - SENTE_END -// -// Copyright (c) 1997-2005, Sen:te (Sente SA). All rights reserved. -// -// Use of this source code is governed by the following license: -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// (1) Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// (2) Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -// IN NO EVENT SHALL Sente SA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Note: this license is equivalent to the FreeBSD license. -// -// This notice may not be removed from this file. - - -#import "NSValue+GHValueFormatter.h" - -// SENTE_BEGIN - -@implementation NSValue(GHValueFormatter) -- (NSString *)ghu_contentDescription { - const char *objCType = [self objCType]; - if (objCType != NULL) { - if (strlen (objCType) == 1) { - switch (objCType[0]) { - case 'c': { - char scalarValue = 0; - [self getValue:(void *)&scalarValue]; - return [NSString stringWithFormat:@"%c", scalarValue]; - } - case 'C': { - unsigned char scalarValue = 0; - [self getValue:(void *)&scalarValue]; - return [NSString stringWithFormat:@"%c", scalarValue]; - } - case 's': { - short scalarValue = 0; - [self getValue:(void *)&scalarValue]; - return [NSString stringWithFormat:@"%hi", scalarValue]; - } - case 'S': { - unsigned short scalarValue = 0; - [self getValue:(void *)&scalarValue]; - return [NSString stringWithFormat:@"%hu", scalarValue]; - } - case 'l': { - long scalarValue = 0; - [self getValue:(void *)&scalarValue]; - return [NSString stringWithFormat:@"%li", scalarValue]; - } - case 'L': { - unsigned long scalarValue = 0; - [self getValue:(void *)&scalarValue]; - return [NSString stringWithFormat:@"%lu", scalarValue]; - } - case 'q': { - long long scalarValue = 0; - [self getValue:(void *)&scalarValue]; - return [NSString stringWithFormat:@"%lli", scalarValue]; - } - case 'Q': { - unsigned long long scalarValue = 0; - [self getValue:(void *)&scalarValue]; - return [NSString stringWithFormat:@"%llu", scalarValue]; - } - case 'i': { - int scalarValue = 0; - [self getValue:(void *)&scalarValue]; - return [NSString stringWithFormat:@"%i", scalarValue]; - } - case 'I': { - unsigned int long scalarValue = 0; - [self getValue:(void *)&scalarValue]; - return [NSString stringWithFormat:@"%u", scalarValue]; - } - case 'f': { - float scalarValue = 0.0f; - [self getValue:(void *)&scalarValue]; - return [NSString stringWithFormat:@"%f", scalarValue]; - } - case 'd': { - double scalarValue = 0.0; - [self getValue:(void *)&scalarValue]; - return [NSString stringWithFormat:@"%.12g", scalarValue]; - } - default: { - return [self description]; - } - } - } - else if (strncmp (objCType, "^", 1) == 0) { - return [NSString stringWithFormat:@"%p", [self pointerValue]]; - } - //else if (strcmp (objCType, "{_NSPoint=ff}") == 0) { -// return [NSString stringWithFormat:@"%@", NSStringFromPoint ([self pointValue])]; -// } -// else if (strcmp (objCType, "{_NSSize=ff}") == 0) { -// return [NSString stringWithFormat:@"%@", NSStringFromSize ([self sizeValue])]; -// } -// else if (strcmp (objCType, "{_NSRange=II}") == 0) { -// return [NSString stringWithFormat:@"%@", NSStringFromRange ([self rangeValue])]; -// } -// else if (strcmp (objCType, "{_NSRect={_NSPoint=ff}{_NSSize=ff}}") == 0) { -// return [NSString stringWithFormat:@"%@", NSStringFromRect ([self rectValue])]; -// } - } - return [self description]; -} -@end - -// SENTE_END - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/GHTestCase.h b/socketio/Classes-iOS/GHUNIT/GHTestCase.h deleted file mode 100644 index 045d9bb..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTestCase.h +++ /dev/null @@ -1,152 +0,0 @@ -// -// GHTestCase.h -// GHUnit -// -// Created by Gabriel Handford on 1/21/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -// -// Portions of this file fall under the following license, marked with: -// GTM_BEGIN : GTM_END -// -// Copyright 2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -#import "GHTestMacros.h" -#import "GHTest.h" - -/*! - Log to your test case logger. For example, - - GHTestLog(@"Some debug info, %@", obj); - - */ -#define GHTestLog(...) [self log:[NSString stringWithFormat:__VA_ARGS__, nil]] - -/*! - The base class for a test case. - - @interface MyTest : GHTestCase {} - @end - - @implementation MyTest - - // Run before each test method - - (void)setUp { } - - // Run after each test method - - (void)tearDown { } - - // Run before the tests are run for this class - - (void)setUpClass { } - - // Run before the tests are run for this class - - (void)tearDownClass { } - - // Tests are prefixed by 'test' and contain no arguments and no return value - - (void)testA { - GHTestLog(@"Log with a test with the GHTestLog(...) for test specific logging."); - } - - // Another test; Tests are run in lexical order - - (void)testB { } - - // Override any exceptions; By default exceptions are raised, causing a test failure - - (void)failWithException:(NSException *)exception { } - - @end - - */ -@interface GHTestCase : NSObject { - id logWriter_; // weak - - SEL currentSelector_; -} - -//! The current test selector -@property (assign, nonatomic) SEL currentSelector; -@property (assign, nonatomic) id logWriter; - -// GTM_BEGIN -//! Run before each test method -- (void)setUp; - -//! Run after each test method -- (void)tearDown; - -/*! - By default exceptions are raised, causing a test failure - - @param exception Exception that was raised by test - */ -- (void)failWithException:(NSException*)exception; -// GTM_END - -/*! - Run before the tests (once per test case). - */ -- (void)setUpClass; - -/*! - Run after the tests (once per test case). - */ -- (void)tearDownClass; - -/*! - Whether to run the tests on a separate thread. Override this method in your - test case to override the default. - Default is NO, tests are run on a separate thread by default. - - @result If YES, the test will run on the main thread - */ -- (BOOL)shouldRunOnMainThread; - -/*! - Any special handling of exceptions after they are thrown; By default logs stack trace to standard out. - @param exception Exception - */ -- (void)handleException:(NSException *)exception; - -/*! - Log a message, which notifies the log delegate. - This is not meant to be used directly, see GHTestLog(...) macro. - - @param message Message to log - */ -- (void)log:(NSString *)message; - -@end diff --git a/socketio/Classes-iOS/GHUNIT/GHTestCase.m b/socketio/Classes-iOS/GHUNIT/GHTestCase.m deleted file mode 100644 index 4a26d70..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTestCase.m +++ /dev/null @@ -1,64 +0,0 @@ -// -// GHTestCase.m -// GHUnit -// -// Created by Gabriel Handford on 1/21/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import "GHTestCase.h" -#import "GTMStackTrace.h" -#import "GHTesting.h" - -@implementation GHTestCase - -@synthesize logWriter=logWriter_, currentSelector=currentSelector_; - -- (void)failWithException:(NSException *)exception { - [exception raise]; -} - -- (void)setUp { } - -- (void)tearDown { } - -- (void)setUpClass { } - -- (void)tearDownClass { } - -- (BOOL)shouldRunOnMainThread { - return NO; -} - -- (void)handleException:(NSException *)exception { - NSLog(@"%@", [GHTesting descriptionForException:exception]); -} - -#pragma mark Logging - -- (void)log:(NSString *)message { - [logWriter_ log:message testCase:self]; -} - -@end diff --git a/socketio/Classes-iOS/GHUNIT/GHTestMacros.h b/socketio/Classes-iOS/GHUNIT/GHTestMacros.h deleted file mode 100644 index e9a8197..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHTestMacros.h +++ /dev/null @@ -1,1045 +0,0 @@ -// -// GHTestMacros.h -// -// Created by Gabriel Handford on 1/17/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -// -// Portions of this file fall under the following license, marked with -// SENTE_BEGIN - SENTE_END -// -// Copyright (c) 1997-2005, Sen:te (Sente SA). All rights reserved. -// -// Use of this source code is governed by the following license: -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// (1) Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// (2) Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -// IN NO EVENT SHALL Sente SA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Note: this license is equivalent to the FreeBSD license. -// -// This notice may not be removed from this file. - -// -// Portions of this file fall under the following license, marked with: -// GTM_BEGIN : GTM_END -// -// Copyright 2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -#import - -#import "NSException+GHTestFailureExceptions.h" -#import "NSValue+GHValueFormatter.h" - -// GTM_BEGIN - -extern NSString *const GHTestFilenameKey; -extern NSString *const GHTestLineNumberKey; -extern NSString *const GHTestFailureException; - -#if defined(__cplusplus) -extern "C" -#endif - -NSString *GHComposeString(NSString *, ...); - - -/*! - Generates a failure when a1 != noErr - - @param a1 Should be either an OSErr or an OSStatus - @param description A format string as in the printf() function. Can be nil or an empty string but must be present. - @param ...: A variable number of arguments to the format string. Can be absent. - */ -#define GHAssertNoErr(a1, description, ...) \ -do { \ -@try {\ -OSStatus a1value = (a1); \ -if (a1value != noErr) { \ -NSString *_expression = [NSString stringWithFormat:@"Expected noErr, got %ld for (%s)", a1value, #a1]; \ -if (description) { \ -_expression = [NSString stringWithFormat:@"%@: %@", _expression, GHComposeString(description, ##__VA_ARGS__)]; \ -} \ -[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:_expression]]; \ -} \ -}\ -@catch (id anException) {\ -[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat:@"(%s) == noErr fails", #a1] \ -exception:anException \ -inFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -} while(0) - -/*! - Generates a failure when a1 != a2 - - @param a1 Rreceived value. Should be either an OSErr or an OSStatus - @param a2 Expected value. Should be either an OSErr or an OSStatus - @param description A format string as in the printf() function. Can be nil or an empty string but must be present. - @param ... A variable number of arguments to the format string. Can be absent. - */ -#define GHAssertErr(a1, a2, description, ...) \ -do { \ -@try {\ -OSStatus a1value = (a1); \ -OSStatus a2value = (a2); \ -if (a1value != a2value) { \ -NSString *_expression = [NSString stringWithFormat:@"Expected %s(%ld) but got %ld for (%s)", #a2, a2value, a1value, #a1]; \ -if (description) { \ -_expression = [NSString stringWithFormat:@"%@: %@", _expression, GHComposeString(description, ##__VA_ARGS__)]; \ -} \ -[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:_expression]]; \ -} \ -}\ -@catch (id anException) {\ -[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat:@"(%s) == (%s) fails", #a1, #a2] \ -exception:anException \ -inFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -} while(0) - - -/*! - Generates a failure when a1 is NULL - - @param a1 Should be a pointer (use GHAssertNotNil for an object) - @param description A format string as in the printf() function. Can be nil or an empty string but must be present. - @param ... A variable number of arguments to the format string. Can be absent. - */ -#define GHAssertNotNULL(a1, description, ...) \ -do { \ -@try {\ -const void* a1value = (a1); \ -if (a1value == NULL) { \ -NSString *_expression = [NSString stringWithFormat:@"(%s) != NULL", #a1]; \ -if (description) { \ -_expression = [NSString stringWithFormat:@"%@: %@", _expression, GHComposeString(description, ##__VA_ARGS__)]; \ -} \ -[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:_expression]]; \ -} \ -}\ -@catch (id anException) {\ -[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat:@"(%s) != NULL fails", #a1] \ -exception:anException \ -inFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -} while(0) - -/*! - Generates a failure when a1 is not NULL - - @param a1 should be a pointer (use GHAssertNil for an object) - @param description A format string as in the printf() function. Can be nil or an empty string but must be present. - @param ... A variable number of arguments to the format string. Can be absent. - */ -#define GHAssertNULL(a1, description, ...) \ -do { \ -@try {\ -const void* a1value = (a1); \ -if (a1value != NULL) { \ -NSString *_expression = [NSString stringWithFormat:@"(%s) == NULL", #a1]; \ -if (description) { \ -_expression = [NSString stringWithFormat:@"%@: %@", _expression, GHComposeString(description, ##__VA_ARGS__)]; \ -} \ -[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:_expression]]; \ -} \ -}\ -@catch (id anException) {\ -[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat:@"(%s) == NULL fails", #a1] \ -exception:anException \ -inFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -} while(0) - -/*! - Generates a failure when a1 is equal to a2. This test is for C scalars, structs and unions. - - @param a1 Argument 1 - @param a2 Argument 2 - @param description A format string as in the printf() function. Can be nil or an empty string but must be present. - @param ... A variable number of arguments to the format string. Can be absent. - */ -#define GHAssertNotEquals(a1, a2, description, ...) \ -do { \ -@try {\ -if (strcmp(@encode(__typeof__(a1)), @encode(__typeof__(a2))) != 0) { \ -[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:[@"Type mismatch -- " stringByAppendingString:GHComposeString(description, ##__VA_ARGS__)]]]; \ -} else { \ -__typeof__(a1) a1value = (a1); \ -__typeof__(a2) a2value = (a2); \ -NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \ -NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \ -if ([a1encoded isEqualToValue:a2encoded]) { \ -NSString *_expression = [NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2]; \ -if (description) { \ -_expression = [NSString stringWithFormat:@"%@: %@", _expression, GHComposeString(description, ##__VA_ARGS__)]; \ -} \ -[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:_expression]]; \ -} \ -} \ -} \ -@catch (id anException) {\ -[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2] \ -exception:anException \ -inFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -} while(0) - -/*! - Generates a failure when a1 is equal to a2. This test is for objects. - - @param a1 Argument 1. object. - @param a2 Argument 2. object. - @param description A format string as in the printf() function. Can be nil or an empty string but must be present. - @param ... A variable number of arguments to the format string. Can be absent. - */ -#define GHAssertNotEqualObjects(a1, a2, desc, ...) \ -do { \ -@try {\ -id a1value = (a1); \ -id a2value = (a2); \ -if ( (strcmp(@encode(__typeof__(a1value)), @encode(id)) == 0) && \ -(strcmp(@encode(__typeof__(a2value)), @encode(id)) == 0) && \ -![(id)a1value isEqual:(id)a2value] ) continue; \ -NSString *_expression = [NSString stringWithFormat:@"%s('%@') != %s('%@')", #a1, [a1 description], #a2, [a2 description]]; \ -if (desc) { \ -_expression = [NSString stringWithFormat:@"%@: %@", _expression, GHComposeString(desc, ##__VA_ARGS__)]; \ -} \ -[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:_expression]]; \ -}\ -@catch (id anException) {\ -[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \ -exception:anException \ -inFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:GHComposeString(desc, ##__VA_ARGS__)]]; \ -}\ -} while(0) - -/*! - Generates a failure when a1 is not 'op' to a2. This test is for C scalars. - - @param a1 Argument 1 - @param a2 Argument 2 - @param op Operation - @param description A format string as in the printf() function. Can be nil or an empty string but must be present. - @param ... A variable number of arguments to the format string. Can be absent. - */ -#define GHAssertOperation(a1, a2, op, description, ...) \ -do { \ -@try {\ -if (strcmp(@encode(__typeof__(a1)), @encode(__typeof__(a2))) != 0) { \ -[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:[@"Type mismatch -- " stringByAppendingString:GHComposeString(description, ##__VA_ARGS__)]]]; \ -} else { \ -__typeof__(a1) a1value = (a1); \ -__typeof__(a2) a2value = (a2); \ -if (!(a1value op a2value)) { \ -double a1DoubleValue = a1value; \ -double a2DoubleValue = a2value; \ -NSString *_expression = [NSString stringWithFormat:@"%s (%lg) %s %s (%lg)", #a1, a1DoubleValue, #op, #a2, a2DoubleValue]; \ -if (description) { \ -_expression = [NSString stringWithFormat:@"%@: %@", _expression, GHComposeString(description, ##__VA_ARGS__)]; \ -} \ -[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:_expression]]; \ -} \ -} \ -} \ -@catch (id anException) {\ -[self failWithException:[NSException \ -ghu_failureInRaise:[NSString stringWithFormat:@"(%s) %s (%s)", #a1, #op, #a2] \ -exception:anException \ -inFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -} while(0) - -/*! - Generates a failure when a1 is not > a2. This test is for C scalars. - - @param a1 argument 1 - @param a2 argument 2 - @param op operation - @param description A format string as in the printf() function. Can be nil or an empty string but must be present. - @param ... A variable number of arguments to the format string. Can be absent. - */ -#define GHAssertGreaterThan(a1, a2, description, ...) \ -GHAssertOperation(a1, a2, >, description, ##__VA_ARGS__) - -/*! - Generates a failure when a1 is not >= a2. This test is for C scalars. - - @param a1 argument 1 - @param a2 argument 2 - @param op operation - @param description A format string as in the printf() function. Can be nil or an empty string but must be present. - @param ... A variable number of arguments to the format string. Can be absent. - */ -#define GHAssertGreaterThanOrEqual(a1, a2, description, ...) \ -GHAssertOperation(a1, a2, >=, description, ##__VA_ARGS__) - -/*! - Generates a failure when a1 is not < a2. This test is for C scalars. - - @param a1 argument 1 - @param a2 argument 2 - @param op operation - @param description A format string as in the printf() function. Can be nil or an empty string but must be present. - @param ... A variable number of arguments to the format string. Can be absent. - */ -#define GHAssertLessThan(a1, a2, description, ...) \ -GHAssertOperation(a1, a2, <, description, ##__VA_ARGS__) - -/*! Generates a failure when a1 is not <= a2. This test is for C scalars. - - @param a1 argument 1 - @param a2 argument 2 - @param op operation - @param description A format string as in the printf() function. Can be nil or an empty string but must be present. - @param ... A variable number of arguments to the format string. Can be absent. - */ -#define GHAssertLessThanOrEqual(a1, a2, description, ...) \ -GHAssertOperation(a1, a2, <=, description, ##__VA_ARGS__) - -/*! - Generates a failure when string a1 is not equal to string a2. This call - differs from GHAssertEqualObjects in that strings that are different in - composition (precomposed vs decomposed) will compare equal if their final - representation is equal. - ex O + umlaut decomposed is the same as O + umlaut composed. - - @param a1 string 1 - @param a2 string 2 - @param description A format string as in the printf() function. Can be nil or an empty string but must be present. - @param ... A variable number of arguments to the format string. Can be absent. - */ -#define GHAssertEqualStrings(a1, a2, description, ...) \ -do { \ -@try {\ -id a1value = (a1); \ -id a2value = (a2); \ -if (a1value == a2value) continue; \ -if ([a1value isKindOfClass:[NSString class]] && \ -[a2value isKindOfClass:[NSString class]] && \ -[a1value compare:a2value options:0] == NSOrderedSame) continue; \ -[self failWithException:[NSException ghu_failureInEqualityBetweenObject: a1value \ -andObject: a2value \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -@catch (id anException) {\ -[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ -exception:anException \ -inFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -} while(0) - -/*! - Generates a failure when string a1 is equal to string a2. This call - differs from GHAssertEqualObjects in that strings that are different in - composition (precomposed vs decomposed) will compare equal if their final - representation is equal. - ex O + umlaut decomposed is the same as O + umlaut composed. - - @param a1 string 1 - @param a2 string 2 - @param description A format string as in the printf() function. Can be nil or an empty string but must be present. - @param ... A variable number of arguments to the format string. Can be absent. - */ -#define GHAssertNotEqualStrings(a1, a2, description, ...) \ -do { \ -@try {\ -id a1value = (a1); \ -id a2value = (a2); \ -if (([a1value isKindOfClass:[NSString class]] && \ -[a2value isKindOfClass:[NSString class]] && \ -[a1value compare:a2value options:0] != NSOrderedSame) || \ -(a1value == nil && [a2value isKindOfClass:[NSString class]]) || \ -(a2value == nil && [a1value isKindOfClass:[NSString class]]) \ -) continue; \ -[self failWithException:[NSException ghu_failureInInequalityBetweenObject: a1value \ -andObject: a2value \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -@catch (id anException) {\ -[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \ -exception:anException \ -inFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -} while(0) - -/*! - Generates a failure when c-string a1 is not equal to c-string a2. - - @param a1 string 1 - @param a2 string 2 - @param description A format string as in the printf() function. Can be nil or an empty string but must be present. - @param ... A variable number of arguments to the format string. Can be absent. - */ -#define GHAssertEqualCStrings(a1, a2, description, ...) \ -do { \ -@try {\ -const char* a1value = (a1); \ -const char* a2value = (a2); \ -if (a1value == a2value) continue; \ -if (strcmp(a1value, a2value) == 0) continue; \ -[self failWithException:[NSException ghu_failureInEqualityBetweenObject: [NSString stringWithUTF8String:a1value] \ -andObject: [NSString stringWithUTF8String:a2value] \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -@catch (id anException) {\ -[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ -exception:anException \ -inFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -} while(0) - -/*! - Generates a failure when c-string a1 is equal to c-string a2. - - @param a1 string 1 - @param a2 string 2 - @param description A format string as in the printf() function. Can be nil or an empty string but must be present. - @param ... A variable number of arguments to the format string. Can be absent. - */ -#define GHAssertNotEqualCStrings(a1, a2, description, ...) \ -do { \ -@try {\ -const char* a1value = (a1); \ -const char* a2value = (a2); \ -if (strcmp(a1value, a2value) != 0) continue; \ -[self failWithException:[NSException ghu_failureInEqualityBetweenObject: [NSString stringWithUTF8String:a1value] \ -andObject: [NSString stringWithUTF8String:a2value] \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -@catch (id anException) {\ -[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \ -exception:anException \ -inFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -} while(0) - -// GTM_END - -// SENTE_BEGIN -/*! Generates a failure when !{ [a1 isEqualTo:a2] } is false - (or one is nil and the other is not). - - @param a1 The object on the left - @param a2 The object on the right - @param description A format string as in the printf() function. Can be nil or an empty string but must be present - @param ... A variable number of arguments to the format string. Can be absent - */ -#define GHAssertEqualObjects(a1, a2, description, ...) \ -do { \ -@try {\ -id a1value = (a1); \ -id a2value = (a2); \ -if (a1value == a2value) continue; \ -if ( (strcmp(@encode(__typeof__(a1value)), @encode(id)) == 0) && \ -(strcmp(@encode(__typeof__(a2value)), @encode(id)) == 0) && \ -[(id)a1value isEqual: (id)a2value] ) continue; \ -[self failWithException:[NSException ghu_failureInEqualityBetweenObject: a1value \ -andObject: a2value \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -@catch (id anException) {\ -[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ -exception:anException \ -inFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -} while(0) - - -/*! Generates a failure when a1 is not equal to a2. This test is for - C scalars, structs and unions. - - @param a1 The argument on the left - @param a2 The argument on the right - @param description A format string as in the printf() function. Can be nil or an empty string but must be present - @param ... A variable number of arguments to the format string. Can be absent - */ -#define GHAssertEquals(a1, a2, description, ...) \ -do { \ -@try {\ -if ( strcmp(@encode(__typeof__(a1)), @encode(__typeof__(a2))) != 0 ) { \ -[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:[@"Type mismatch -- " stringByAppendingString:GHComposeString(description, ##__VA_ARGS__)]]]; \ -} else { \ -__typeof__(a1) a1value = (a1); \ -__typeof__(a2) a2value = (a2); \ -NSValue *a1encoded = [NSValue value:&a1value withObjCType: @encode(__typeof__(a1))]; \ -NSValue *a2encoded = [NSValue value:&a2value withObjCType: @encode(__typeof__(a2))]; \ -if (![a1encoded isEqualToValue:a2encoded]) { \ -[self failWithException:[NSException ghu_failureInEqualityBetweenValue: a1encoded \ -andValue: a2encoded \ -withAccuracy: nil \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(description, ##__VA_ARGS__)]]; \ -} \ -} \ -} \ -@catch (id anException) {\ -[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ -exception:anException \ -inFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -} while(0) - -//! Absolute difference -#define GHAbsoluteDifference(left,right) (MAX(left,right)-MIN(left,right)) - - -/*! - Generates a failure when a1 is not equal to a2 within + or - accuracy is false. - This test is for scalars such as floats and doubles where small differences - could make these items not exactly equal, but also works for all scalars. - - @param a1 The scalar on the left - @param a2 The scalar on the right - @param accuracy The maximum difference between a1 and a2 for these values to be considered equal - @param description A format string as in the printf() function. Can be nil or an empty string but must be present - @param ... A variable number of arguments to the format string. Can be absent - */ -#define GHAssertEqualsWithAccuracy(a1, a2, accuracy, description, ...) \ -do { \ -@try {\ -if (strcmp(@encode(__typeof__(a1)), @encode(__typeof__(a2))) != 0) { \ -[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:[@"Type mismatch -- " stringByAppendingString:GHComposeString(description, ##__VA_ARGS__)]]]; \ -} else { \ -__typeof__(a1) a1value = (a1); \ -__typeof__(a2) a2value = (a2); \ -__typeof__(accuracy) accuracyvalue = (accuracy); \ -if (GHAbsoluteDifference(a1value, a2value) > accuracyvalue) { \ -NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \ -NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \ -NSValue *accuracyencoded = [NSValue value:&accuracyvalue withObjCType:@encode(__typeof__(accuracy))]; \ -[self failWithException:[NSException ghu_failureInEqualityBetweenValue: a1encoded \ -andValue: a2encoded \ -withAccuracy: accuracyencoded \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(description, ##__VA_ARGS__)]]; \ -} \ -} \ -} \ -@catch (id anException) {\ -[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ -exception:anException \ -inFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -} while(0) - - - -/*! - Generates a failure unconditionally. - - @param description A format string as in the printf() function. Can be nil or an empty string but must be present - @param ... A variable number of arguments to the format string. Can be absent - */ -#define GHFail(description, ...) \ -[self failWithException:[NSException ghu_failureInFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(description, ##__VA_ARGS__)]] - - - -/*! - Generates a failure when a1 is not nil. - - @param a1 An object - @param description A format string as in the printf() function. Can be nil or an empty string but must be present - @param ... A variable number of arguments to the format string. Can be absent - */ -#define GHAssertNil(a1, description, ...) \ -do { \ -@try {\ -id a1value = (a1); \ -if (a1value != nil) { \ -NSString *_a1 = [NSString stringWithUTF8String: #a1]; \ -NSString *_expression = [NSString stringWithFormat:@"((%@) == nil)", _a1]; \ -[self failWithException:[NSException ghu_failureInCondition: _expression \ -isTrue: NO \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(description, ##__VA_ARGS__)]]; \ -} \ -}\ -@catch (id anException) {\ -[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat: @"(%s) == nil fails", #a1] \ -exception:anException \ -inFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -} while(0) - - -/*! - Generates a failure when a1 is nil. - - @param a1 An object - @param description A format string as in the printf() function. Can be nil or an empty string but must be present - @param ... A variable number of arguments to the format string. Can be absent - */ -#define GHAssertNotNil(a1, description, ...) \ -do { \ -@try {\ -id a1value = (a1); \ -if (a1value == nil) { \ -NSString *_a1 = [NSString stringWithUTF8String: #a1]; \ -NSString *_expression = [NSString stringWithFormat:@"((%@) != nil)", _a1]; \ -[self failWithException:[NSException ghu_failureInCondition: _expression \ -isTrue: NO \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(description, ##__VA_ARGS__)]]; \ -} \ -}\ -@catch (id anException) {\ -[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat: @"(%s) != nil fails", #a1] \ -exception:anException \ -inFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -} while(0) - - -/*! - Generates a failure when expression evaluates to false. - - @param expr The expression that is tested - @param description A format string as in the printf() function. Can be nil or an empty string but must be present - @param ... A variable number of arguments to the format string. Can be absent - */ -#define GHAssertTrue(expr, description, ...) \ -do { \ -BOOL _evaluatedExpression = (expr);\ -if (!_evaluatedExpression) {\ -NSString *_expression = [NSString stringWithUTF8String: #expr];\ -[self failWithException:[NSException ghu_failureInCondition: _expression \ -isTrue: YES \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(description, ##__VA_ARGS__)]]; \ -} \ -} while (0) - - -/*! - Generates a failure when expression evaluates to false and in addition will - generate error messages if an exception is encountered. - - @param expr The expression that is tested - @param description A format string as in the printf() function. Can be nil or an empty string but must be present - @param ... A variable number of arguments to the format string. Can be absent - */ -#define GHAssertTrueNoThrow(expr, description, ...) \ -do { \ -@try {\ -BOOL _evaluatedExpression = (expr);\ -if (!_evaluatedExpression) {\ -NSString *_expression = [NSString stringWithUTF8String: #expr];\ -[self failWithException:[NSException ghu_failureInCondition: _expression \ -isTrue: NO \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(description, ##__VA_ARGS__)]]; \ -} \ -} \ -@catch (id anException) {\ -[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat: @"(%s) ", #expr] \ -exception:anException \ -inFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -} while (0) - - -/*! - Generates a failure when the expression evaluates to true. - - @param expr The expression that is tested - @param description A format string as in the printf() function. Can be nil or an empty string but must be present - @param ... A variable number of arguments to the format string. Can be absent - */ -#define GHAssertFalse(expr, description, ...) \ -do { \ -BOOL _evaluatedExpression = (expr);\ -if (_evaluatedExpression) {\ -NSString *_expression = [NSString stringWithUTF8String: #expr];\ -[self failWithException:[NSException ghu_failureInCondition: _expression \ -isTrue: NO \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(description, ##__VA_ARGS__)]]; \ -} \ -} while (0) - - -/*! - Generates a failure when the expression evaluates to true and in addition - will generate error messages if an exception is encountered. - - @param expr The expression that is tested - @param description A format string as in the printf() function. Can be nil or an empty string but must be present - @param ... A variable number of arguments to the format string. Can be absent - */ -#define GHAssertFalseNoThrow(expr, description, ...) \ -do { \ -@try {\ -BOOL _evaluatedExpression = (expr);\ -if (_evaluatedExpression) {\ -NSString *_expression = [NSString stringWithUTF8String: #expr];\ -[self failWithException:[NSException ghu_failureInCondition: _expression \ -isTrue: YES \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(description, ##__VA_ARGS__)]]; \ -} \ -} \ -@catch (id anException) {\ -[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat: @"!(%s) ", #expr] \ -exception:anException \ -inFile:[NSString stringWithUTF8String:__FILE__] \ -atLine:__LINE__ \ -withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -} while (0) - - -/*! - Generates a failure when expression does not throw an exception. - - @param expression The expression that is evaluated - @param description A format string as in the printf() function. Can be nil or an empty string but must be present - @param ... A variable number of arguments to the format string. Can be absent. - */ -#define GHAssertThrows(expr, description, ...) \ -do { \ -@try { \ -(expr);\ -} \ -@catch (id anException) { \ -continue; \ -}\ -[self failWithException:[NSException ghu_failureInRaise: [NSString stringWithUTF8String:#expr] \ -exception: nil \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(description, ##__VA_ARGS__)]]; \ -} while (0) - - -/*! - Generates a failure when expression does not throw an exception of a - specific class. - - @param expression The expression that is evaluated - @param specificException The specified class of the exception - @param description A format string as in the printf() function. Can be nil or an empty string but must be present - @param ... A variable number of arguments to the format string. Can be absent - */ -#define GHAssertThrowsSpecific(expr, specificException, description, ...) \ -do { \ -@try { \ -(expr);\ -} \ -@catch (specificException *anException) { \ -continue; \ -}\ -@catch (id anException) {\ -NSString *_descrip = GHComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\ -[self failWithException:[NSException ghu_failureInRaise: [NSString stringWithUTF8String:#expr] \ -exception: anException \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(_descrip, ##__VA_ARGS__)]]; \ -continue; \ -}\ -NSString *_descrip = GHComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\ -[self failWithException:[NSException ghu_failureInRaise: [NSString stringWithUTF8String:#expr] \ -exception: nil \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(_descrip, ##__VA_ARGS__)]]; \ -} while (0) - - -/*! Generates a failure when expression does not throw an exception of a - specific class with a specific name. Useful for those frameworks like - AppKit or Foundation that throw generic NSException w/specific names - (NSInvalidArgumentException, etc). - - @param expression The expression that is evaluated - @param specificException The specified class of the exception - @param aName The name of the specified exception - @param description A format string as in the printf() function. Can be nil or an empty string but must be present - @param ... A variable number of arguments to the format string. Can be absent - */ -#define GHAssertThrowsSpecificNamed(expr, specificException, aName, description, ...) \ -do { \ -@try { \ -(expr);\ -} \ -@catch (specificException *anException) { \ -if ([aName isEqualToString: [anException name]]) continue; \ -NSString *_descrip = GHComposeString(@"(Expected exception: %@ (name: %@)) %@", NSStringFromClass([specificException class]), aName, description);\ -[self failWithException: \ -[NSException ghu_failureInRaise: [NSString stringWithUTF8String:#expr] \ -exception: anException \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(_descrip, ##__VA_ARGS__)]]; \ -continue; \ -}\ -@catch (id anException) {\ -NSString *_descrip = GHComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\ -[self failWithException: \ -[NSException ghu_failureInRaise: [NSString stringWithUTF8String:#expr] \ -exception: anException \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(_descrip, ##__VA_ARGS__)]]; \ -continue; \ -}\ -NSString *_descrip = GHComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\ -[self failWithException: \ -[NSException ghu_failureInRaise: [NSString stringWithUTF8String:#expr] \ -exception: nil \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(_descrip, ##__VA_ARGS__)]]; \ -} while (0) - - -/*! - Generates a failure when expression does throw an exception. - - @param expression The expression that is evaluated - @param description A format string as in the printf() function. Can be nil or an empty string but must be present - @param ... A variable number of arguments to the format string. Can be absent - */ -#define GHAssertNoThrow(expr, description, ...) \ -do { \ -@try { \ -(expr);\ -} \ -@catch (id anException) { \ -[self failWithException:[NSException ghu_failureInRaise: [NSString stringWithUTF8String:#expr] \ -exception: anException \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -} while (0) - - -/*! - Generates a failure when expression does throw an exception of the specitied - class. Any other exception is okay (i.e. does not generate a failure). - - @param expression The expression that is evaluated - @param specificException The specified class of the exception - @param description A format string as in the printf() function. Can be nil or an empty string but must be present - @param ... A variable number of arguments to the format string. Can be absent - */ -#define GHAssertNoThrowSpecific(expr, specificException, description, ...) \ -do { \ -@try { \ -(expr);\ -} \ -@catch (specificException *anException) { \ -[self failWithException:[NSException ghu_failureInRaise: [NSString stringWithUTF8String:#expr] \ -exception: anException \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(description, ##__VA_ARGS__)]]; \ -}\ -@catch (id anythingElse) {\ -; \ -}\ -} while (0) - - -/*! - Generates a failure when expression does throw an exception of a - specific class with a specific name. Useful for those frameworks like - AppKit or Foundation that throw generic NSException w/specific names - (NSInvalidArgumentException, etc). - - @param expression The expression that is evaluated. - @param specificException The specified class of the exception - @param aName The name of the specified exception - @param description A format string as in the printf() function. Can be nil or an empty string but must be present - @param ... A variable number of arguments to the format string. Can be absent - */ -#define GHAssertNoThrowSpecificNamed(expr, specificException, aName, description, ...) \ -do { \ -@try { \ -(expr);\ -} \ -@catch (specificException *anException) { \ -if ([aName isEqualToString: [anException name]]) { \ -NSString *_descrip = GHComposeString(@"(Expected exception: %@ (name: %@)) %@", NSStringFromClass([specificException class]), aName, description);\ -[self failWithException: \ -[NSException ghu_failureInRaise: [NSString stringWithUTF8String:#expr] \ -exception: anException \ -inFile: [NSString stringWithUTF8String:__FILE__] \ -atLine: __LINE__ \ -withDescription: GHComposeString(_descrip, ##__VA_ARGS__)]]; \ -} \ -continue; \ -}\ -@catch (id anythingElse) {\ -; \ -}\ -} while (0) - - -@interface NSException(GHTestMacros_GTMSenTestAdditions) -+ (NSException *)ghu_failureInFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ...; -+ (NSException *)ghu_failureInCondition:(NSString *)condition - isTrue:(BOOL)isTrue - inFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ...; -+ (NSException *)ghu_failureInEqualityBetweenObject:(id)left - andObject:(id)right - inFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ...; -+ (NSException *)ghu_failureInInequalityBetweenObject:(id)left - andObject:(id)right - inFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ...; -+ (NSException *)ghu_failureInEqualityBetweenValue:(NSValue *)left - andValue:(NSValue *)right - withAccuracy:(NSValue *)accuracy - inFile:(NSString *)filename - atLine:(int) ineNumber - withDescription:(NSString *)formatString, ...; -+ (NSException *)ghu_failureInRaise:(NSString *)expression - inFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ...; -+ (NSException *)ghu_failureInRaise:(NSString *)expression - exception:(NSException *)exception - inFile:(NSString *)filename - atLine:(int)lineNumber - withDescription:(NSString *)formatString, ...; -@end - -// SENTE_END diff --git a/socketio/Classes-iOS/GHUNIT/GHUnit.h b/socketio/Classes-iOS/GHUNIT/GHUnit.h deleted file mode 100644 index 80b458b..0000000 --- a/socketio/Classes-iOS/GHUNIT/GHUnit.h +++ /dev/null @@ -1,55 +0,0 @@ -// -// GHUnit.h -// GHUnit -// -// Created by Gabriel Handford on 1/19/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import "GHTestCase.h" -#import "GHAsyncTestCase.h" -#import "GHTestSuite.h" -#import "GHTestMacros.h" -#import "GHTestRunner.h" - -#import "GHTest.h" -#import "GHTesting.h" -#import "GHTestOperation.h" -#import "GHTestGroup.h" -#import "GHTest+JUnitXML.h" -#import "GHTestGroup+JUnitXML.h" -#import "NSException+GHTestFailureExceptions.h" -#import "NSValue+GHValueFormatter.h" - -#if TARGET_OS_IPHONE -#import "GHUnitIOSAppDelegate.h" -#endif - -#ifdef DEBUG -#define GHUDebug(fmt, ...) do { \ -fputs([[[NSString stringWithFormat:fmt, ##__VA_ARGS__] stringByAppendingString:@"\n"] UTF8String], stdout); \ -} while(0) -#else -#define GHUDebug(fmt, ...) do {} while(0) -#endif diff --git a/socketio/Classes-iOS/GHUNIT/Mock/GHMockNSHTTPURLResponse.h b/socketio/Classes-iOS/GHUNIT/Mock/GHMockNSHTTPURLResponse.h deleted file mode 100644 index 837c615..0000000 --- a/socketio/Classes-iOS/GHUNIT/Mock/GHMockNSHTTPURLResponse.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// GHMockNSHTTPURLResponse.h -// GHUnit -// -// Created by Gabriel Handford on 4/9/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import - -/* - NSHTTPURLResponse subclass for use with mocking. - Allows us to manually set the status code and headers in the response. - */ -@interface GHMockNSHTTPURLResponse : NSHTTPURLResponse { - NSInteger statusCode_; - NSDictionary *headers_; -} - -- (id)initWithStatusCode:(NSInteger)statusCode headers:(NSDictionary *)headers; - -- (void)setStatusCode:(NSInteger)code; -- (void)setHeaders:(NSDictionary *)headers; - -@end diff --git a/socketio/Classes-iOS/GHUNIT/Mock/GHMockNSHTTPURLResponse.m b/socketio/Classes-iOS/GHUNIT/Mock/GHMockNSHTTPURLResponse.m deleted file mode 100644 index 8486916..0000000 --- a/socketio/Classes-iOS/GHUNIT/Mock/GHMockNSHTTPURLResponse.m +++ /dev/null @@ -1,69 +0,0 @@ -// -// GHMockNSHTTPURLResponse.m -// GHUnit -// -// Created by Gabriel Handford on 4/9/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -#import "GHMockNSHTTPURLResponse.h" - -@implementation GHMockNSHTTPURLResponse - -- (id)initWithStatusCode:(NSInteger)statusCode headers:(NSDictionary *)headers { - if ((self = [super init])) { - [self setStatusCode:statusCode]; - [self setHeaders:headers]; - } - return self; -} - -- (void)dealloc { - [headers_ release]; - [super dealloc]; -} - -- (void)setStatusCode:(NSInteger)code { - statusCode_ = code; -} - -- (NSInteger)statusCode { - return statusCode_ ? statusCode_ : [super statusCode]; -} - -- (void)setHeaders:(NSDictionary *)headers { - [headers retain]; - [headers_ release]; - headers_ = headers; -} - -- (NSDictionary *)allHeaderFields { - return headers_ ? [[headers_ copy] autorelease] : [super allHeaderFields]; -} - -@end - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/Mock/GHMockNSURLConnection.h b/socketio/Classes-iOS/GHUNIT/Mock/GHMockNSURLConnection.h deleted file mode 100644 index 5f1e580..0000000 --- a/socketio/Classes-iOS/GHUNIT/Mock/GHMockNSURLConnection.h +++ /dev/null @@ -1,167 +0,0 @@ -// -// GHMockNSURLConnection.h -// GHUnit -// -// Created by Gabriel Handford on 4/9/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import - -extern NSString *const GHMockNSURLConnectionException; - -/*! - NSURLConnection for mocking. - - Use with GHAsyncTestCase to mock out connections. - - @interface GHNSURLConnectionMockTest : GHAsyncTestCase {} - @end - - @implementation GHNSURLConnectionMockTest - - - (void)testMock { - [self prepare]; - GHMockNSURLConnection *connection = [[GHMockNSURLConnection alloc] initWithRequest:nil delegate:self]; - [connection receiveHTTPResponseWithStatusCode:204 headers:testHeaders_ afterDelay:0.1]; - [connection receiveData:testData_ afterDelay:0.2]; - [connection finishAfterDelay:0.3]; - [self waitForStatus:kGHUnitWaitStatusSuccess timeout:1.0]; - } - - - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { - GHAssertEquals([(NSHTTPURLResponse *)response statusCode], 204, nil); - GHAssertEqualObjects([(NSHTTPURLResponse *)response allHeaderFields], testHeaders_, nil); - } - - - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { - GHAssertEqualObjects(data, testData_, nil); - } - - - (void)connectionDidFinishLoading:(NSURLConnection *)connection { - [self notify:kGHUnitWaitStatusSuccess forSelector:@selector(testMock)]; - } - @end - - */ -@interface GHMockNSURLConnection : NSObject { - NSURLRequest *request_; - id delegate_; // weak - - BOOL cancelled_; - BOOL started_; -} - -@property (readonly, nonatomic, getter=isStarted) BOOL started; -@property (readonly, nonatomic, getter=isCancelled) BOOL cancelled; - -// Mocked version of NSURLConnection#initWithRequest:delegate: -- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate; - -// Mocked version of NSURLConnection#initWithRequest:delegate:startImmediately: -- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately; - -// Mocked version of NSURLConnection#scheduleInRunLoop:forMode: (NOOP) -- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; - -// Mocked version of NSURLConnection#start (NOOP) -- (void)start; - -/*! - Send generic response to delegate after delay. - (For asynchronous requests) - @param response Response - @param afterDelay Delay in seconds (if < 0, there is no delay) - */ -- (void)receiveResponse:(NSURLResponse *)response afterDelay:(NSTimeInterval)afterDelay; - -/*! - Send HTTP response to delegate with status code, headers, after delay. - This is only the HTTP response (and not data or finished). - (For asynchronous requests) - @param statusCode HTTP status code - @param headers Headers - @param afterDelay Delay in seconds (if < 0, there is no delay) - */ -- (void)receiveHTTPResponseWithStatusCode:(int)statusCode headers:(NSDictionary *)headers afterDelay:(NSTimeInterval)afterDelay; - -/*! - Send data to connection delegate after delay. - @param data Data to send - @param afterDelay Delay in seconds - */ -- (void)receiveData:(NSData *)data afterDelay:(NSTimeInterval)afterDelay; - -/*! - Send data to connection delegate. - @param data Data to send - @param statusCode HTTP status code - @param MIMEType Mime type - @param afterDelay Delay - */ -- (void)receiveData:(NSData *)data statusCode:(NSInteger)statusCode MIMEType:(NSString *)MIMEType afterDelay:(NSTimeInterval)afterDelay; - -/*! - Send data (from file in bundle resource) to connection delegate after delay. - (For asynchronous requests) - @param path Path to file - @param afterDelay Delay in seconds - */ -- (void)receiveDataFromPath:(NSString *)path afterDelay:(NSTimeInterval)afterDelay; - -/*! - Calls connectionDidFinish: delegate after delay. - (For asynchronous requests) - @param delay Delay in seconds (if < 0, there is no delay) - */ -- (void)finishAfterDelay:(NSTimeInterval)delay; - -/*! - Sends mock response, sends data, and then calls finish. - (For asynchronous requests) - @param path Path to load data from. File should be available in Test target (bundle) - @param statusCode Status code for response - @param MIMEType Content type for response header - @param afterDelay Delay before responding (if < 0, there is no delay) - */ -- (void)receiveFromPath:(NSString *)path statusCode:(NSInteger)statusCode MIMEType:(NSString *)MIMEType afterDelay:(NSTimeInterval)afterDelay; - -/*! - Sends mock response, sends data, and then calls finish. - (For asynchronous requests) - @param data Data to load. File should be available in Test target (bundle) - @param statusCode Status code for response - @param MIMEType Content type for response header - @param afterDelay Delay before responding (if < 0, there is no delay) - */ -- (void)receiveData:(NSData *)data statusCode:(NSInteger)statusCode MIMEType:(NSString *)MIMEType afterDelay:(NSTimeInterval)afterDelay; - -/*! - Calls connection:didFailWithError: on delegate after specified delay. - @param error The error to pass to the delegate. - @param afterDelay Delay before responding (if < 0, there is no delay) - */ -- (void)failWithError:(NSError *)error afterDelay:(NSTimeInterval)afterDelay; - -@end diff --git a/socketio/Classes-iOS/GHUNIT/Mock/GHMockNSURLConnection.m b/socketio/Classes-iOS/GHUNIT/Mock/GHMockNSURLConnection.m deleted file mode 100644 index 46c180b..0000000 --- a/socketio/Classes-iOS/GHUNIT/Mock/GHMockNSURLConnection.m +++ /dev/null @@ -1,129 +0,0 @@ -// -// GHMockNSURLConnection.m -// GHUnit -// -// Created by Gabriel Handford on 4/9/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -#import "GHMockNSURLConnection.h" -#import "GHMockNSHTTPURLResponse.h" -#import "GHNSObject+Invocation.h" - -NSString *const GHMockNSURLConnectionException = @"GHMockNSURLConnectionException"; - -@implementation GHMockNSURLConnection - -@synthesize started=started_, cancelled=cancelled_; - -- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate { - if ((self = [super init])) { - request_ = [request retain]; - delegate_ = delegate; - } - return self; -} - -- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately { - return [self initWithRequest:request delegate:delegate]; -} - -- (void)dealloc { - [request_ release]; - delegate_ = nil; - [super dealloc]; -} - -- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode { } - -- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode { } - -#pragma mark - - -- (void)start { - started_ = YES; -} - -- (void)cancel { - cancelled_ = YES; -} - -#pragma mark - - -- (void)receiveData:(NSData *)data afterDelay:(NSTimeInterval)delay { - [delegate_ ghu_performSelector:@selector(connection:didReceiveData:) afterDelay:delay withObjects:[NSNull null], data, nil]; -} - -- (NSData *)loadDataFromPath:(NSString *)path { - NSString *resourcePath = [[NSBundle mainBundle] pathForResource:[path stringByDeletingPathExtension] ofType:[path pathExtension]]; - - NSError *error = nil; - NSData *data = [NSData dataWithContentsOfFile:resourcePath options:0 error:&error]; - if (error) - [NSException raise:GHMockNSURLConnectionException format:@"%@", error]; - return data; -} - -- (void)receiveDataFromPath:(NSString *)path afterDelay:(NSTimeInterval)delay { - NSData *data = [self loadDataFromPath:path]; - [self receiveData:data afterDelay:delay]; -} - -- (void)receiveFromPath:(NSString *)path statusCode:(NSInteger)statusCode MIMEType:(NSString *)MIMEType afterDelay:(NSTimeInterval)delay { - NSData *data = [self loadDataFromPath:path]; - [self receiveData:data statusCode:statusCode MIMEType:MIMEType afterDelay:delay]; -} - -- (void)receiveData:(NSData *)data statusCode:(NSInteger)statusCode MIMEType:(NSString *)MIMEType afterDelay:(NSTimeInterval)delay { - GHMockNSHTTPURLResponse *response = [[[GHMockNSHTTPURLResponse alloc] initWithURL:[request_ URL] - MIMEType:MIMEType - expectedContentLength:[data length] - textEncodingName:nil] autorelease]; - [response setStatusCode:statusCode]; - [self receiveResponse:response afterDelay:delay]; - [self receiveData:data afterDelay:delay]; - [self finishAfterDelay:delay]; -} - -- (void)receiveHTTPResponseWithStatusCode:(int)statusCode headers:(NSDictionary *)headers afterDelay:(NSTimeInterval)delay { - [self receiveResponse:[[[GHMockNSHTTPURLResponse alloc] initWithStatusCode:statusCode headers:headers] autorelease] afterDelay:delay]; -} - -- (void)receiveResponse:(NSURLResponse *)response afterDelay:(NSTimeInterval)delay { - [delegate_ ghu_performSelector:@selector(connection:didReceiveResponse:) afterDelay:delay withObjects:self, response, nil]; -} - -- (void)finishAfterDelay:(NSTimeInterval)delay { - [delegate_ ghu_performSelector:@selector(connectionDidFinishLoading:) afterDelay:delay withObjects:self, nil]; -} - -- (void)failWithError:(NSError *)error afterDelay:(NSTimeInterval)delay { - [delegate_ ghu_performSelector:@selector(connection:didFailWithError:) afterDelay:delay withObjects:self, error, nil]; -} - -@end - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/Mock/GHNSLocale+Mock.h b/socketio/Classes-iOS/GHUNIT/Mock/GHNSLocale+Mock.h deleted file mode 100644 index 71f5e89..0000000 --- a/socketio/Classes-iOS/GHUNIT/Mock/GHNSLocale+Mock.h +++ /dev/null @@ -1,68 +0,0 @@ -// -// GHNSLocale+Mock.h -// GHUnit -// -// Created by Gabriel Handford on 4/13/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import - -/*! - Category for overriding the current locale at runtime. - - #import "GHNSLocale+Mock.h" - // This aliases the currentLocale method and with the specified locale identifier - [NSLocale gh_setLocaleIdentifier:@"en_GB"]; - - [[NSLocale currentLocale] localeIdentifier] == "en_GB" - - */ -@interface NSLocale(GHMock) - -/*! - Set locale. - @param localeIdentifier Locale identifier, e.g. "en_US" - */ -+ (void)gh_setLocaleIdentifier:(NSString *)localeIdentifier; - -/*! - Aliases to currentLocale with locale set from gh_setLocaleIdentifier. - If not set, defaults to NSLocale with identifier en_US. - */ -+ (NSLocale *)gh_currentLocale; - -/*! - Set preferred languages. To reset, set to nil. - @param preferredLanguages Preferred languages to set - */ -+ (void)gh_setPreferredLanguages:(NSArray *)preferredLanguages; - -/*! - Aliases to preferredLanguages set from gh_setPreferredLanguages. - If not set, defaults to [@"en"]. - */ -+ (NSArray *)gh_preferredLanguages; - -@end diff --git a/socketio/Classes-iOS/GHUNIT/Mock/GHNSLocale+Mock.m b/socketio/Classes-iOS/GHUNIT/Mock/GHNSLocale+Mock.m deleted file mode 100644 index 227b731..0000000 --- a/socketio/Classes-iOS/GHUNIT/Mock/GHNSLocale+Mock.m +++ /dev/null @@ -1,85 +0,0 @@ -// -// GHNSLocale+Mock.h -// GHUnit -// -// Created by Gabriel Handford on 4/13/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -#import "GHNSLocale+Mock.h" - -#import "GHUNSObject+Swizzle.h" - -// Allows us to override the current locale for testing -@implementation NSLocale(GHMock) - -static NSString *gGHUNSLocaleLocaleIdentifier = NULL; -static NSArray *gGHUNSLocalePreferredLanguages = NULL; -static BOOL gGHUNSLocaleMockSetup = NO; - -+ (void)_gh_setUpMock { - @synchronized([NSLocale class]) { - if (!gGHUNSLocaleMockSetup) { - // TODO(gabe): Check and handle swizzle errors - [NSLocale ghu_swizzleClassMethod:@selector(currentLocale) withClassMethod:@selector(gh_currentLocale)]; - [NSLocale ghu_swizzleClassMethod:@selector(preferredLanguages) withClassMethod:@selector(gh_preferredLanguages)]; - gGHUNSLocaleMockSetup = YES; - } - } -} - -+ (void)gh_setLocaleIdentifier:(NSString *)localeIdentifier { - [self _gh_setUpMock]; - [gGHUNSLocaleLocaleIdentifier release]; - gGHUNSLocaleLocaleIdentifier = [localeIdentifier copy]; -} - -+ (void)gh_setPreferredLanguages:(NSArray *)preferredLanguages { - [self _gh_setUpMock]; - [preferredLanguages retain]; - [gGHUNSLocalePreferredLanguages release]; - gGHUNSLocalePreferredLanguages = preferredLanguages; -} - -+ (NSLocale *)gh_currentLocale { - if (gGHUNSLocaleLocaleIdentifier != NULL) { - return [[[NSLocale alloc] initWithLocaleIdentifier:gGHUNSLocaleLocaleIdentifier] autorelease]; - } else { - return [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease]; - } -} - -+ (NSArray *)gh_preferredLanguages { - if (gGHUNSLocalePreferredLanguages != NULL) { - return gGHUNSLocalePreferredLanguages; - } else { - return [NSArray arrayWithObject:@"en"]; - } -} - -@end - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/Mock/GHUNSObject+Swizzle.h b/socketio/Classes-iOS/GHUNIT/Mock/GHUNSObject+Swizzle.h deleted file mode 100644 index 5ce9597..0000000 --- a/socketio/Classes-iOS/GHUNIT/Mock/GHUNSObject+Swizzle.h +++ /dev/null @@ -1,51 +0,0 @@ -// -// GHNSObject+Swizzle.h -// GHUnit -// -// Created by Gabriel Handford on 4/13/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import - -/*! - Method swizzling. - */ -@interface NSObject(GHUSwizzle) - -/*! - Swizzle instance method. - @param original Original method - @param withMethod New method - */ -+ (void)ghu_swizzleMethod:(SEL)original withMethod:(SEL)withMethod; - -/*! - Swizzle class method. - @param original Original method - @param withClassMethod New method - */ -+ (void)ghu_swizzleClassMethod:(SEL)original withClassMethod:(SEL)withClassMethod; - -@end diff --git a/socketio/Classes-iOS/GHUNIT/Mock/GHUNSObject+Swizzle.m b/socketio/Classes-iOS/GHUNIT/Mock/GHUNSObject+Swizzle.m deleted file mode 100644 index b9e8162..0000000 --- a/socketio/Classes-iOS/GHUNIT/Mock/GHUNSObject+Swizzle.m +++ /dev/null @@ -1,131 +0,0 @@ -// -// GHUNSObject+Swizzle.m -// GHUnit -// -// Created by Gabriel Handford on 4/13/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -#import "GHUNSObject+Swizzle.h" - -#import - -BOOL GHU_PerformSwizzle(Class klass, SEL origSel, SEL altSel, BOOL forInstance); - -@implementation NSObject(GHUSwizzle) - -+ (void)ghu_swizzleMethod:(SEL)original withMethod:(SEL)alternate { - GHU_PerformSwizzle([self class], original, alternate, YES); -} - -+ (void)ghu_swizzleClassMethod:(SEL)original withClassMethod:(SEL)alternate { - GHU_PerformSwizzle([self class], original, alternate, NO); -} - -@end - -// @see http://www.cocoadev.com/index.pl?MethodSwizzling -BOOL GHU_PerformSwizzle(Class klass, SEL origSel, SEL altSel, BOOL forInstance) { - - // Make sure the class isn't nil - if (klass == nil) - return NO; - - // Look for the methods in the implementation of the immediate class - Class iterKlass = (forInstance ? klass : klass->isa); - Method origMethod = NULL, altMethod = NULL; - unsigned int methodCount = 0; - Method *mlist = class_copyMethodList(iterKlass, &methodCount); - if ( mlist != NULL ) { - int i; - for (i = 0; i < methodCount; ++i) { - if ( method_getName(mlist[i]) == origSel ) - origMethod = mlist[i]; - if (method_getName(mlist[i]) == altSel) - altMethod = mlist[i]; - } - } - - // if origMethod was not found, that means it is not in the immediate class - // try searching the entire class hierarchy with class_getInstanceMethod - // if not found or not added, bail out - if ( origMethod == NULL ) { - origMethod = class_getInstanceMethod(iterKlass, origSel); - if ( origMethod == NULL ) - return NO; - if ( class_addMethod(iterKlass, method_getName(origMethod), method_getImplementation(origMethod), method_getTypeEncoding(origMethod)) == NO ) - return NO; - } - - // same thing with altMethod - if ( altMethod == NULL ) { - altMethod = class_getInstanceMethod(iterKlass, altSel); - if ( altMethod == NULL ) - return NO; - if ( class_addMethod(iterKlass, method_getName(altMethod), method_getImplementation(altMethod), method_getTypeEncoding(altMethod)) == NO ) - return NO; - } - - //clean up - free(mlist); - - // we now have to look up again for the methods in case they were not in the class implementation, - //but in one of the superclasses. In the latter, that means we added the method to the class, - //but the Leopard APIs is only 'class_addMethod', in which case we need to have the pointer - //to the Method objects actually stored in the Class structure (in the Tiger implementation, - //a new mlist was explicitely created with the added methods and directly added to the class; - //thus we were able to add a new Method AND get the pointer to it) - - // for simplicity, just use the same code as in the first step - origMethod = NULL; - altMethod = NULL; - methodCount = 0; - mlist = class_copyMethodList(iterKlass, &methodCount); - if ( mlist != NULL ) { - int i; - for (i = 0; i < methodCount; ++i) { - if ( method_getName(mlist[i]) == origSel ) - origMethod = mlist[i]; - if (method_getName(mlist[i]) == altSel) - altMethod = mlist[i]; - } - } - - // bail if one of the methods doesn't exist anywhere - // with all we did, this should not happen, though - if (origMethod == NULL || altMethod == NULL) - return NO; - - // now swizzle - method_exchangeImplementations(origMethod, altMethod); - - //clean up - free(mlist); - - return YES; -} - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/SharedUI/GHTestViewModel.h b/socketio/Classes-iOS/GHUNIT/SharedUI/GHTestViewModel.h deleted file mode 100644 index fcb4fcb..0000000 --- a/socketio/Classes-iOS/GHUNIT/SharedUI/GHTestViewModel.h +++ /dev/null @@ -1,218 +0,0 @@ -// -// GHTest.h -// GHUnit -// -// Created by Gabriel Handford on 1/17/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -#import "GHTestGroup.h" -#import "GHTestSuite.h" -#import "GHTestRunner.h" - -@class GHTestNode; - -@protocol GHTestNodeDelegate -- (void)testNodeDidChange:(GHTestNode *)node; -@end - -typedef enum { - GHTestNodeFilterNone = 0, - GHTestNodeFilterFailed = 1 -} GHTestNodeFilter; - -/*! - Test view model for use in a tree view. - */ -@interface GHTestViewModel : NSObject { - - NSString *identifier_; - GHTestSuite *suite_; - GHTestNode *root_; - - GHTestRunner *runner_; - - NSMutableDictionary *map_; // id#identifier -> GHTestNode - - BOOL editing_; - - NSMutableDictionary *defaults_; -} - -@property (readonly, nonatomic) GHTestNode *root; -@property (assign, nonatomic, getter=isEditing) BOOL editing; - -/*! - Create view model with root test group node. - - @param identifier Unique identifier for test model (used to load defaults) - @param suite Suite - */ -- (id)initWithIdentifier:(NSString *)identifier suite:(GHTestSuite *)suite; - -/*! - @result Name of test suite. - */ -- (NSString *)name; - -/*! - Status description. - - @param prefix Prefix to append - @result Current status string - */ -- (NSString *)statusString:(NSString *)prefix; - -/*! - Find the test node from the test. - - @param test Find test - */ -- (GHTestNode *)findTestNodeForTest:(id)test; - -/*! - Find the first failure. - - @result The first failure - */ -- (GHTestNode *)findFailure; - -/*! - Find the next failure starting from node. - - @param node Node to start from - */ -- (GHTestNode *)findFailureFromNode:(GHTestNode *)node; - -/*! - Register node, so that we can do a lookup later. See findTestNodeForTest:. - - @param node Node to register - */ -- (void)registerNode:(GHTestNode *)node; - -/*! - @result Returns the number of test groups. - */ -- (NSInteger)numberOfGroups; - -/*! - Returns the number of tests in group. - @param group Group number - @result The number of tests in group. - */ -- (NSInteger)numberOfTestsInGroup:(NSInteger)group; - -/*! - Search for path to test. - @param test Test - @result Index path - */ -- (NSIndexPath *)indexPathToTest:(id)test; - -/*! - Load defaults (user settings saved with saveDefaults). - */ -- (void)loadDefaults; - -/*! - Save defaults (user settings to be loaded with loadDefaults). - */ -- (void)saveDefaults; - -/*! - Run with current test suite. - - @param delegate Callback - @param inParallel If YES, will run tests in operation queue - @param options Options - */ -- (void)run:(id)delegate inParallel:(BOOL)inParallel options:(GHTestOptions)options; - -/*! - Cancel test run. - */ -- (void)cancel; - -/*! - Check if running. - - @result YES if running. - */ -- (BOOL)isRunning; - -@end - - -@interface GHTestNode : NSObject { - - id test_; - NSMutableArray */*of GHTestNode*/children_; - NSMutableArray */* of GHTestNode*/filteredChildren_; - - id delegate_; - GHTestNodeFilter filter_; - NSString *textFilter_; -} - -@property (readonly, nonatomic) NSArray */* of GHTestNode*/children; -@property (readonly, nonatomic) id test; -@property (assign, nonatomic) id delegate; -@property (assign, nonatomic) GHTestNodeFilter filter; -@property (retain, nonatomic) NSString *textFilter; - -- (id)initWithTest:(id)test children:(NSArray */*of id*/)children source:(GHTestViewModel *)source; -+ (GHTestNode *)nodeWithTest:(id)test children:(NSArray */*of id*/)children source:(GHTestViewModel *)source; - -- (NSString *)identifier; -- (NSString *)name; -- (NSString *)nameWithStatus; - -- (GHTestStatus)status; -- (NSString *)statusString; -- (NSString *)stackTrace; -- (NSString *)exceptionFilename; -- (NSInteger)exceptionLineNumber; -- (NSString *)log; -- (BOOL)isRunning; -- (BOOL)isDisabled; -- (BOOL)isHidden; -- (BOOL)isEnded; -- (BOOL)isGroupTest; // YES if test has "sub tests" - -- (BOOL)isSelected; -- (void)setSelected:(BOOL)selected; - -- (BOOL)hasChildren; -- (BOOL)failed; - -- (void)notifyChanged; - -- (void)setFilter:(GHTestNodeFilter)filter textFilter:(NSString *)textFilter; - -@end - -//! @endcond diff --git a/socketio/Classes-iOS/GHUNIT/SharedUI/GHTestViewModel.m b/socketio/Classes-iOS/GHUNIT/SharedUI/GHTestViewModel.m deleted file mode 100644 index 9758377..0000000 --- a/socketio/Classes-iOS/GHUNIT/SharedUI/GHTestViewModel.m +++ /dev/null @@ -1,424 +0,0 @@ -// -// GHTestViewModel.m -// GHUnit -// -// Created by Gabriel Handford on 1/17/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -//! @cond DEV - -#import "GHTestViewModel.h" -#import "GTMStackTrace.h" -#import "GHTesting.h" - -@implementation GHTestViewModel - -@synthesize root=root_, editing=editing_; - -- (id)initWithIdentifier:(NSString *)identifier suite:(GHTestSuite *)suite { - if ((self = [super init])) { - identifier_ = [identifier retain]; - suite_ = [suite retain]; - root_ = [[GHTestNode alloc] initWithTest:suite_ children:[suite_ children] source:self]; - map_ = [[NSMutableDictionary dictionary] retain]; - } - return self; -} - -- (void)dealloc { - // Clear delegates - for(NSString *identifier in map_) - [[map_ objectForKey:identifier] setDelegate:nil]; - - [identifier_ release]; - [root_ release]; - [map_ release]; - [defaults_ release]; - [suite_ release]; - [runner_ cancel]; - runner_.delegate = nil; - [runner_ release]; - - [super dealloc]; -} - -- (NSString *)name { - return [root_ name]; -} - -- (NSString *)statusString:(NSString *)prefix { - NSInteger totalRunCount = [suite_ stats].testCount - ([suite_ disabledCount] + [suite_ stats].cancelCount); - NSString *statusInterval = [NSString stringWithFormat:@"%@ %0.3fs (%0.3fs in test time)", (self.isRunning ? @"Running" : @"Took"), runner_.interval, [suite_ interval]]; - return [NSString stringWithFormat:@"%@%@ %d/%d (%d failures)", prefix, statusInterval, - [suite_ stats].succeedCount, totalRunCount, [suite_ stats].failureCount]; -} - -- (void)registerNode:(GHTestNode *)node { - [map_ setObject:node forKey:node.identifier]; - node.delegate = self; -} - -- (GHTestNode *)findTestNodeForTest:(id)test { - return [map_ objectForKey:[test identifier]]; -} - -- (GHTestNode *)findFailure { - return [self findFailureFromNode:root_]; -} - -- (GHTestNode *)findFailureFromNode:(GHTestNode *)node { - if (node.failed && [node.test exception]) return node; - for(GHTestNode *childNode in node.children) { - GHTestNode *foundNode = [self findFailureFromNode:childNode]; - if (foundNode) return foundNode; - } - return nil; -} - -- (NSInteger)numberOfGroups { - return [[root_ children] count]; -} - -- (NSInteger)numberOfTestsInGroup:(NSInteger)group { - NSArray *children = [root_ children]; - if ([children count] == 0) return 0; - GHTestNode *groupNode = [children objectAtIndex:group]; - return [[groupNode children] count]; -} - -- (NSIndexPath *)indexPathToTest:(id)test { - NSInteger section = 0; - for(GHTestNode *node in [root_ children]) { - NSInteger row = 0; - if ([node.test isEqual:test]) { - NSUInteger pathIndexes[] = {section,row}; - return [NSIndexPath indexPathWithIndexes:pathIndexes length:2]; // Not user row:section: for compatibility with MacOSX - } - for(GHTestNode *childNode in [node children]) { - if ([childNode.test isEqual:test]) { - NSUInteger pathIndexes[] = {section,row}; - return [NSIndexPath indexPathWithIndexes:pathIndexes length:2]; - } - row++; - } - section++; - } - return nil; -} - -- (void)testNodeDidChange:(GHTestNode *)node { } - -- (NSString *)_defaultsPath { - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); - if ([paths count] == 0) return nil; - NSString *identifier = identifier_; - if (!identifier) identifier = @"Tests"; - return [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"GHUnit-%@.tests", identifier]]; -} - -- (void)_updateTestNodeWithDefaults:(GHTestNode *)node { - id test = node.test; - id testDefault = [defaults_ objectForKey:test.identifier]; - if (testDefault) { - test.status = testDefault.status; - test.interval = testDefault.interval; - #if !TARGET_OS_IPHONE // Don't use hidden state for iPhone - if ([test isKindOfClass:[GHTest class]]) - [test setHidden:testDefault.hidden]; - #endif - } - for(GHTestNode *childNode in [node children]) - [self _updateTestNodeWithDefaults:childNode]; -} - -- (void)_saveTestNodeToDefaults:(GHTestNode *)node { - [defaults_ setObject:node.test forKey:node.test.identifier]; - for(GHTestNode *childNode in [node children]) - [self _saveTestNodeToDefaults:childNode]; -} - -- (void)loadDefaults { - if (!defaults_) { - NSString *path = [self _defaultsPath]; - if (path) defaults_ = [[NSKeyedUnarchiver unarchiveObjectWithFile:path] retain]; - } - if (!defaults_) defaults_ = [[NSMutableDictionary dictionary] retain]; - [self _updateTestNodeWithDefaults:root_]; -} - -- (void)saveDefaults { - NSString *path = [self _defaultsPath]; - if (!path || !defaults_) return; - - [self _saveTestNodeToDefaults:root_]; - [NSKeyedArchiver archiveRootObject:defaults_ toFile:path]; -} - -- (void)cancel { - [runner_ cancel]; -} - -- (void)run:(id)delegate inParallel:(BOOL)inParallel options:(GHTestOptions)options { - // Reset (non-disabled) tests so we don't clear non-filtered tests status; in case we re-filter and they become visible - for(id test in [suite_ children]) - if (!test.disabled) [test reset]; - - if (!runner_) { - runner_ = [[GHTestRunner runnerForSuite:suite_] retain]; - } - runner_.delegate = delegate; - runner_.options = options; - [runner_ setInParallel:inParallel]; - [runner_ runInBackground]; -} - -- (BOOL)isRunning { - return runner_.isRunning; -} - -@end - -@implementation GHTestNode - -@synthesize test=test_, children=children_, delegate=delegate_, filter=filter_, textFilter=textFilter_; - -- (id)initWithTest:(id)test children:(NSArray */*of id*/)children source:(GHTestViewModel *)source { - if ((self = [super init])) { - test_ = [test retain]; - - NSMutableArray *nodeChildren = [NSMutableArray array]; - for(id test in children) { - - GHTestNode *node = nil; - if ([test conformsToProtocol:@protocol(GHTestGroup)]) { - NSArray *testChildren = [(id)test children]; - if ([testChildren count] > 0) - node = [GHTestNode nodeWithTest:test children:testChildren source:source]; - } else { - node = [GHTestNode nodeWithTest:test children:nil source:source]; - } - if (node) - [nodeChildren addObject:node]; - } - children_ = [nodeChildren retain]; - [source registerNode:self]; - } - return self; -} - -- (void)dealloc { - [test_ release]; - [children_ release]; - [filteredChildren_ release]; - [textFilter_ release]; - [super dealloc]; -} - -+ (GHTestNode *)nodeWithTest:(id)test children:(NSArray *)children source:(GHTestViewModel *)source { - return [[[GHTestNode alloc] initWithTest:test children:children source:source] autorelease]; -} - -- (BOOL)hasChildren { - return [self.children count] > 0; -} - -- (void)notifyChanged { - [delegate_ testNodeDidChange:self]; -} - -- (NSArray *)children { - if (filter_ != GHTestNodeFilterNone || textFilter_) return filteredChildren_; - return children_; -} - -- (void)_applyFilters { - NSMutableSet *textFiltered = [NSMutableSet set]; - for(GHTestNode *childNode in children_) { - [childNode setTextFilter:textFilter_]; - if (textFilter_) { - if ([self.name hasPrefix:textFilter_] || [childNode.name hasPrefix:textFilter_] || [childNode hasChildren]) - [textFiltered addObject:childNode]; - } - } - - NSMutableSet *filtered = [NSMutableSet set]; - for(GHTestNode *childNode in children_) { - [childNode setFilter:filter_]; - if (filter_ == GHTestNodeFilterFailed) { - if ([childNode hasChildren] || childNode.failed) - [filtered addObject:childNode]; - } - } - - [filteredChildren_ release]; - filteredChildren_ = [[NSMutableArray array] retain]; - for(GHTestNode *childNode in children_) { - if ((!textFilter_ || [textFiltered containsObject:childNode]) && - (filter_ == GHTestNodeFilterNone || [filtered containsObject:childNode]) || [childNode hasChildren]) { - [filteredChildren_ addObject:childNode]; - if (![childNode hasChildren]) { - [childNode.test setDisabled:NO]; - } - } else { - if (![childNode hasChildren]) { - [childNode.test setDisabled:YES]; - } - } - } -} - -- (void)setTextFilter:(NSString *)textFilter { - [self setFilter:filter_ textFilter:textFilter]; -} - -- (void)setFilter:(GHTestNodeFilter)filter { - [self setFilter:filter textFilter:textFilter_]; -} - -- (void)setFilter:(GHTestNodeFilter)filter textFilter:(NSString *)textFilter { - filter_ = filter; - - textFilter = [textFilter stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; - if ([textFilter isEqualToString:@""]) textFilter = nil; - - [textFilter retain]; - [textFilter_ release]; - textFilter_ = textFilter; - [self _applyFilters]; -} - -- (NSString *)name { - return [test_ name]; -} - -- (NSString *)identifier { - return [test_ identifier]; -} - -- (NSString *)statusString { - // TODO(gabe): Some other special chars: ☐✖✗✘✓ - NSString *status = @""; - NSString *interval = @""; - if (self.isRunning) { - status = @"✸"; - if (self.isGroupTest) - interval = [NSString stringWithFormat:@"%0.2fs", [test_ interval]]; - } else if (self.isEnded) { - if ([test_ interval] >= 0) - interval = [NSString stringWithFormat:@"%0.2fs", [test_ interval]]; - - if ([test_ status] == GHTestStatusErrored) status = @"✘"; - else if ([test_ status] == GHTestStatusSucceeded) status = @"✔"; - else if ([test_ status] == GHTestStatusCancelled) { - status = @"-"; - interval = @""; - } else if ([test_ isDisabled] || [test_ isHidden]) { - status = @"⊝"; - interval = @""; - } - } else if (!self.isSelected) { - status = @""; - } - - if (self.isGroupTest) { - NSString *statsString = [NSString stringWithFormat:@"%d/%d (%d failed)", - ([test_ stats].succeedCount+[test_ stats].failureCount), - [test_ stats].testCount, [test_ stats].failureCount]; - return [NSString stringWithFormat:@"%@ %@ %@", status, statsString, interval]; - } else { - return [NSString stringWithFormat:@"%@ %@", status, interval]; - } -} - -- (NSString *)nameWithStatus { - NSString *interval = @""; - if (self.isEnded) interval = [NSString stringWithFormat:@" (%0.2fs)", [test_ interval]]; - return [NSString stringWithFormat:@"%@%@", self.name, interval]; -} - -- (BOOL)isGroupTest { - return ([test_ conformsToProtocol:@protocol(GHTestGroup)]); -} - -- (BOOL)failed { - return [test_ status] == GHTestStatusErrored; -} - -- (BOOL)isRunning { - return GHTestStatusIsRunning([test_ status]); -} - -- (BOOL)isDisabled { - return [test_ isDisabled]; -} - -- (BOOL)isHidden { - return [test_ isHidden]; -} - -- (BOOL)isEnded { - return GHTestStatusEnded([test_ status]); -} - -- (GHTestStatus)status { - return [test_ status]; -} - -- (NSString *)stackTrace { - if (![test_ exception]) return nil; - - return [GHTesting descriptionForException:[test_ exception]]; -} - -- (NSString *)exceptionFilename { - return [GHTesting exceptionFilenameForTest:test_]; -} - -- (NSInteger)exceptionLineNumber { - return [GHTesting exceptionLineNumberForTest:test_]; -} - -- (NSString *)log { - return [[test_ log] componentsJoinedByString:@"\n"]; // TODO(gabe): This isn't very performant -} - -- (NSString *)description { - return [test_ description]; -} - -- (BOOL)isSelected { - return ![test_ isHidden]; -} - -- (void)setSelected:(BOOL)selected { - [test_ setHidden:!selected]; - for(GHTestNode *node in children_) - [node setSelected:selected]; - [self notifyChanged]; -} - -@end - -//! @endcond diff --git a/socketio/External/GHUnit/README-GHUnit b/socketio/External/GHUnit/README-GHUnit deleted file mode 100644 index 52daa83..0000000 --- a/socketio/External/GHUnit/README-GHUnit +++ /dev/null @@ -1,3 +0,0 @@ -This folder is where ASIHTTPRequest looks for GHUnit frameworks. - -When you run one of the test targets, a build script will attempt to download a pre-compiled framework and install it here, if one does not already exist. If you would prefer to build GHUnit yourself, simply grab a copy from https://github.com/gabriel/gh-unit, and drop your built framework in this folder. \ No newline at end of file diff --git a/socketio/External/Reachability/Reachability.h b/socketio/External/Reachability/Reachability.h deleted file mode 100644 index af52444..0000000 --- a/socketio/External/Reachability/Reachability.h +++ /dev/null @@ -1,194 +0,0 @@ -/* - - File: Reachability.h - Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs. - - Version: 2.0.4ddg - */ - -/* - Significant additions made by Andrew W. Donoho, August 11, 2009. - This is a derived work of Apple's Reachability v2.0 class. - - The below license is the new BSD license with the OSI recommended personalizations. - - - Extensions Copyright (C) 2009 Donoho Design Group, LLC. All Rights Reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of Andrew W. Donoho nor Donoho Design Group, L.L.C. - may be used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY DONOHO DESIGN GROUP, L.L.C. "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - */ - - -/* - - Apple's Original License on Reachability v2.0 - - Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. - ("Apple") in consideration of your agreement to the following terms, and your - use, installation, modification or redistribution of this Apple software - constitutes acceptance of these terms. If you do not agree with these terms, - please do not use, install, modify or redistribute this Apple software. - - In consideration of your agreement to abide by the following terms, and subject - to these terms, Apple grants you a personal, non-exclusive license, under - Apple's copyrights in this original Apple software (the "Apple Software"), to - use, reproduce, modify and redistribute the Apple Software, with or without - modifications, in source and/or binary forms; provided that if you redistribute - the Apple Software in its entirety and without modifications, you must retain - this notice and the following text and disclaimers in all such redistributions - of the Apple Software. - - Neither the name, trademarks, service marks or logos of Apple Inc. may be used - to endorse or promote products derived from the Apple Software without specific - prior written permission from Apple. Except as expressly stated in this notice, - no other rights or licenses, express or implied, are granted by Apple herein, - including but not limited to any patent rights that may be infringed by your - derivative works or by other works in which the Apple Software may be - incorporated. - - The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO - WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED - WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN - COMBINATION WITH YOUR PRODUCTS. - - IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR - DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF - CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF - APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - Copyright (C) 2009 Apple Inc. All Rights Reserved. - - */ - - -/* - DDG extensions include: - Each reachability object now has a copy of the key used to store it in a - dictionary. This allows each observer to quickly determine if the event is - important to them. - - -currentReachabilityStatus also has a significantly different decision criteria than - Apple's code. - - A multiple convenience test methods have been added. - */ - -#import -#import -#import - -#define USE_DDG_EXTENSIONS 1 // Use DDG's Extensions to test network criteria. -// Since NSAssert and NSCAssert are used in this code, -// I recommend you set NS_BLOCK_ASSERTIONS=1 in the release versions of your projects. - -enum { - - // DDG NetworkStatus Constant Names. - kNotReachable = 0, // Apple's code depends upon 'NotReachable' being the same value as 'NO'. - kReachableViaWWAN, // Switched order from Apple's enum. WWAN is active before WiFi. - kReachableViaWiFi - -}; -typedef uint32_t NetworkStatus; - -enum { - - // Apple NetworkStatus Constant Names. - NotReachable = kNotReachable, - ReachableViaWiFi = kReachableViaWiFi, - ReachableViaWWAN = kReachableViaWWAN - -}; - - -extern NSString *const kInternetConnection; -extern NSString *const kLocalWiFiConnection; -extern NSString *const kReachabilityChangedNotification; - -@interface Reachability: NSObject { - -@private - NSString *key_; - SCNetworkReachabilityRef reachabilityRef; - -} - -@property (copy) NSString *key; // Atomic because network operations are asynchronous. - -// Designated Initializer. -- (Reachability *) initWithReachabilityRef: (SCNetworkReachabilityRef) ref; - -// Use to check the reachability of a particular host name. -+ (Reachability *) reachabilityWithHostName: (NSString*) hostName; - -// Use to check the reachability of a particular IP address. -+ (Reachability *) reachabilityWithAddress: (const struct sockaddr_in*) hostAddress; - -// Use to check whether the default route is available. -// Should be used to, at minimum, establish network connectivity. -+ (Reachability *) reachabilityForInternetConnection; - -// Use to check whether a local wifi connection is available. -+ (Reachability *) reachabilityForLocalWiFi; - -//Start listening for reachability notifications on the current run loop. -- (BOOL) startNotifier; -- (void) stopNotifier; - -// Comparison routines to enable choosing actions in a notification. -- (BOOL) isEqual: (Reachability *) r; - -// These are the status tests. -- (NetworkStatus) currentReachabilityStatus; - -// The main direct test of reachability. -- (BOOL) isReachable; - -// WWAN may be available, but not active until a connection has been established. -// WiFi may require a connection for VPN on Demand. -- (BOOL) isConnectionRequired; // Identical DDG variant. -- (BOOL) connectionRequired; // Apple's routine. - -// Dynamic, on demand connection? -- (BOOL) isConnectionOnDemand; - -// Is user intervention required? -- (BOOL) isInterventionRequired; - -// Routines for specific connection testing by your app. -- (BOOL) isReachableViaWWAN; -- (BOOL) isReachableViaWiFi; - -- (SCNetworkReachabilityFlags) reachabilityFlags; - -@end diff --git a/socketio/External/Reachability/Reachability.m b/socketio/External/Reachability/Reachability.m deleted file mode 100644 index efe99b3..0000000 --- a/socketio/External/Reachability/Reachability.m +++ /dev/null @@ -1,814 +0,0 @@ -/* - - File: Reachability.m - Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs. - - Version: 2.0.4ddg - */ - -/* - Significant additions made by Andrew W. Donoho, August 11, 2009. - This is a derived work of Apple's Reachability v2.0 class. - - The below license is the new BSD license with the OSI recommended personalizations. - - - Extensions Copyright (C) 2009 Donoho Design Group, LLC. All Rights Reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of Andrew W. Donoho nor Donoho Design Group, L.L.C. - may be used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY DONOHO DESIGN GROUP, L.L.C. "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - */ - - -/* - - Apple's Original License on Reachability v2.0 - - Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. - ("Apple") in consideration of your agreement to the following terms, and your - use, installation, modification or redistribution of this Apple software - constitutes acceptance of these terms. If you do not agree with these terms, - please do not use, install, modify or redistribute this Apple software. - - In consideration of your agreement to abide by the following terms, and subject - to these terms, Apple grants you a personal, non-exclusive license, under - Apple's copyrights in this original Apple software (the "Apple Software"), to - use, reproduce, modify and redistribute the Apple Software, with or without - modifications, in source and/or binary forms; provided that if you redistribute - the Apple Software in its entirety and without modifications, you must retain - this notice and the following text and disclaimers in all such redistributions - of the Apple Software. - - Neither the name, trademarks, service marks or logos of Apple Inc. may be used - to endorse or promote products derived from the Apple Software without specific - prior written permission from Apple. Except as expressly stated in this notice, - no other rights or licenses, express or implied, are granted by Apple herein, - including but not limited to any patent rights that may be infringed by your - derivative works or by other works in which the Apple Software may be - incorporated. - - The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO - WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED - WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN - COMBINATION WITH YOUR PRODUCTS. - - IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR - DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF - CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF - APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - Copyright (C) 2009 Apple Inc. All Rights Reserved. - -*/ - -/* - Each reachability object now has a copy of the key used to store it in a dictionary. - This allows each observer to quickly determine if the event is important to them. -*/ - -#import -#import -#import -#import -#import -#import - -#import - -#import "Reachability.h" - -NSString *const kInternetConnection = @"InternetConnection"; -NSString *const kLocalWiFiConnection = @"LocalWiFiConnection"; -NSString *const kReachabilityChangedNotification = @"NetworkReachabilityChangedNotification"; - -#define CLASS_DEBUG 1 // Turn on logReachabilityFlags. Must also have a project wide defined DEBUG. - -#if (defined DEBUG && defined CLASS_DEBUG) -#define logReachabilityFlags(flags) (logReachabilityFlags_(__PRETTY_FUNCTION__, __LINE__, flags)) - -static NSString *reachabilityFlags_(SCNetworkReachabilityFlags flags) { - -#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 30000) // Apple advises you to use the magic number instead of a symbol. - return [NSString stringWithFormat:@"Reachability Flags: %c%c %c%c%c%c%c%c%c", - (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-', - (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-', - - (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-', - (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-', - (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-', - (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-', - (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-', - (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-', - (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-']; -#else - // Compile out the v3.0 features for v2.2.1 deployment. - return [NSString stringWithFormat:@"Reachability Flags: %c%c %c%c%c%c%c%c", - (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-', - (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-', - - (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-', - (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-', - (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-', - // v3 kSCNetworkReachabilityFlagsConnectionOnTraffic == v2 kSCNetworkReachabilityFlagsConnectionAutomatic - (flags & kSCNetworkReachabilityFlagsConnectionAutomatic) ? 'C' : '-', - // (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-', // No v2 equivalent. - (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-', - (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-']; -#endif - -} // reachabilityFlags_() - -static void logReachabilityFlags_(const char *name, int line, SCNetworkReachabilityFlags flags) { - - NSLog(@"%s (%d) \n\t%@", name, line, reachabilityFlags_(flags)); - -} // logReachabilityFlags_() - -#define logNetworkStatus(status) (logNetworkStatus_(__PRETTY_FUNCTION__, __LINE__, status)) - -static void logNetworkStatus_(const char *name, int line, NetworkStatus status) { - - NSString *statusString = nil; - - switch (status) { - case kNotReachable: - statusString = [NSString stringWithString: @"Not Reachable"]; - break; - case kReachableViaWWAN: - statusString = [NSString stringWithString: @"Reachable via WWAN"]; - break; - case kReachableViaWiFi: - statusString = [NSString stringWithString: @"Reachable via WiFi"]; - break; - } - - NSLog(@"%s (%d) \n\tNetwork Status: %@", name, line, statusString); - -} // logNetworkStatus_() - -#else -#define logReachabilityFlags(flags) -#define logNetworkStatus(status) -#endif - -@interface Reachability (private) - -- (NetworkStatus) networkStatusForFlags: (SCNetworkReachabilityFlags) flags; - -@end - -@implementation Reachability - -@synthesize key = key_; - -// Preclude direct access to ivars. -+ (BOOL) accessInstanceVariablesDirectly { - - return NO; - -} // accessInstanceVariablesDirectly - - -- (void) dealloc { - - [self stopNotifier]; - if(reachabilityRef) { - - CFRelease(reachabilityRef); reachabilityRef = NULL; - - } - - self.key = nil; - - [super dealloc]; - -} // dealloc - - -- (Reachability *) initWithReachabilityRef: (SCNetworkReachabilityRef) ref -{ - self = [super init]; - if (self != nil) - { - reachabilityRef = ref; - } - - return self; - -} // initWithReachabilityRef: - - -#if (defined DEBUG && defined CLASS_DEBUG) -- (NSString *) description { - - NSAssert(reachabilityRef, @"-description called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags = 0; - - SCNetworkReachabilityGetFlags(reachabilityRef, &flags); - - return [NSString stringWithFormat: @"%@\n\t%@", self.key, reachabilityFlags_(flags)]; - -} // description -#endif - - -#pragma mark - -#pragma mark Notification Management Methods - - -//Start listening for reachability notifications on the current run loop -static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) { - - #pragma unused (target, flags) - NSCAssert(info, @"info was NULL in ReachabilityCallback"); - NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], @"info was the wrong class in ReachabilityCallback"); - - //We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively - // in case someone uses the Reachablity object in a different thread. - NSAutoreleasePool* pool = [NSAutoreleasePool new]; - - // Post a notification to notify the client that the network reachability changed. - [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification - object: (Reachability *) info]; - - [pool release]; - -} // ReachabilityCallback() - - -- (BOOL) startNotifier { - - SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL}; - - if(SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context)) { - - if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) { - - return YES; - - } - - } - - return NO; - -} // startNotifier - - -- (void) stopNotifier { - - if(reachabilityRef) { - - SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); - - } - -} // stopNotifier - - -- (BOOL) isEqual: (Reachability *) r { - - return [r.key isEqualToString: self.key]; - -} // isEqual: - - -#pragma mark - -#pragma mark Reachability Allocation Methods - - -+ (Reachability *) reachabilityWithHostName: (NSString *) hostName { - - SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]); - - if (ref) { - - Reachability *r = [[[self alloc] initWithReachabilityRef: ref] autorelease]; - - r.key = hostName; - - return r; - - } - - return nil; - -} // reachabilityWithHostName - - -+ (NSString *) makeAddressKey: (in_addr_t) addr { - // addr is assumed to be in network byte order. - - static const int highShift = 24; - static const int highMidShift = 16; - static const int lowMidShift = 8; - static const in_addr_t mask = 0x000000ff; - - addr = ntohl(addr); - - return [NSString stringWithFormat: @"%d.%d.%d.%d", - (addr >> highShift) & mask, - (addr >> highMidShift) & mask, - (addr >> lowMidShift) & mask, - addr & mask]; - -} // makeAddressKey: - - -+ (Reachability *) reachabilityWithAddress: (const struct sockaddr_in *) hostAddress { - - SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress); - - if (ref) { - - Reachability *r = [[[self alloc] initWithReachabilityRef: ref] autorelease]; - - r.key = [self makeAddressKey: hostAddress->sin_addr.s_addr]; - - return r; - - } - - return nil; - -} // reachabilityWithAddress - - -+ (Reachability *) reachabilityForInternetConnection { - - struct sockaddr_in zeroAddress; - bzero(&zeroAddress, sizeof(zeroAddress)); - zeroAddress.sin_len = sizeof(zeroAddress); - zeroAddress.sin_family = AF_INET; - - Reachability *r = [self reachabilityWithAddress: &zeroAddress]; - - r.key = kInternetConnection; - - return r; - -} // reachabilityForInternetConnection - - -+ (Reachability *) reachabilityForLocalWiFi { - - struct sockaddr_in localWifiAddress; - bzero(&localWifiAddress, sizeof(localWifiAddress)); - localWifiAddress.sin_len = sizeof(localWifiAddress); - localWifiAddress.sin_family = AF_INET; - // IN_LINKLOCALNETNUM is defined in as 169.254.0.0 - localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); - - Reachability *r = [self reachabilityWithAddress: &localWifiAddress]; - - r.key = kLocalWiFiConnection; - - return r; - -} // reachabilityForLocalWiFi - - -#pragma mark - -#pragma mark Network Flag Handling Methods - - -#if USE_DDG_EXTENSIONS -// -// iPhone condition codes as reported by a 3GS running iPhone OS v3.0. -// Airplane Mode turned on: Reachability Flag Status: -- ------- -// WWAN Active: Reachability Flag Status: WR -t----- -// WWAN Connection required: Reachability Flag Status: WR ct----- -// WiFi turned on: Reachability Flag Status: -R ------- Reachable. -// Local WiFi turned on: Reachability Flag Status: -R xxxxxxd Reachable. -// WiFi turned on: Reachability Flag Status: -R ct----- Connection down. (Non-intuitive, empirically determined answer.) -const SCNetworkReachabilityFlags kConnectionDown = kSCNetworkReachabilityFlagsConnectionRequired | - kSCNetworkReachabilityFlagsTransientConnection; -// WiFi turned on: Reachability Flag Status: -R ct-i--- Reachable but it will require user intervention (e.g. enter a WiFi password). -// WiFi turned on: Reachability Flag Status: -R -t----- Reachable via VPN. -// -// In the below method, an 'x' in the flag status means I don't care about its value. -// -// This method differs from Apple's by testing explicitly for empirically observed values. -// This gives me more confidence in it's correct behavior. Apple's code covers more cases -// than mine. My code covers the cases that occur. -// -- (NetworkStatus) networkStatusForFlags: (SCNetworkReachabilityFlags) flags { - - if (flags & kSCNetworkReachabilityFlagsReachable) { - - // Local WiFi -- Test derived from Apple's code: -localWiFiStatusForFlags:. - if (self.key == kLocalWiFiConnection) { - - // Reachability Flag Status: xR xxxxxxd Reachable. - return (flags & kSCNetworkReachabilityFlagsIsDirect) ? kReachableViaWiFi : kNotReachable; - - } - - // Observed WWAN Values: - // WWAN Active: Reachability Flag Status: WR -t----- - // WWAN Connection required: Reachability Flag Status: WR ct----- - // - // Test Value: Reachability Flag Status: WR xxxxxxx - if (flags & kSCNetworkReachabilityFlagsIsWWAN) { return kReachableViaWWAN; } - - // Clear moot bits. - flags &= ~kSCNetworkReachabilityFlagsReachable; - flags &= ~kSCNetworkReachabilityFlagsIsDirect; - flags &= ~kSCNetworkReachabilityFlagsIsLocalAddress; // kInternetConnection is local. - - // Reachability Flag Status: -R ct---xx Connection down. - if (flags == kConnectionDown) { return kNotReachable; } - - // Reachability Flag Status: -R -t---xx Reachable. WiFi + VPN(is up) (Thank you Ling Wang) - if (flags & kSCNetworkReachabilityFlagsTransientConnection) { return kReachableViaWiFi; } - - // Reachability Flag Status: -R -----xx Reachable. - if (flags == 0) { return kReachableViaWiFi; } - - // Apple's code tests for dynamic connection types here. I don't. - // If a connection is required, regardless of whether it is on demand or not, it is a WiFi connection. - // If you care whether a connection needs to be brought up, use -isConnectionRequired. - // If you care about whether user intervention is necessary, use -isInterventionRequired. - // If you care about dynamically establishing the connection, use -isConnectionIsOnDemand. - - // Reachability Flag Status: -R cxxxxxx Reachable. - if (flags & kSCNetworkReachabilityFlagsConnectionRequired) { return kReachableViaWiFi; } - - // Required by the compiler. Should never get here. Default to not connected. -#if (defined DEBUG && defined CLASS_DEBUG) - NSAssert1(NO, @"Uncaught reachability test. Flags: %@", reachabilityFlags_(flags)); -#endif - return kNotReachable; - - } - - // Reachability Flag Status: x- xxxxxxx - return kNotReachable; - -} // networkStatusForFlags: - - -- (NetworkStatus) currentReachabilityStatus { - - NSAssert(reachabilityRef, @"currentReachabilityStatus called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags = 0; - NetworkStatus status = kNotReachable; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - -// logReachabilityFlags(flags); - - status = [self networkStatusForFlags: flags]; - - return status; - - } - - return kNotReachable; - -} // currentReachabilityStatus - - -- (BOOL) isReachable { - - NSAssert(reachabilityRef, @"isReachable called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags = 0; - NetworkStatus status = kNotReachable; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - -// logReachabilityFlags(flags); - - status = [self networkStatusForFlags: flags]; - -// logNetworkStatus(status); - - return (kNotReachable != status); - - } - - return NO; - -} // isReachable - - -- (BOOL) isConnectionRequired { - - NSAssert(reachabilityRef, @"isConnectionRequired called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - - logReachabilityFlags(flags); - - return (flags & kSCNetworkReachabilityFlagsConnectionRequired); - - } - - return NO; - -} // isConnectionRequired - - -- (BOOL) connectionRequired { - - return [self isConnectionRequired]; - -} // connectionRequired -#endif - - -#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 30000) -static const SCNetworkReachabilityFlags kOnDemandConnection = kSCNetworkReachabilityFlagsConnectionOnTraffic | - kSCNetworkReachabilityFlagsConnectionOnDemand; -#else -static const SCNetworkReachabilityFlags kOnDemandConnection = kSCNetworkReachabilityFlagsConnectionAutomatic; -#endif - -- (BOOL) isConnectionOnDemand { - - NSAssert(reachabilityRef, @"isConnectionIsOnDemand called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - - logReachabilityFlags(flags); - - return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) && - (flags & kOnDemandConnection)); - - } - - return NO; - -} // isConnectionOnDemand - - -- (BOOL) isInterventionRequired { - - NSAssert(reachabilityRef, @"isInterventionRequired called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - - logReachabilityFlags(flags); - - return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) && - (flags & kSCNetworkReachabilityFlagsInterventionRequired)); - - } - - return NO; - -} // isInterventionRequired - - -- (BOOL) isReachableViaWWAN { - - NSAssert(reachabilityRef, @"isReachableViaWWAN called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags = 0; - NetworkStatus status = kNotReachable; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - - logReachabilityFlags(flags); - - status = [self networkStatusForFlags: flags]; - - return (kReachableViaWWAN == status); - - } - - return NO; - -} // isReachableViaWWAN - - -- (BOOL) isReachableViaWiFi { - - NSAssert(reachabilityRef, @"isReachableViaWiFi called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags = 0; - NetworkStatus status = kNotReachable; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - - logReachabilityFlags(flags); - - status = [self networkStatusForFlags: flags]; - - return (kReachableViaWiFi == status); - - } - - return NO; - -} // isReachableViaWiFi - - -- (SCNetworkReachabilityFlags) reachabilityFlags { - - NSAssert(reachabilityRef, @"reachabilityFlags called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags = 0; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - - logReachabilityFlags(flags); - - return flags; - - } - - return 0; - -} // reachabilityFlags - - -#pragma mark - -#pragma mark Apple's Network Flag Handling Methods - - -#if !USE_DDG_EXTENSIONS -/* - * - * Apple's Network Status testing code. - * The only changes that have been made are to use the new logReachabilityFlags macro and - * test for local WiFi via the key instead of Apple's boolean. Also, Apple's code was for v3.0 only - * iPhone OS. v2.2.1 and earlier conditional compiling is turned on. Hence, to mirror Apple's behavior, - * set your Base SDK to v3.0 or higher. - * - */ - -- (NetworkStatus) localWiFiStatusForFlags: (SCNetworkReachabilityFlags) flags -{ - logReachabilityFlags(flags); - - BOOL retVal = NotReachable; - if((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect)) - { - retVal = ReachableViaWiFi; - } - return retVal; -} - - -- (NetworkStatus) networkStatusForFlags: (SCNetworkReachabilityFlags) flags -{ - logReachabilityFlags(flags); - if (!(flags & kSCNetworkReachabilityFlagsReachable)) - { - // if target host is not reachable - return NotReachable; - } - - BOOL retVal = NotReachable; - - if (!(flags & kSCNetworkReachabilityFlagsConnectionRequired)) - { - // if target host is reachable and no connection is required - // then we'll assume (for now) that your on Wi-Fi - retVal = ReachableViaWiFi; - } - -#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 30000) // Apple advises you to use the magic number instead of a symbol. - if ((flags & kSCNetworkReachabilityFlagsConnectionOnDemand) || - (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)) -#else - if (flags & kSCNetworkReachabilityFlagsConnectionAutomatic) -#endif - { - // ... and the connection is on-demand (or on-traffic) if the - // calling application is using the CFSocketStream or higher APIs - - if (!(flags & kSCNetworkReachabilityFlagsInterventionRequired)) - { - // ... and no [user] intervention is needed - retVal = ReachableViaWiFi; - } - } - - if (flags & kSCNetworkReachabilityFlagsIsWWAN) - { - // ... but WWAN connections are OK if the calling application - // is using the CFNetwork (CFSocketStream?) APIs. - retVal = ReachableViaWWAN; - } - return retVal; -} - - -- (NetworkStatus) currentReachabilityStatus -{ - NSAssert(reachabilityRef, @"currentReachabilityStatus called with NULL reachabilityRef"); - - NetworkStatus retVal = NotReachable; - SCNetworkReachabilityFlags flags; - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) - { - if(self.key == kLocalWiFiConnection) - { - retVal = [self localWiFiStatusForFlags: flags]; - } - else - { - retVal = [self networkStatusForFlags: flags]; - } - } - return retVal; -} - - -- (BOOL) isReachable { - - NSAssert(reachabilityRef, @"isReachable called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags = 0; - NetworkStatus status = kNotReachable; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - - logReachabilityFlags(flags); - - if(self.key == kLocalWiFiConnection) { - - status = [self localWiFiStatusForFlags: flags]; - - } else { - - status = [self networkStatusForFlags: flags]; - - } - - return (kNotReachable != status); - - } - - return NO; - -} // isReachable - - -- (BOOL) isConnectionRequired { - - return [self connectionRequired]; - -} // isConnectionRequired - - -- (BOOL) connectionRequired { - - NSAssert(reachabilityRef, @"connectionRequired called with NULL reachabilityRef"); - - SCNetworkReachabilityFlags flags; - - if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { - - logReachabilityFlags(flags); - - return (flags & kSCNetworkReachabilityFlagsConnectionRequired); - - } - - return NO; - -} // connectionRequired -#endif - -@end diff --git a/socketio/Tests/ASICloudFilesRequestTests.h b/socketio/Tests/ASICloudFilesRequestTests.h deleted file mode 100644 index b349204..0000000 --- a/socketio/Tests/ASICloudFilesRequestTests.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// ASICloudFilesRequestTests.h -// -// Created by Michael Mayo on 1/6/10. -// - -#import "ASITestCase.h" - -@class ASINetworkQueue; - -@interface ASICloudFilesRequestTests : ASITestCase { - ASINetworkQueue *networkQueue; - float progress; -} - -@property (retain,nonatomic) ASINetworkQueue *networkQueue; - -@end diff --git a/socketio/Tests/ASICloudFilesRequestTests.m b/socketio/Tests/ASICloudFilesRequestTests.m deleted file mode 100644 index d0c459d..0000000 --- a/socketio/Tests/ASICloudFilesRequestTests.m +++ /dev/null @@ -1,338 +0,0 @@ -// -// ASICloudFilesRequestTests.m -// -// Created by Michael Mayo on 1/6/10. -// - -#import "ASICloudFilesRequestTests.h" - -// models -#import "ASICloudFilesContainer.h" -#import "ASICloudFilesObject.h" - -// requests -#import "ASICloudFilesRequest.h" -#import "ASICloudFilesContainerRequest.h" -#import "ASICloudFilesObjectRequest.h" -#import "ASICloudFilesCDNRequest.h" - -// Fill in these to run the tests that actually connect and manipulate objects on Cloud Files -static NSString *username = @""; -static NSString *apiKey = @""; - -@implementation ASICloudFilesRequestTests - -@synthesize networkQueue; - -// Authenticate before any test if there's no auth token present -- (void)authenticate { - if (![ASICloudFilesRequest authToken]) { - [ASICloudFilesRequest setUsername:username]; - [ASICloudFilesRequest setApiKey:apiKey]; - [ASICloudFilesRequest authenticate]; - } -} - -// ASICloudFilesRequest -- (void)testAuthentication { - [self authenticate]; - GHAssertNotNil([ASICloudFilesRequest authToken], @"Failed to authenticate and obtain authentication token"); - GHAssertNotNil([ASICloudFilesRequest storageURL], @"Failed to authenticate and obtain storage URL"); - GHAssertNotNil([ASICloudFilesRequest cdnManagementURL], @"Failed to authenticate and obtain CDN URL"); -} - -- (void)testDateParser { - ASICloudFilesRequest *request = [[[ASICloudFilesRequest alloc] init] autorelease]; - - NSDate *date = [request dateFromString:@"invalid date string"]; - GHAssertNil(date, @"Should have failed to parse an invalid date string"); - - date = [request dateFromString:@"2009-11-04T19:46:20.192723"]; - GHAssertNotNil(date, @"Failed to parse date string"); - - NSDateComponents *components = [[[NSDateComponents alloc] init] autorelease]; - [components setYear:2009]; - [components setMonth:11]; - [components setDay:4]; - [components setHour:19]; - [components setMinute:46]; - [components setSecond:20]; - NSCalendar *calendar = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease]; - NSDate *referenceDate = [calendar dateFromComponents:components]; - - // NSDateComponents has seconds as the smallest value, so we'll just check the created date is less than 1 second different from what we expect - NSTimeInterval timeDifference = [date timeIntervalSinceDate:referenceDate]; - BOOL success = (timeDifference < 1.0); - GHAssertTrue(success, @"Parsed date incorrectly"); -} - -// ASICloudFilesContainerRequest -- (void)testAccountInfo { - [self authenticate]; - - ASICloudFilesContainerRequest *request = [ASICloudFilesContainerRequest accountInfoRequest]; - [request startSynchronous]; - - GHAssertTrue([request containerCount] > 0, @"Failed to retrieve account info"); - GHAssertTrue([request bytesUsed] > 0, @"Failed to retrieve account info"); -} - -- (void)testContainerList { - [self authenticate]; - - NSArray *containers = nil; - - ASICloudFilesContainerRequest *containerListRequest = [ASICloudFilesContainerRequest listRequest]; - [containerListRequest startSynchronous]; - - containers = [containerListRequest containers]; - GHAssertTrue([containers count] > 0, @"Failed to list containers"); - NSUInteger i; - for (i = 0; i < [containers count]; i++) { - ASICloudFilesContainer *container = [containers objectAtIndex:i]; - GHAssertNotNil(container.name, @"Failed to parse container"); - } - - ASICloudFilesContainerRequest *limitContainerListRequest = [ASICloudFilesContainerRequest listRequestWithLimit:2 marker:nil]; - [limitContainerListRequest startSynchronous]; - containers = [limitContainerListRequest containers]; - GHAssertTrue([containers count] == 2, @"Failed to limit container list"); -} - -- (void)testContainerCreate { - [self authenticate]; - - ASICloudFilesContainerRequest *createContainerRequest = [ASICloudFilesContainerRequest createContainerRequest:@"ASICloudFilesContainerTest"]; - [createContainerRequest startSynchronous]; - GHAssertTrue([createContainerRequest error] == nil, @"Failed to create container"); -} - -- (void)testContainerDelete { - [self authenticate]; - - ASICloudFilesContainerRequest *deleteContainerRequest = [ASICloudFilesContainerRequest deleteContainerRequest:@"ASICloudFilesContainerTest"]; - [deleteContainerRequest startSynchronous]; - GHAssertTrue([deleteContainerRequest error] == nil, @"Failed to delete container"); -} - -// ASICloudFilesObjectRequest -- (void)testContainerInfo { - [self authenticate]; - - // create a file first - ASICloudFilesContainerRequest *createContainerRequest = [ASICloudFilesContainerRequest createContainerRequest:@"ASICloudFilesTest"]; - [createContainerRequest startSynchronous]; - NSData *data = [@"this is a test" dataUsingEncoding:NSUTF8StringEncoding]; - ASICloudFilesObjectRequest *putRequest - = [ASICloudFilesObjectRequest putObjectRequestWithContainer:@"ASICloudFilesTest" - objectPath:@"infotestfile.txt" contentType:@"text/plain" - objectData:data metadata:nil etag:nil]; - - [putRequest startSynchronous]; - - ASICloudFilesObjectRequest *request = [ASICloudFilesObjectRequest containerInfoRequest:@"ASICloudFilesTest"]; - [request startSynchronous]; - GHAssertTrue([request containerObjectCount] > 0, @"Failed to retrieve container info"); - GHAssertTrue([request containerBytesUsed] > 0, @"Failed to retrieve container info"); -} - -- (void)testObjectInfo { - [self authenticate]; - - ASICloudFilesObjectRequest *request = [ASICloudFilesObjectRequest objectInfoRequest:@"ASICloudFilesTest" objectPath:@"infotestfile.txt"]; - [request startSynchronous]; - - ASICloudFilesObject *object = [request object]; - GHAssertNotNil(object, @"Failed to retrieve object"); - GHAssertTrue([object.metadata count] > 0, @"Failed to parse metadata"); - - GHAssertTrue([object.metadata objectForKey:@"Test"] != nil, @"Failed to parse metadata"); - -} - -- (void)testObjectList { - [self authenticate]; - - ASICloudFilesObjectRequest *objectListRequest = [ASICloudFilesObjectRequest listRequestWithContainer:@"ASICloudFilesTest"]; - [objectListRequest startSynchronous]; - - NSArray *containers = [objectListRequest objects]; - GHAssertTrue([containers count] > 0, @"Failed to list objects"); - NSUInteger i; - for (i = 0; i < [containers count]; i++) { - ASICloudFilesObject *object = [containers objectAtIndex:i]; - GHAssertNotNil(object.name, @"Failed to parse object"); - } - -} - -- (void)testGetObject { - [self authenticate]; - - ASICloudFilesObjectRequest *request = [ASICloudFilesObjectRequest getObjectRequestWithContainer:@"ASICloudFilesTest" objectPath:@"infotestfile.txt"]; - [request startSynchronous]; - - ASICloudFilesObject *object = [request object]; - GHAssertNotNil(object, @"Failed to retrieve object"); - - GHAssertNotNil(object.name, @"Failed to parse object name"); - GHAssertTrue(object.bytes > 0, @"Failed to parse object bytes"); - GHAssertNotNil(object.contentType, @"Failed to parse object content type"); - GHAssertNotNil(object.lastModified, @"Failed to parse object last modified"); - GHAssertNotNil(object.data, @"Failed to parse object data"); -} - -- (void)testPutObject { - [self authenticate]; - - ASICloudFilesContainerRequest *createContainerRequest - = [ASICloudFilesContainerRequest createContainerRequest:@"ASICloudFilesTest"]; - [createContainerRequest startSynchronous]; - - NSData *data = [@"this is a test" dataUsingEncoding:NSUTF8StringEncoding]; - - ASICloudFilesObjectRequest *putRequest - = [ASICloudFilesObjectRequest putObjectRequestWithContainer:@"ASICloudFilesTest" - objectPath:@"puttestfile.txt" contentType:@"text/plain" - objectData:data metadata:nil etag:nil]; - - [putRequest startSynchronous]; - - GHAssertNil([putRequest error], @"Failed to PUT object"); - - ASICloudFilesObjectRequest *getRequest = [ASICloudFilesObjectRequest getObjectRequestWithContainer:@"ASICloudFilesTest" objectPath:@"puttestfile.txt"]; - [getRequest startSynchronous]; - - ASICloudFilesObject *object = [getRequest object]; - NSString *string = [[NSString alloc] initWithData:object.data encoding:NSASCIIStringEncoding]; - - GHAssertNotNil(object, @"Failed to retrieve new object"); - GHAssertNotNil(object.name, @"Failed to parse object name"); - GHAssertEqualStrings(object.name, @"puttestfile.txt", @"Failed to parse object name", @"Failed to parse object name"); - GHAssertNotNil(object.data, @"Failed to parse object data"); - GHAssertEqualStrings(string, @"this is a test", @"Failed to parse object data", @"Failed to parse object data"); - - - ASICloudFilesContainerRequest *deleteContainerRequest = [ASICloudFilesContainerRequest deleteContainerRequest:@"ASICloudFilesTest"]; - [deleteContainerRequest startSynchronous]; - - // Now put the object from a file - - createContainerRequest = [ASICloudFilesContainerRequest createContainerRequest:@"ASICloudFilesTest"]; - [createContainerRequest startSynchronous]; - - NSString *filePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"cloudfile"]; - [data writeToFile:filePath atomically:NO]; - - putRequest = [ASICloudFilesObjectRequest putObjectRequestWithContainer:@"ASICloudFilesTest" objectPath:@"puttestfile.txt" contentType:@"text/plain" file:filePath metadata:nil etag:nil]; - - [putRequest startSynchronous]; - - GHAssertNil([putRequest error], @"Failed to PUT object"); - - getRequest = [ASICloudFilesObjectRequest getObjectRequestWithContainer:@"ASICloudFilesTest" objectPath:@"puttestfile.txt"]; - [getRequest startSynchronous]; - - object = [getRequest object]; - - GHAssertNotNil(object, @"Failed to retrieve new object"); - GHAssertNotNil(object.name, @"Failed to parse object name"); - GHAssertEqualStrings(object.name, @"puttestfile.txt", @"Failed to parse object name", @"Failed to parse object name"); - GHAssertNotNil(object.data, @"Failed to parse object data"); - GHAssertEqualStrings(string, @"this is a test", @"Failed to parse object data", @"Failed to parse object data"); - - [string release]; - - deleteContainerRequest = [ASICloudFilesContainerRequest deleteContainerRequest:@"ASICloudFilesTest"]; - [deleteContainerRequest startSynchronous]; -} - -- (void)testPostObject { - [self authenticate]; - - NSMutableDictionary *metadata = [[NSMutableDictionary alloc] initWithCapacity:2]; - [metadata setObject:@"test" forKey:@"Test"]; - [metadata setObject:@"test" forKey:@"ASITest"]; - - ASICloudFilesObject *object = [ASICloudFilesObject object]; - object.name = @"infotestfile.txt"; - object.metadata = metadata; - - ASICloudFilesObjectRequest *request = [ASICloudFilesObjectRequest postObjectRequestWithContainer:@"ASICloudFilesTest" object:object]; - [request startSynchronous]; - - GHAssertTrue([request responseStatusCode] == 202, @"Failed to post object metadata"); - - [metadata release]; - -} - -- (void)testDeleteObject { - [self authenticate]; - - ASICloudFilesObjectRequest *deleteRequest = [ASICloudFilesObjectRequest deleteObjectRequestWithContainer:@"ASICloudFilesTest" objectPath:@"puttestfile.txt"]; - [deleteRequest startSynchronous]; - GHAssertTrue([deleteRequest responseStatusCode] == 204, @"Failed to delete object"); -} - -#pragma mark - -#pragma mark CDN Tests - -- (void)testCDNContainerInfo { - [self authenticate]; - - ASICloudFilesCDNRequest *request = [ASICloudFilesCDNRequest containerInfoRequest:@"ASICloudFilesTest"]; - [request startSynchronous]; - - GHAssertTrue([request responseStatusCode] == 204, @"Failed to retrieve CDN container info"); - GHAssertTrue([request cdnEnabled], @"Failed to retrieve CDN container info"); - GHAssertNotNil([request cdnURI], @"Failed to retrieve CDN container info"); - GHAssertTrue([request cdnTTL] > 0, @"Failed to retrieve CDN container info"); -} - -- (void)testCDNContainerList { - [self authenticate]; - - ASICloudFilesCDNRequest *request = [ASICloudFilesCDNRequest listRequest]; - [request startSynchronous]; - - GHAssertNotNil([request containers], @"Failed to retrieve CDN container list"); -} - -- (void)testCDNContainerListWithParams { - [self authenticate]; - - ASICloudFilesCDNRequest *request = [ASICloudFilesCDNRequest listRequestWithLimit:2 marker:nil enabledOnly:YES]; - [request startSynchronous]; - - GHAssertNotNil([request containers], @"Failed to retrieve CDN container list"); - GHAssertTrue([[request containers] count] == 2, @"Failed to retrieve limited CDN container list"); -} - -- (void)testCDNPut { - [self authenticate]; - - ASICloudFilesCDNRequest *request = [ASICloudFilesCDNRequest putRequestWithContainer:@"ASICloudFilesTest"]; - [request startSynchronous]; - - GHAssertNotNil([request cdnURI], @"Failed to PUT to CDN container"); -} - -- (void)testCDNPost { - [self authenticate]; - - ASICloudFilesCDNRequest *request = [ASICloudFilesCDNRequest postRequestWithContainer:@"ASICloudFilesTest" cdnEnabled:YES ttl:86600]; - [request startSynchronous]; - - GHAssertNotNil([request cdnURI], @"Failed to POST to CDN container"); -} - -#pragma mark - -#pragma mark Memory Management - --(void)dealloc { - [networkQueue release]; - [super dealloc]; -} - -@end diff --git a/socketio/Tests/ASIDataCompressorTests.h b/socketio/Tests/ASIDataCompressorTests.h deleted file mode 100644 index 26767f8..0000000 --- a/socketio/Tests/ASIDataCompressorTests.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// ASIDataCompressorTests.h -// Mac -// -// Created by Ben Copsey on 17/08/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// - -#import -#import "ASITestCase.h" - -@interface ASIDataCompressorTests : ASITestCase { - -} - -@end diff --git a/socketio/Tests/ASIDataCompressorTests.m b/socketio/Tests/ASIDataCompressorTests.m deleted file mode 100644 index ad92e37..0000000 --- a/socketio/Tests/ASIDataCompressorTests.m +++ /dev/null @@ -1,179 +0,0 @@ -// -// ASIDataCompressorTests.m -// Mac -// -// Created by Ben Copsey on 17/08/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// -// Sadly these tests only work on Mac because of the dependency on NSTask, but I'm fairly sure this class should behave in the same way on iOS - -#import "ASIDataCompressorTests.h" -#import "ASIDataCompressor.h" -#import "ASIDataDecompressor.h" -#import "ASIHTTPRequest.h" - -@implementation ASIDataCompressorTests - -- (void)setUp -{ - NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease]; - - // Download a 1.7MB text file - NSString *filePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"story.txt"]; - if (![fileManager fileExistsAtPath:filePath] || [[[fileManager attributesOfItemAtPath:filePath error:NULL] objectForKey:NSFileSize] unsignedLongLongValue] < 1693961) { - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_hound_of_the_baskervilles.text"]]; - [request setDownloadDestinationPath:[[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"story.txt"]]; - [request startSynchronous]; - } -} - -- (void)testInflateData -{ - - NSString *originalString = [NSString stringWithContentsOfFile:[[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"story.txt"] encoding:NSUTF8StringEncoding error:NULL]; - - // Test in-memory inflate using uncompressData:error: - NSError *error = nil; - NSString *filePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"uncompressed_file.txt"]; - NSString *gzippedFilePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"uncompressed_file.txt.gz"]; - [ASIHTTPRequest removeFileAtPath:gzippedFilePath error:&error]; - if (error) { - GHFail(@"Failed to remove old file, cannot proceed with test"); - } - [originalString writeToFile:filePath atomically:NO encoding:NSUTF8StringEncoding error:&error]; - if (error) { - GHFail(@"Failed to write string, cannot proceed with test"); - } - - NSTask *task = [[[NSTask alloc] init] autorelease]; - [task setLaunchPath:@"/usr/bin/gzip"]; - [task setArguments:[NSArray arrayWithObject:filePath]]; - [task launch]; - [task waitUntilExit]; - - NSData *deflatedData = [NSData dataWithContentsOfFile:gzippedFilePath]; - - NSData *inflatedData = [ASIDataDecompressor uncompressData:deflatedData error:&error]; - if (error) { - GHFail(@"Inflate failed because %@",error); - } - - NSString *inflatedString = [[[NSString alloc] initWithBytes:[inflatedData bytes] length:[inflatedData length] encoding:NSUTF8StringEncoding] autorelease]; - - - BOOL success = [inflatedString isEqualToString:originalString]; - GHAssertTrue(success,@"inflated data is not the same as original"); - - // Test file to file inflate - NSString *inflatedFilePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"inflated_file.txt"]; - [ASIHTTPRequest removeFileAtPath:inflatedFilePath error:&error]; - if (error) { - GHFail(@"Failed to remove old file, cannot proceed with test"); - } - - if (![ASIDataDecompressor uncompressDataFromFile:gzippedFilePath toFile:inflatedFilePath error:&error]) { - GHFail(@"Inflate failed because %@",error); - } - - originalString = [NSString stringWithContentsOfFile:inflatedFilePath encoding:NSUTF8StringEncoding error:&error]; - if (error) { - GHFail(@"Failed to read the inflated data, cannot proceed with test"); - } - - success = [inflatedString isEqualToString:originalString]; - GHAssertTrue(success,@"inflated data is not the same as original"); - -} - -- (void)testDeflateData -{ - - NSString *originalString = [NSString stringWithContentsOfFile:[[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"story.txt"] encoding:NSUTF8StringEncoding error:NULL]; - - // Test in-memory deflate using compressData:error: - NSError *error = nil; - NSData *deflatedData = [ASIDataCompressor compressData:[originalString dataUsingEncoding:NSUTF8StringEncoding] error:&error]; - if (error) { - GHFail(@"Failed to deflate the data"); - } - - NSString *gzippedFilePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"uncompressed_file.txt.gz"]; - [ASIHTTPRequest removeFileAtPath:gzippedFilePath error:&error]; - if (error) { - GHFail(@"Failed to remove old file, cannot proceed with test"); - } - - [deflatedData writeToFile:gzippedFilePath options:0 error:&error]; - if (error) { - GHFail(@"Failed to write data, cannot proceed with test"); - } - - NSString *filePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"uncompressed_file.txt"]; - [ASIHTTPRequest removeFileAtPath:filePath error:&error]; - if (error) { - GHFail(@"Failed to remove old file, cannot proceed with test"); - } - - NSTask *task = [[[NSTask alloc] init] autorelease]; - [task setLaunchPath:@"/usr/bin/gzip"]; - [task setArguments:[NSArray arrayWithObjects:@"-d",gzippedFilePath,nil]]; - [task launch]; - [task waitUntilExit]; - - NSString *inflatedString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error]; - if (error) { - GHFail(@"Failed to read the inflated data, cannot proceed with test"); - } - - BOOL success = [inflatedString isEqualToString:originalString]; - GHAssertTrue(success,@"inflated data is not the same as original"); - - - // Test file to file deflate - [ASIHTTPRequest removeFileAtPath:gzippedFilePath error:&error]; - - if (![ASIDataCompressor compressDataFromFile:filePath toFile:gzippedFilePath error:&error]) { - GHFail(@"Deflate failed because %@",error); - } - [ASIHTTPRequest removeFileAtPath:filePath error:&error]; - - task = [[[NSTask alloc] init] autorelease]; - [task setLaunchPath:@"/usr/bin/gzip"]; - [task setArguments:[NSArray arrayWithObjects:@"-d",gzippedFilePath,nil]]; - [task launch]; - [task waitUntilExit]; - - inflatedString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error]; - - success = ([inflatedString isEqualToString:originalString]); - GHAssertTrue(success,@"deflate data is not the same as that generated by gzip"); - - // Test for bug https://github.com/pokeb/asi-http-request/issues/147 - [ASIHTTPRequest removeFileAtPath:gzippedFilePath error:&error]; - [ASIHTTPRequest removeFileAtPath:filePath error:&error]; - - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://spaceharvest.com/i/screen6.png"]]; - [request setDownloadDestinationPath:filePath]; - [request startSynchronous]; - - if (![ASIDataCompressor compressDataFromFile:filePath toFile:gzippedFilePath error:&error]) { - GHFail(@"Deflate failed because %@",error); - } - - unsigned long long originalFileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error] fileSize]; - [ASIHTTPRequest removeFileAtPath:filePath error:&error]; - - task = [[[NSTask alloc] init] autorelease]; - [task setLaunchPath:@"/usr/bin/gzip"]; - [task setArguments:[NSArray arrayWithObjects:@"-d",gzippedFilePath,nil]]; - [task launch]; - [task waitUntilExit]; - - unsigned long long inflatedFileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error] fileSize]; - - success = (originalFileSize == inflatedFileSize); - GHAssertTrue(success,@"inflated data is not the same size as the original"); - -} - -@end diff --git a/socketio/Tests/ASIDownloadCacheTests.h b/socketio/Tests/ASIDownloadCacheTests.h deleted file mode 100644 index 6cb7674..0000000 --- a/socketio/Tests/ASIDownloadCacheTests.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// ASIDownloadCacheTests.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 03/05/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// - -#import "ASITestCase.h" - - -@interface ASIDownloadCacheTests : ASITestCase { - NSUInteger requestsFinishedCount; - BOOL requestRedirectedWasCalled; -} - -@end diff --git a/socketio/Tests/ASIDownloadCacheTests.m b/socketio/Tests/ASIDownloadCacheTests.m deleted file mode 100644 index 7966c5f..0000000 --- a/socketio/Tests/ASIDownloadCacheTests.m +++ /dev/null @@ -1,562 +0,0 @@ -// -// ASIDownloadCacheTests.m -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 03/05/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// - -#import "ASIDownloadCacheTests.h" -#import "ASIDownloadCache.h" -#import "ASIHTTPRequest.h" - -// Stop clang complaining about undeclared selectors -@interface ASIDownloadCacheTests () -- (void)runCacheOnlyCallsRequestFinishedOnceTest; -- (void)finishCached:(ASIHTTPRequest *)request; -- (void)runRedirectTest; -@end - - -@implementation ASIDownloadCacheTests - -- (void)testDownloadCache -{ - [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy]; - [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICachePermanentlyCacheStoragePolicy]; - [[ASIDownloadCache sharedCache] setDefaultCachePolicy:ASIUseDefaultCachePolicy]; - [ASIHTTPRequest setDefaultCache:nil]; - - // Ensure a request without a download cache does not pull from the cache - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cache-away"]]; - [request startSynchronous]; - BOOL success = ![request didUseCachedResponse]; - GHAssertTrue(success,@"Used cached response when we shouldn't have"); - - // Make all requests use the cache - [ASIHTTPRequest setDefaultCache:[ASIDownloadCache sharedCache]]; - - // Check a request isn't setting didUseCachedResponse when the data is not in the cache - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cache-away"]]; - [request startSynchronous]; - success = ![request didUseCachedResponse]; - GHAssertTrue(success,@"Cached response should not have been available"); - - // Test read from the cache - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cache-away"]]; - [request startSynchronous]; - success = [request didUseCachedResponse]; - GHAssertTrue(success,@"Failed to use cached response"); - - // Test preventing reads from the cache - [[ASIDownloadCache sharedCache] setShouldRespectCacheControlHeaders:YES]; - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cache-away"]]; - [request setCachePolicy:ASIAskServerIfModifiedWhenStaleCachePolicy|ASIDoNotReadFromCacheCachePolicy]; - [request startSynchronous]; - success = ![request didUseCachedResponse]; - GHAssertTrue(success,@"Used the cache when reads were not enabled"); - - //Empty the cache - [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy]; - - // Test preventing writes to the cache - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cache-away"]]; - [request setCachePolicy:ASIAskServerIfModifiedWhenStaleCachePolicy|ASIDoNotWriteToCacheCachePolicy]; - [request startSynchronous]; - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cache-away"]]; - [request setCachePolicy:ASIAskServerIfModifiedWhenStaleCachePolicy]; - [request startSynchronous]; - success = ![request didUseCachedResponse]; - GHAssertTrue(success,@"Used cached response when the cache should have been empty"); - - // Test respecting etag - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/content-always-new"]]; - [request startSynchronous]; - success = ![request didUseCachedResponse]; - GHAssertTrue(success,@"Used cached response when we shouldn't have"); - - // Etag will be different on the second request - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/content-always-new"]]; - - // Note: we are forcing it to perform a conditional GET - [request setCachePolicy:ASIDoNotReadFromCacheCachePolicy|ASIAskServerIfModifiedCachePolicy]; - [request startSynchronous]; - success = ![request didUseCachedResponse]; - GHAssertTrue(success,@"Used cached response when we shouldn't have"); - - // Test ignoring server headers - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/no-cache"]]; - [[ASIDownloadCache sharedCache] setShouldRespectCacheControlHeaders:NO]; - [request startSynchronous]; - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/no-cache"]]; - [[ASIDownloadCache sharedCache] setShouldRespectCacheControlHeaders:NO]; - [request startSynchronous]; - - success = [request didUseCachedResponse]; - GHAssertTrue(success,@"Failed to use cached response"); - - // Test ASIOnlyLoadIfNotCachedCachePolicy - [[ASIDownloadCache sharedCache] setShouldRespectCacheControlHeaders:YES]; - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/content-always-new"]]; - [[ASIDownloadCache sharedCache] setDefaultCachePolicy:ASIOnlyLoadIfNotCachedCachePolicy]; - [request startSynchronous]; - success = [request didUseCachedResponse]; - GHAssertTrue(success,@"Failed to use cached response"); - - // Test clearing the cache - [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy]; - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cache-away"]]; - [request startSynchronous]; - success = ![request didUseCachedResponse]; - GHAssertTrue(success,@"Cached response should not have been available"); - - // Test ASIAskServerIfModifiedWhenStaleCachePolicy - [[ASIDownloadCache sharedCache] setDefaultCachePolicy:ASIUseDefaultCachePolicy]; - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/content-always-new"]]; - [request setSecondsToCache:2]; - [request startSynchronous]; - - // This request should not go to the network - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/content-always-new"]]; - [request startSynchronous]; - success = [request didUseCachedResponse]; - GHAssertTrue(success,@"Failed to use cached response"); - - [NSThread sleepForTimeInterval:2]; - - // This request will perform a conditional GET - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/content-always-new"]]; - [request setSecondsToCache:2]; - [request startSynchronous]; - success = ![request didUseCachedResponse]; - GHAssertTrue(success,@"Failed to use cached response"); - - // Test ASIFallbackToCacheIfLoadFailsCachePolicy - // Store something in the cache - [request setURL:[NSURL URLWithString:@"http://"]]; - [request setResponseHeaders:[NSDictionary dictionaryWithObject:@"test" forKey:@"test"]]; - [request setRawResponseData:(NSMutableData *)[@"test" dataUsingEncoding:NSUTF8StringEncoding]]; - [[ASIDownloadCache sharedCache] storeResponseForRequest:request maxAge:0]; - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://"]]; - [request setCachePolicy:ASIFallbackToCacheIfLoadFailsCachePolicy]; - [request startSynchronous]; - - success = [request didUseCachedResponse]; - GHAssertTrue(success,@"Failed to use cached response"); - - success = [[request responseString] isEqualToString:@"test"]; - GHAssertTrue(success,@"Failed to read cached response"); - - success = [[[request responseHeaders] valueForKey:@"test"] isEqualToString:@"test"]; - GHAssertTrue(success,@"Failed to read cached response headers"); - - // Remove the stuff from the cache, and try again - [request setURL:[NSURL URLWithString:@"http://"]]; - [[ASIDownloadCache sharedCache] removeCachedDataForRequest:request]; - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://"]]; - [request setCachePolicy:ASIFallbackToCacheIfLoadFailsCachePolicy]; - [request startSynchronous]; - - success = ![request didUseCachedResponse]; - GHAssertTrue(success,@"Request says it used a cached response, but there wasn't one to use"); - - success = ([request error] != nil); - GHAssertTrue(success,@"Request had no error set"); - - // Cache some data - NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cache-away"]; - request = [ASIHTTPRequest requestWithURL:url]; - [request startSynchronous]; - - NSString *path = [[ASIDownloadCache sharedCache] pathToStoreCachedResponseDataForRequest:request]; - success = (path != nil); - GHAssertTrue(success,@"Cache failed to store data"); - - path = [[ASIDownloadCache sharedCache] pathToStoreCachedResponseHeadersForRequest:request]; - success = (path != nil); - GHAssertTrue(success,@"Cache failed to store data"); - - // Make sure data gets removed - [[ASIDownloadCache sharedCache] removeCachedDataForURL:url]; - - path = [[ASIDownloadCache sharedCache] pathToCachedResponseDataForURL:url]; - success = (path == nil); - GHAssertTrue(success,@"Cache failed to remove data"); - - path = [[ASIDownloadCache sharedCache] pathToCachedResponseHeadersForURL:url]; - success = (path == nil); - GHAssertTrue(success,@"Cache failed to remove data"); - - // Test ASIDontLoadCachePolicy - [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy]; - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/content-always-new"]]; - [request setCachePolicy:ASIDontLoadCachePolicy]; - [request startSynchronous]; - success = ![request error]; - GHAssertTrue(success,@"Request had an error"); - success = ![request contentLength]; - GHAssertTrue(success,@"Request had a response"); -} - -- (void)testDefaultPolicy -{ - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_(abridged).txt"]]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request startSynchronous]; - BOOL success = ([request cachePolicy] == [[ASIDownloadCache sharedCache] defaultCachePolicy]); - GHAssertTrue(success,@"Failed to use the cache policy from the cache"); - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_(abridged).txt"]]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request setCachePolicy:ASIOnlyLoadIfNotCachedCachePolicy]; - [request startSynchronous]; - success = ([request cachePolicy] == ASIOnlyLoadIfNotCachedCachePolicy); - GHAssertTrue(success,@"Failed to use the cache policy from the cache"); -} - -- (void)testNoCache -{ - - // Test server no-cache headers - [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy]; - NSArray *cacheHeaders = [NSArray arrayWithObjects:@"cache-control/no-cache",@"cache-control/no-store",@"pragma/no-cache",nil]; - for (NSString *cacheType in cacheHeaders) { - NSString *url = [NSString stringWithFormat:@"http://allseeing-i.com/ASIHTTPRequest/tests/%@",cacheType]; - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:url]]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request startSynchronous]; - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:url]]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request startSynchronous]; - BOOL success = ![request didUseCachedResponse]; - GHAssertTrue(success,@"Data should not have been stored in the cache"); - } -} - -- (void)testSharedCache -{ - [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy]; - - // Make using the cache automatic - [ASIHTTPRequest setDefaultCache:[ASIDownloadCache sharedCache]]; - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_(abridged).txt"]]; - [request startSynchronous]; - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_(abridged).txt"]]; - [request startSynchronous]; - BOOL success = [request didUseCachedResponse]; - GHAssertTrue(success,@"Failed to use data cached in default cache"); - - [ASIHTTPRequest setDefaultCache:nil]; - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_(abridged).txt"]]; - [request startSynchronous]; - success = ![request didUseCachedResponse]; - GHAssertTrue(success,@"Should not have used data cached in default cache"); -} - -- (void)testExpiry -{ - [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy]; - [[ASIDownloadCache sharedCache] setDefaultCachePolicy:ASIAskServerIfModifiedCachePolicy]; - - NSArray *headers = [NSArray arrayWithObjects:@"last-modified",@"etag",@"expires",@"max-age",nil]; - for (NSString *header in headers) { - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://allseeing-i.com/ASIHTTPRequest/tests/content-always-new/%@",header]]]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request startSynchronous]; - - if ([header isEqualToString:@"last-modified"]) { - [NSThread sleepForTimeInterval:2]; - } - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://allseeing-i.com/ASIHTTPRequest/tests/content-always-new/%@",header]]]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request startSynchronous]; - BOOL success = ![request didUseCachedResponse]; - GHAssertTrue(success,@"Cached data should have expired"); - } -} - -- (void)testMaxAgeParsing -{ - [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy]; - [[ASIDownloadCache sharedCache] setDefaultCachePolicy:ASIUseDefaultCachePolicy]; - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cache-control-max-age-parsing"]]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request startSynchronous]; - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cache-control-max-age-parsing"]]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request startSynchronous]; - BOOL success = [request didUseCachedResponse]; - GHAssertTrue(success,@"Failed to use cached response"); -} - -- (void)testExtensionHandling -{ - NSArray *extensions = [ASIDownloadCache fileExtensionsToHandleAsHTML]; - for (NSString *extension in extensions) { - NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://allseeing-i.com/file.%@",extension]]; - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; - NSString *path = [[ASIDownloadCache sharedCache] pathToStoreCachedResponseDataForRequest:request]; - BOOL success = [[path pathExtension] isEqualToString:@"html"]; - GHAssertTrue(success, @"Failed to use html extension on cached path for a resource we know a webview won't be able to open locally"); - } - - NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/"]; - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; - NSString *path = [[ASIDownloadCache sharedCache] pathToStoreCachedResponseDataForRequest:request]; - BOOL success = [[path pathExtension] isEqualToString:@"html"]; - GHAssertTrue(success, @"Failed to use html extension on cached path for a url without an extension"); - - url = [NSURL URLWithString:@"http://allseeing-i.com/i/logo.png"]; - request = [ASIHTTPRequest requestWithURL:url]; - path = [[ASIDownloadCache sharedCache] pathToStoreCachedResponseDataForRequest:request]; - success = [[path pathExtension] isEqualToString:@"png"]; - GHAssertTrue(success, @"Failed to preserve file extension on cached path"); -} - -- (void)testCustomExpiry -{ - [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy]; - [[ASIDownloadCache sharedCache] setDefaultCachePolicy:ASIUseDefaultCachePolicy]; - [[ASIDownloadCache sharedCache] setShouldRespectCacheControlHeaders:YES]; - - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cache-away"]]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request setSecondsToCache:-2]; - [request startSynchronous]; - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cache-away"]]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request startSynchronous]; - - BOOL success = ![request didUseCachedResponse]; - GHAssertTrue(success,@"Cached data should have expired"); - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cache-away"]]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request setSecondsToCache:20]; - [request startSynchronous]; - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cache-away"]]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request startSynchronous]; - - success = [request didUseCachedResponse]; - GHAssertTrue(success,@"Cached data should have been used"); -} - -- (void)test304 -{ - NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_(abridged).txt"]; - - // Test default cache policy - [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy]; - [[ASIDownloadCache sharedCache] setDefaultCachePolicy:ASIUseDefaultCachePolicy]; - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request startSynchronous]; - - request = [ASIHTTPRequest requestWithURL:url]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request startSynchronous]; - BOOL success = ([request responseStatusCode] == 200); - GHAssertTrue(success,@"Failed to perform a conditional get"); - - success = [request didUseCachedResponse]; - GHAssertTrue(success,@"Cached data should have been used"); - - success = ([[request responseData] length]); - GHAssertTrue(success,@"Response was empty"); - - // Test 304 updates expiry date - url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/content_not_modified_but_expires_tomorrow"]; - request = [ASIHTTPRequest requestWithURL:url]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request startSynchronous]; - - NSTimeInterval expiryTimestamp = [[[[ASIDownloadCache sharedCache] cachedResponseHeadersForURL:url] objectForKey:@"X-ASIHTTPRequest-Expires"] doubleValue]; - - // Wait to give the expiry date a chance to change - sleep(2); - - request = [ASIHTTPRequest requestWithURL:url]; - [request setCachePolicy:ASIAskServerIfModifiedCachePolicy]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request startSynchronous]; - - success = [request didUseCachedResponse]; - GHAssertTrue(success, @"Cached data should have been used"); - - NSTimeInterval newExpiryTimestamp = [[[[ASIDownloadCache sharedCache] cachedResponseHeadersForURL:url] objectForKey:@"X-ASIHTTPRequest-Expires"] doubleValue]; - NSLog(@"%@",[request responseString]); - success = (newExpiryTimestamp > expiryTimestamp); - GHAssertTrue(success, @"Failed to update expiry timestamp on 304"); -} - -- (void)testStringEncoding -{ - [ASIHTTPRequest setDefaultCache:[ASIDownloadCache sharedCache]]; - [[ASIDownloadCache sharedCache] setShouldRespectCacheControlHeaders:NO]; - [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy]; - [[ASIDownloadCache sharedCache] setDefaultCachePolicy:ASIOnlyLoadIfNotCachedCachePolicy]; - - NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/Character-Encoding/UTF-16"]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request startSynchronous]; - BOOL success = ([request responseEncoding] == NSUnicodeStringEncoding); - GHAssertTrue(success,@"Got the wrong encoding back, cannot proceed with test"); - - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request startSynchronous]; - success = [request responseEncoding] == NSUnicodeStringEncoding; - GHAssertTrue(success,@"Failed to set the correct encoding on the cached response"); - - [ASIHTTPRequest setDefaultCache:nil]; -} - -- (void)testCookies -{ - [ASIHTTPRequest setDefaultCache:[ASIDownloadCache sharedCache]]; - [[ASIDownloadCache sharedCache] setShouldRespectCacheControlHeaders:NO]; - [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy]; - [[ASIDownloadCache sharedCache] setDefaultCachePolicy:ASIOnlyLoadIfNotCachedCachePolicy]; - - NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/set_cookie"]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request startSynchronous]; - NSArray *cookies = [request responseCookies]; - - BOOL success = ([cookies count]); - GHAssertTrue(success,@"Got no cookies back, cannot proceed with test"); - - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request startSynchronous]; - - NSUInteger i; - for (i=0; i<[cookies count]; i++) { - if (![[[cookies objectAtIndex:i] name] isEqualToString:[[[request responseCookies] objectAtIndex:i] name]]) { - GHAssertTrue(success,@"Failed to set response cookies correctly"); - return; - } - } - - [ASIHTTPRequest setDefaultCache:nil]; -} - -// Text fix for a bug where the didFinishSelector would be called twice for a cached response using ASIReloadIfDifferentCachePolicy -- (void)testCacheOnlyCallsRequestFinishedOnce -{ - // Run this request on the main thread to force delegate calls to happen synchronously - [self performSelectorOnMainThread:@selector(runCacheOnlyCallsRequestFinishedOnceTest) withObject:nil waitUntilDone:YES]; -} - -- (void)runCacheOnlyCallsRequestFinishedOnceTest -{ - [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy]; - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/i/logo.png"]]; - [request setCachePolicy:ASIUseDefaultCachePolicy]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request setDelegate:self]; - [request startSynchronous]; - - requestsFinishedCount = 0; - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/i/logo.png"]]; - [request setCachePolicy:ASIUseDefaultCachePolicy]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request setDidFinishSelector:@selector(finishCached:)]; - [request setDelegate:self]; - [request startSynchronous]; - - BOOL success = (requestsFinishedCount == 1); - GHAssertTrue(success,@"didFinishSelector called more than once"); -} - -- (void)finishCached:(ASIHTTPRequest *)request -{ - requestsFinishedCount++; -} - -- (void)testRedirect -{ - // Run this request on the main thread to force delegate calls to happen synchronously - [self performSelectorOnMainThread:@selector(runRedirectTest) withObject:nil waitUntilDone:YES]; -} - -- (void)runRedirectTest -{ - [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy]; - [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICachePermanentlyCacheStoragePolicy]; - [[ASIDownloadCache sharedCache] setDefaultCachePolicy:ASIUseDefaultCachePolicy]; - [ASIHTTPRequest setDefaultCache:[ASIDownloadCache sharedCache]]; - - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cached-redirect"]]; - [request startSynchronous]; - - BOOL success = ([[[request url] absoluteString] isEqualToString:@"http://allseeing-i.com/i/logo.png"]); - GHAssertTrue(success,@"Request did not redirect correctly, cannot proceed with test"); - - requestRedirectedWasCalled = NO; - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cached-redirect"]]; - [request setDelegate:self]; - [request startSynchronous]; - - success = ([request didUseCachedResponse]); - GHAssertTrue(success,@"Failed to cache final response"); - - GHAssertTrue(requestRedirectedWasCalled,@"Failed to call requestRedirected"); -} - -- (void)requestRedirected:(ASIHTTPRequest *)redirected -{ - requestRedirectedWasCalled = YES; -} - -- (void)request:(ASIHTTPRequest *)request willRedirectToURL:(NSURL *)newURL -{ - BOOL success = ([[newURL absoluteString] isEqualToString:@"http://allseeing-i.com/i/logo.png"]); - GHAssertTrue(success,@"Request did not redirect correctly, cannot proceed with test"); - - success = ([request didUseCachedResponse]); - GHAssertTrue(success,@"Failed to cache redirect response"); - - [request redirectToURL:newURL]; -} - -- (void)testCachedFileOverwritten -{ - // Test for https://github.com/pokeb/asi-http-request/pull/211 - // This test ensures that items in the cache are correctly overwritten when a downloadDestinationPath is set, - // and they need to be copied to the cache at the end of the request - - // This url returns different content every time - NSURL *url = [NSURL URLWithString:@"http://asi/ASIHTTPRequest/tests/random-content"]; - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request setSecondsToCache:0.5f]; - [request startSynchronous]; - - NSString *path = [[ASIDownloadCache sharedCache] pathToCachedResponseDataForURL:url]; - NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL]; - - sleep(1); - - request = [ASIHTTPRequest requestWithURL:url]; - [request setDownloadCache:[ASIDownloadCache sharedCache]]; - [request setDownloadDestinationPath:[[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"test.html"]]; - [request startSynchronous]; - - NSString *content2 = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL]; - - BOOL success = ![content isEqualToString:content2]; - GHAssertTrue(success, @"Failed to overwrite response in cache"); -} - -@end diff --git a/socketio/Tests/ASIFormDataRequestTests.h b/socketio/Tests/ASIFormDataRequestTests.h deleted file mode 100644 index cdba0f2..0000000 --- a/socketio/Tests/ASIFormDataRequestTests.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// ASIFormDataRequestTests.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 08/11/2008. -// Copyright 2008 All-Seeing Interactive. All rights reserved. -// - -#import "ASITestCase.h" - -@interface ASIFormDataRequestTests : ASITestCase { - float progress; -} - -- (void)testPostWithFileUpload; -- (void)testEmptyData; -- (void)testSubclass; -- (void)testURLEncodedPost; -- (void)testCharset; -- (void)testPUT; -- (void)testCopy; -@end diff --git a/socketio/Tests/ASIFormDataRequestTests.m b/socketio/Tests/ASIFormDataRequestTests.m deleted file mode 100644 index 5a9a9b3..0000000 --- a/socketio/Tests/ASIFormDataRequestTests.m +++ /dev/null @@ -1,291 +0,0 @@ -// -// ASIFormDataRequestTests.m -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 08/11/2008. -// Copyright 2008 All-Seeing Interactive. All rights reserved. -// - -#import "ASIFormDataRequestTests.h" -#import "ASIFormDataRequest.h" - -// Used for subclass test -@interface ASIFormDataRequestSubclass : ASIFormDataRequest {} -@end -@implementation ASIFormDataRequestSubclass; -@end - -@implementation ASIFormDataRequestTests - -- (void)testAddNilKeysAndValues -{ - ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/empty-post-value"]]; - [request setPostValue:nil forKey:@"key1"]; - [request setPostValue:@"value2" forKey:@"key2"]; - [request setData:nil forKey:@"file1"]; - [request setData:[@"hello" dataUsingEncoding:NSUTF8StringEncoding] forKey:@"file2"]; - [request startSynchronous]; - BOOL success = ([[request responseString] isEqualToString:@"key1: \r\nkey2: value2\r\nfile1: \r\nfile2: hello"]); - GHAssertTrue(success, @"Sent wrong data"); - - // Test nil key (no key or value should be sent to the server) - request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request addPostValue:@"value1" forKey:nil]; - [request addPostValue:@"value2" forKey:@"key2"]; - [request buildPostBody]; - NSString *postBody = [[[NSString alloc] initWithData:[request postBody] encoding:NSUTF8StringEncoding] autorelease]; - success = ([postBody isEqualToString:@"key2=value2"]); - GHAssertTrue(success, @"Sent wrong data"); -} - -- (void)testPostWithFileUpload -{ - NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/post"]; - - //Create a 32kb file - unsigned int size = 1024*32; - NSMutableData *data = [NSMutableData dataWithLength:size]; - NSString *path = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"bigfile"]; - [data writeToFile:path atomically:NO]; - - ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url]; - - NSDate *d = [NSDate date]; -#if TARGET_OS_IPHONE - NSValue *v = [NSValue valueWithCGRect:CGRectMake(0, 0, 200, 200)]; -#else - NSValue *v = [NSValue valueWithRect:NSMakeRect(0, 0, 200, 200)]; -#endif - [request setPostValue:@"foo" forKey:@"post_var"]; - [request setPostValue:d forKey:@"post_var2"]; - [request setPostValue:v forKey:@"post_var3"]; - [request setFile:path forKey:@"file"]; - [request startSynchronous]; - - BOOL success = ([[request responseString] isEqualToString:[NSString stringWithFormat:@"post_var: %@\r\npost_var2: %@\r\npost_var3: %@\r\nfile_name: %@\r\nfile_size: %hu\r\ncontent_type: %@",@"foo",d,v,@"bigfile",size,@"application/octet-stream"]]); - GHAssertTrue(success,@"Failed to upload the correct data (using local file)"); - - //Try the same with the raw data - request = [[[ASIFormDataRequest alloc] initWithURL:url] autorelease]; - [request setPostValue:@"foo" forKey:@"post_var"]; - [request setPostValue:d forKey:@"post_var2"]; - [request setPostValue:v forKey:@"post_var3"]; - [request setData:data forKey:@"file"]; - [request startSynchronous]; - - success = ([[request responseString] isEqualToString:[NSString stringWithFormat:@"post_var: %@\r\npost_var2: %@\r\npost_var3: %@\r\nfile_name: %@\r\nfile_size: %hu\r\ncontent_type: %@",@"foo",d,v,@"file",size,@"application/octet-stream"]]); - GHAssertTrue(success,@"Failed to upload the correct data (using NSData)"); - - //Post with custom content-type and file name - request = [[[ASIFormDataRequest alloc] initWithURL:url] autorelease]; - [request setPostValue:@"foo" forKey:@"post_var"]; - [request setPostValue:d forKey:@"post_var2"]; - [request setPostValue:v forKey:@"post_var3"]; - [request setFile:path withFileName:@"myfile" andContentType:@"text/plain" forKey:@"file"]; - [request startSynchronous]; - - success = ([[request responseString] isEqualToString:[NSString stringWithFormat:@"post_var: %@\r\npost_var2: %@\r\npost_var3: %@\r\nfile_name: %@\r\nfile_size: %hu\r\ncontent_type: %@",@"foo",d,v,@"myfile",size,@"text/plain"]]); - GHAssertTrue(success,@"Failed to send the correct content-type / file name"); - - //Post raw data with custom content-type and file name - request = [[[ASIFormDataRequest alloc] initWithURL:url] autorelease]; - [request setPostValue:@"foo" forKey:@"post_var"]; - [request setPostValue:d forKey:@"post_var2"]; - [request setPostValue:v forKey:@"post_var3"]; - [request setData:data withFileName:@"myfile" andContentType:@"text/plain" forKey:@"file"]; - [request startSynchronous]; - - success = ([[request responseString] isEqualToString:[NSString stringWithFormat:@"post_var: %@\r\npost_var2: %@\r\npost_var3: %@\r\nfile_name: %@\r\nfile_size: %hu\r\ncontent_type: %@",@"foo",d,v,@"myfile",size,@"text/plain"]]); - GHAssertTrue(success,@"Failed to send the correct content-type / file name"); - -} - -// Test fix for bug where setting an empty string for a form post value would cause the rest of the post body to be ignored (because an NSOutputStream won't like it if you try to write 0 bytes) -- (void)testEmptyData -{ - ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/post-empty"]]; - [request setPostValue:@"hello" forKey:@"a_non_empty_string"]; - [request setPostValue:@"" forKey:@"zzz_empty_string"]; - [request setPostValue:@"there" forKey:@"xxx_non_empty_string"]; - [request setShouldStreamPostDataFromDisk:YES]; - [request buildPostBody]; - [request startSynchronous]; - - BOOL success = ([[request responseString] isEqualToString:@"a_non_empty_string: hello\r\nzzz_empty_string: \r\nxxx_non_empty_string: there"]); - GHAssertTrue(success,@"Failed to send the correct post data"); - -} - -// Ensure class convenience constructor returns an instance of our subclass -- (void)testSubclass -{ - ASIFormDataRequestSubclass *instance = [ASIFormDataRequestSubclass requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - BOOL success = [instance isKindOfClass:[ASIFormDataRequestSubclass class]]; - GHAssertTrue(success,@"Convenience constructor failed to return an instance of the correct class"); -} - -- (void)testURLEncodedPost -{ - ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/url-encoded-post"]]; - [request setPostValue:@"value1" forKey:@"value1"]; - [request setPostValue:@"(%20 ? =)" forKey:@"value2"]; - [request setPostValue:@"£100.00" forKey:@"value3"]; - [request setPostValue:@"" forKey:@"value4"]; - [request setPostValue:@"&??aaa=//ciaoèèè" forKey:@"teskey&aa"]; - - [request setShouldStreamPostDataFromDisk:YES]; - [request setPostFormat:ASIURLEncodedPostFormat]; - [request startSynchronous]; - - - BOOL success = ([[request responseString] isEqualToString:@"value1: value1\r\nvalue2: (%20 ? =)\r\nvalue3: £100.00\r\nvalue4: \r\nteskey&aa: &??aaa=//ciaoèèè"]); - GHAssertTrue(success,@"Failed to send the correct post data"); -} - -- (void)testCharset -{ - NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/formdata-charset"]; - NSString *testString = @"£££s don't seem to buy me many €€€s these days"; - - // Test the default (UTF-8) with a url-encoded request - NSString *charset = @"utf-8"; - ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url]; - [request setPostValue:testString forKey:@"value"]; - [request startSynchronous]; - BOOL success = ([[request responseString] isEqualToString:[NSString stringWithFormat:@"Got data in %@: %@",charset,testString]]); - GHAssertTrue(success,@"Failed to correctly encode the data"); - - // Test the default (UTF-8) with a multipart/form-data request - request = [ASIFormDataRequest requestWithURL:url]; - [request setPostValue:testString forKey:@"value"]; - [request setPostFormat:ASIMultipartFormDataPostFormat]; - [request startSynchronous]; - success = ([[request responseString] isEqualToString:[NSString stringWithFormat:@"Got data in %@: %@",charset,testString]]); - GHAssertTrue(success,@"Failed to correctly encode the data"); - - // Test a different charset - testString = @"£££s don't seem to buy me many $$$s these days"; - charset = @"iso-8859-1"; - request = [ASIFormDataRequest requestWithURL:url]; - [request setPostValue:testString forKey:@"value"]; - [request setStringEncoding:NSISOLatin1StringEncoding]; - [request startSynchronous]; - success = ([[request responseString] isEqualToString:[NSString stringWithFormat:@"Got data in %@: %@",charset,testString]]); - GHAssertTrue(success,@"Failed to correctly encode the data"); - - // And again with multipart/form-data request - request = [ASIFormDataRequest requestWithURL:url]; - [request setPostValue:testString forKey:@"value"]; - [request setPostFormat:ASIMultipartFormDataPostFormat]; - [request setStringEncoding:NSISOLatin1StringEncoding]; - [request startSynchronous]; - success = ([[request responseString] isEqualToString:[NSString stringWithFormat:@"Got data in %@: %@",charset,testString]]); - GHAssertTrue(success,@"Failed to correctly encode the data"); - - // Once more for luck - charset = @"windows-1252"; - request = [ASIFormDataRequest requestWithURL:url]; - [request setPostValue:testString forKey:@"value"]; - [request setStringEncoding:NSWindowsCP1252StringEncoding]; - [request startSynchronous]; - success = ([[request responseString] isEqualToString:[NSString stringWithFormat:@"Got data in %@: %@",charset,testString]]); - GHAssertTrue(success,@"Failed to correctly encode the data"); - - request = [ASIFormDataRequest requestWithURL:url]; - [request setPostValue:testString forKey:@"value"]; - [request setPostFormat:ASIMultipartFormDataPostFormat]; - [request setStringEncoding:NSWindowsCP1252StringEncoding]; - [request startSynchronous]; - success = ([[request responseString] isEqualToString:[NSString stringWithFormat:@"Got data in %@: %@",charset,testString]]); - GHAssertTrue(success,@"Failed to correctly encode the data"); - - // Ensure charset isn't added to file post (GH issue 36) - request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/Tests/return-raw-request"]]; - [request setData:[@"test 123" dataUsingEncoding:NSUTF8StringEncoding] forKey:@"file"]; - [request setRequestMethod:@"PUT"]; - [request startSynchronous]; - success = ([[request responseString] rangeOfString:@"charset=utf-8"].location == NSNotFound); - GHAssertTrue(success,@"Sent a charset header for an uploaded file"); - - -} - -- (void)testPUT -{ - ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/Tests/put_form_data"]]; - [request setRequestMethod:@"PUT"]; - [request setPostValue:@"cheep cheep" forKey:@"hello"]; - [request startSynchronous]; - - NSString *expectedResponse = [[[NSString alloc] initWithBytes:[[request postBody] bytes] length:[[request postBody] length] encoding:[request stringEncoding]] autorelease]; - BOOL success = ([[request responseString] isEqualToString:expectedResponse]); - GHAssertTrue(success,@"Failed to send form data using PUT"); - - // Ensure that other methods still default to POST - request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/Tests/put_form_data"]]; - [request setRequestMethod:@"DELETE"]; - [request setPostValue:@"cheep cheep" forKey:@"hello"]; - [request startSynchronous]; - - success = ([[request responseString] isEqualToString:@"Got POST instead"]); - GHAssertTrue(success,@"Failed to send form data using PUT"); -} - -- (void)testCopy -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - ASIFormDataRequest *request2 = [request copy]; - GHAssertNotNil(request2,@"Failed to create a copy"); - - [pool release]; - BOOL success = ([request2 retainCount] == 1); - GHAssertTrue(success,@"Failed to create a retained copy"); - success = ([request2 isKindOfClass:[ASIFormDataRequest class]]); - GHAssertTrue(success,@"Copy is of wrong class"); - - [request2 release]; -} - -- (void)testMultipleValuesForASingleKey -{ - ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/multiple-values"]]; - [request addPostValue:@"here" forKey:@"test_value[]"]; - [request addPostValue:@"are" forKey:@"test_value[]"]; - [request addPostValue:@"some" forKey:@"test_value[]"]; - [request addPostValue:@"values" forKey:@"test_value[]"]; - - NSString *path1 = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"file1.txt"]; - NSString *path2 = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"file2.txt"]; - [@"hello" writeToFile:path1 atomically:NO encoding:NSUTF8StringEncoding error:nil]; - [@"there" writeToFile:path2 atomically:NO encoding:NSUTF8StringEncoding error:nil]; - [request addFile:path1 forKey:@"test_file[]"]; - [request addFile:path2 forKey:@"test_file[]"]; - - [request startSynchronous]; - NSString *expectedOutput = @"here\r\nare\r\nsome\r\nvalues\r\nfile1.txt\r\nfile2.txt\r\n"; - BOOL success = [[request responseString] isEqualToString:expectedOutput]; - GHAssertTrue(success,@"Failed to send the correct data"); - - // Check data replaces older data - request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/single-values"]]; - [request addPostValue:@"here" forKey:@"test_value[]"]; - [request addPostValue:@"are" forKey:@"test_value[]"]; - [request addPostValue:@"some" forKey:@"test_value[]"]; - [request addPostValue:@"values" forKey:@"test_value[]"]; - - [request setPostValue:@"this is new data" forKey:@"test_value[]"]; - - [request addFile:path1 forKey:@"test_file[]"]; - [request addFile:path2 forKey:@"test_file[]"]; - - [request setData:[@"this is new data" dataUsingEncoding:NSUTF8StringEncoding] forKey:@"test_file[]"]; - - [request startSynchronous]; - expectedOutput = @"this is new data\r\nfile\r\n"; - success = [[request responseString] isEqualToString:expectedOutput]; - GHAssertTrue(success,@"Failed to send the correct data"); -} - -@end diff --git a/socketio/Tests/ASIHTTPRequestTests.h b/socketio/Tests/ASIHTTPRequestTests.h deleted file mode 100644 index 61c3776..0000000 --- a/socketio/Tests/ASIHTTPRequestTests.h +++ /dev/null @@ -1,64 +0,0 @@ -// -// ASIHTTPRequestTests.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 01/08/2008. -// Copyright 2008 All-Seeing Interactive. All rights reserved. -// - -#import "ASITestCase.h" - -@class ASIHTTPRequest; - -@interface ASIHTTPRequestTests : ASITestCase { - float progress; - BOOL started; - BOOL finished; - BOOL failed; - BOOL receivedResponseHeaders; - NSMutableData *responseData; -} - -- (void)testBasicDownload; -- (void)testBase64Encode; -- (void)testDelegateMethods; -- (void)testConditionalGET; -- (void)testException; -- (void)testTimeOut; -- (void)testRequestMethod; -- (void)testHTTPVersion; -- (void)testUserAgent; -- (void)testAutomaticRedirection; -- (void)test30xCrash; -- (void)testUploadContentLength; -- (void)testDownloadContentLength; -- (void)testFileDownload; -- (void)testDownloadProgress; -- (void)testUploadProgress; -- (void)testCookies; -- (void)testRemoveCredentialsFromKeychain; -- (void)testBasicAuthentication; -- (void)testDigestAuthentication; -- (void)testNTLMHandshake; -- (void)testCharacterEncoding; -- (void)testCompressedResponse; -- (void)testCompressedResponseDownloadToFile; -- (void)test000SSL; -- (void)testRedirectPreservesSession; -- (void)testTooMuchRedirection; -- (void)testRedirectToNewDomain; -- (void)test303Redirect; -- (void)testSubclass; -- (void)testTimeOutWithoutDownloadDelegate; -- (void)testThrottlingDownloadBandwidth; -- (void)testThrottlingUploadBandwidth; -#if TARGET_OS_IPHONE -- (void)testReachability; -#endif -- (void)testAutomaticRetry; -- (void)testCloseConnection; -- (void)testPersistentConnections; -- (void)testNilPortCredentialsMatching; - -@property (retain, nonatomic) NSMutableData *responseData; -@end diff --git a/socketio/Tests/ASIHTTPRequestTests.m b/socketio/Tests/ASIHTTPRequestTests.m deleted file mode 100644 index 39eafa5..0000000 --- a/socketio/Tests/ASIHTTPRequestTests.m +++ /dev/null @@ -1,2001 +0,0 @@ -// -// ASIHTTPRequestTests.m -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 01/08/2008. -// Copyright 2008 All-Seeing Interactive. All rights reserved. -// - -#import "ASIHTTPRequestTests.h" -#import "ASIHTTPRequest.h" -#import "ASINetworkQueue.h" -#import "ASIFormDataRequest.h" -#import -#import - -// Used for subclass test -@interface ASIHTTPRequestSubclass : ASIHTTPRequest {} -@end -@implementation ASIHTTPRequestSubclass; - -// For testing exceptions are caught -- (void)startRequest -{ - [[NSException exceptionWithName:@"Test Exception" reason:@"Test Reason" userInfo:nil] raise]; -} -@end - - -// Stop clang complaining about undeclared selectors -@interface ASIHTTPRequestTests () -- (void)runCancelTest; -- (void)performDelegateMethodsTest; -- (void)requestStarted:(ASIHTTPRequest *)request; -- (void)requestFinished:(ASIHTTPRequest *)request; -- (void)requestFailed:(ASIHTTPRequest *)request; -- (void)delegateTestStarted:(ASIHTTPRequest *)request; -- (void)delegateTestResponseHeaders:(ASIHTTPRequest *)request; -- (void)delegateTestFinished:(ASIHTTPRequest *)request; -- (void)delegateTestFailed:(ASIHTTPRequest *)request; -- (void)runRemoveUploadProgressTest; -- (void)runRedirectedResume; -- (void)performDownloadProgressTest; -- (void)theTestRequest:(ASIHTTPRequest *)request didReceiveData:(NSData *)data; -- (void)theTestRequestFinished:(ASIHTTPRequest *)request; -- (void)performUploadProgressTest; -- (void)performPostBodyStreamedFromDiskTest; -- (void)performPartialFetchTest; -- (void)asyncFail:(ASIHTTPRequest *)request; -- (void)asyncSuccess:(ASIHTTPRequest *)request; -- (void)request:(ASIHTTPRequest *)request isGoingToRedirectToURL:(NSURL *)url; -- (void)redirectURLTestFailed:(ASIHTTPRequest *)request; -- (void)redirectURLTestSucceeded:(ASIHTTPRequest *)request; -@end - -@implementation ASIHTTPRequestTests - -- (void)testBasicDownload -{ - NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com"]; - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; - [request startSynchronous]; - NSString *html = [request responseString]; - GHAssertNotNil(html,@"Basic synchronous request failed"); - - // Check we're getting the correct response headers - NSString *pingBackHeader = [[request responseHeaders] objectForKey:@"X-Pingback"]; - BOOL success = [pingBackHeader isEqualToString:@"http://allseeing-i.com/Ping-Back"]; - GHAssertTrue(success,@"Failed to populate response headers"); - - // Check we're getting back the correct status code - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/a-page-that-does-not-exist"] autorelease]; - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request startSynchronous]; - success = ([request responseStatusCode] == 404); - GHAssertTrue(success,@"Didn't get correct status code"); - - // Check data is as expected - NSRange notFound = NSMakeRange(NSNotFound, 0); - success = !NSEqualRanges([html rangeOfString:@"All-Seeing Interactive"],notFound); - GHAssertTrue(success,@"Failed to download the correct data"); - - // Attempt to grab from bad url - url = [[[NSURL alloc] initWithString:@""] autorelease]; - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request startSynchronous]; - success = [[request error] code] == ASIInternalErrorWhileBuildingRequestType; - GHAssertTrue(success,@"Failed to generate an error for a bad host"); - - request = [[[ASIHTTPRequest alloc] initWithURL:nil] autorelease]; - [request startSynchronous]; - success = [[request error] code] == ASIUnableToCreateRequestErrorType; - GHAssertTrue(success,@"Failed to generate an error for a bad host"); -} - -- (void)testBase64Encode -{ - NSData *data = [@"Hello, world" dataUsingEncoding:NSUTF8StringEncoding]; - NSString *base64 = [ASIHTTPRequest base64forData:data]; - BOOL success = [base64 isEqualToString:@"SGVsbG8sIHdvcmxk"]; - GHAssertTrue(success,@"Failed to encode data using base64 data correctly"); -} - -- (void)testCancel -{ - // We run this test on the main thread because otherwise we can't depend on the delegate being notified before we need to test it's working - [self performSelectorOnMainThread:@selector(runCancelTest) withObject:nil waitUntilDone:YES]; - -} - -- (void)runCancelTest -{ - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_%28abridged%29.txt"]]; - [request startAsynchronous]; - [request cancel]; - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]]; - GHAssertNotNil([request error],@"Failed to cancel the request"); - - // Test cancelling a redirected request works - // This test is probably unreliable on very slow or very fast connections, as it depends on being able to complete the first request (but not the second) in under 2 seconds - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cancel_redirect"]]; - [request startAsynchronous]; - - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]]; - [request cancel]; - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]]; - - BOOL success = ([[[request url] absoluteString] isEqualToString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel.txt"]); - - GHAssertTrue(success, @"Request did not redirect quickly enough, cannot proceed with test"); - - GHAssertNotNil([request error],@"Failed to cancel the request"); - - success = [request totalBytesRead] < 7900198; - GHAssertTrue(success, @"Downloaded the whole of the response even though we should have cancelled by now"); - - -} - - - -- (void)testDelegateMethods -{ - // We run this test on the main thread because otherwise we can't depend on the delegate being notified before we need to test it's working - [self performSelectorOnMainThread:@selector(performDelegateMethodsTest) withObject:nil waitUntilDone:YES]; -} - -- (void)performDelegateMethodsTest -{ - started = NO; - finished = NO; - failed = NO; - - // Test default delegate methods - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request setDelegate:self]; - [request startSynchronous]; - - - GHAssertTrue(started,@"Failed to call the delegate method when the request started"); - GHAssertTrue(receivedResponseHeaders,@"Failed to call the delegate method when the request started"); - GHAssertTrue(finished,@"Failed to call the delegate method when the request finished"); - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel.txt"]]; - [request setDelegate:self]; - [request setTimeOutSeconds:0.01]; - [request startSynchronous]; - - GHAssertTrue(failed,@"Failed to call the delegate method when the request failed"); - - started = NO; - finished = NO; - failed = NO; - receivedResponseHeaders = NO; - - // Test custom delegate methods - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request setDelegate:self]; - [request setDidStartSelector:@selector(delegateTestStarted:)]; - [request setDidReceiveResponseHeadersSelector:@selector(delegateTestResponseHeaders:)]; - [request setDidFinishSelector:@selector(delegateTestFinished:)]; - [request startSynchronous]; - - - GHAssertTrue(started,@"Failed to call the delegate method when the request started"); - GHAssertTrue(finished,@"Failed to call the delegate method when the request finished"); - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel.txt"]]; - [request setDidFailSelector:@selector(delegateTestFailed:)]; - [request setDelegate:self]; - [request setTimeOutSeconds:0.01]; - [request startSynchronous]; - - GHAssertTrue(failed,@"Failed to call the delegate method when the request failed"); - -} - -- (void)requestStarted:(ASIHTTPRequest *)request -{ - started = YES; -} - -- (void)request:(ASIHTTPRequest *)request didReceiveResponseHeaders:(NSDictionary *)newResponseHeaders -{ - GHAssertNotNil(newResponseHeaders,@"Called request:didReceiveResponseHeaders: when we have no headers"); - receivedResponseHeaders = YES; -} - - -- (void)requestFinished:(ASIHTTPRequest *)request -{ - finished = YES; -} - -- (void)requestFailed:(ASIHTTPRequest *)request -{ - failed = YES; -} - -- (void)delegateTestStarted:(ASIHTTPRequest *)request -{ - started = YES; -} - -- (void)delegateTestResponseHeaders:(ASIHTTPRequest *)request -{ - GHAssertNotNil([request responseHeaders],@"Called delegateTestResponseHeaders: when we have no headers"); - receivedResponseHeaders = YES; -} - -- (void)delegateTestFinished:(ASIHTTPRequest *)request -{ - finished = YES; -} - -- (void)delegateTestFailed:(ASIHTTPRequest *)request -{ - failed = YES; -} - -- (void)testConditionalGET -{ - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/i/logo.png"]]; - [request startSynchronous]; - BOOL success = ([request responseStatusCode] == 200); - GHAssertTrue(success, @"Failed to download file, cannot proceed with this test"); - success = ([[request responseData] length] > 0); - GHAssertTrue(success, @"Response length is 0, this shouldn't happen"); - - NSString *etag = [[request responseHeaders] objectForKey:@"Etag"]; - NSString *lastModified = [[request responseHeaders] objectForKey:@"Last-Modified"]; - - GHAssertNotNil(etag, @"Response didn't return an etag"); - GHAssertNotNil(lastModified, @"Response didn't return a last modified date"); - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/i/logo.png"]]; - [request addRequestHeader:@"If-Modified-Since" value:lastModified]; - [request addRequestHeader:@"If-None-Match" value:etag]; - [request startSynchronous]; - success = ([request responseStatusCode] == 304); - GHAssertTrue(success, @"Got wrong status code"); - success = ([[request responseData] length] == 0); - GHAssertTrue(success, @"Response length is not 0, this shouldn't happen"); - -} - -- (void)testException -{ - ASIHTTPRequestSubclass *request = [ASIHTTPRequestSubclass requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request startSynchronous]; - NSError *error = [request error]; - GHAssertNotNil(error,@"Failed to generate an error for an exception"); - - BOOL success = [[[error userInfo] objectForKey:NSLocalizedDescriptionKey] isEqualToString:@"Test Exception"]; - GHAssertTrue(success, @"Generated wrong error for exception"); - -} - -- (void)testCharacterEncoding -{ - - NSArray *IANAEncodings = [NSArray arrayWithObjects:@"UTF-8",@"US-ASCII",@"ISO-8859-1",@"UTF-16",nil]; - NSUInteger NSStringEncodings[] = {NSUTF8StringEncoding,NSASCIIStringEncoding,NSISOLatin1StringEncoding,NSUnicodeStringEncoding}; - - NSUInteger i; - for (i=0; i<[IANAEncodings count]; i++) { - NSURL *url = [[[NSURL alloc] initWithString:[NSString stringWithFormat:@"http://allseeing-i.com/ASIHTTPRequest/tests/Character-Encoding/%@",[IANAEncodings objectAtIndex:i]]] autorelease]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request startSynchronous]; - BOOL success = [request responseEncoding] == NSStringEncodings[i]; - GHAssertTrue(success,[NSString stringWithFormat:@"Failed to use the correct text encoding for %@i",[IANAEncodings objectAtIndex:i]]); - } - - NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/Character-Encoding/Something-else"] autorelease]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setDefaultResponseEncoding:NSWindowsCP1251StringEncoding]; - [request startSynchronous]; - BOOL success = [request responseEncoding] == [request defaultResponseEncoding]; - GHAssertTrue(success,[NSString stringWithFormat:@"Failed to use the default string encoding"]); - - // Will return a Content-Type header with charset in the middle of the value (Fix contributed by Roman Busyghin) - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/Character-Encoding/utf-16-with-type-header"] autorelease]; - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request startSynchronous]; - success = [request responseEncoding] == NSUnicodeStringEncoding; - GHAssertTrue(success,[NSString stringWithFormat:@"Failed to parse the content type header correctly"]); -} - -- (void)testTimeOut -{ - NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_%28abridged%29.txt"] autorelease]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setTimeOutSeconds:0.0001]; //It's pretty unlikely we will be able to grab the data this quickly, so the request should timeout - [request startSynchronous]; - - BOOL success = [[request error] code] == ASIRequestTimedOutErrorType; - GHAssertTrue(success,@"Timeout didn't generate the correct error"); - - [ASIHTTPRequest setDefaultTimeOutSeconds:0.0001]; - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request startSynchronous]; - - success = [[request error] code] == ASIRequestTimedOutErrorType; - GHAssertTrue(success,@"Failed to change the default timeout"); - - [ASIHTTPRequest setDefaultTimeOutSeconds:10]; -} - - -// Test fix for a bug that might have caused timeouts when posting data -- (void)testTimeOutWithoutDownloadDelegate -{ - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_%28young_readers_edition%29.txt"]]; - [request setTimeOutSeconds:5]; - [request setShowAccurateProgress:NO]; - [request setPostBody:[NSMutableData dataWithData:[@"Small Body" dataUsingEncoding:NSUTF8StringEncoding]]]; - [request startSynchronous]; - - GHAssertNil([request error],@"Generated an error (most likely a timeout) - this test might fail on high latency connections"); -} - - -- (void)testRequestMethod -{ - NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/request-method"] autorelease]; - NSArray *methods = [[[NSArray alloc] initWithObjects:@"GET",@"POST",@"PUT",@"DELETE", nil] autorelease]; - for (NSString *method in methods) { - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setRequestMethod:method]; - [request startSynchronous]; - BOOL success = [[request responseString] isEqualToString:method]; - GHAssertTrue(success,@"Failed to set the request method correctly"); - } - - // Test to ensure we don't change the request method when we have an unrecognised method already set - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setRequestMethod:@"FINK"]; - [request appendPostData:[@"King" dataUsingEncoding:NSUTF8StringEncoding]]; - [request buildPostBody]; - BOOL success = [[request requestMethod] isEqualToString:@"FINK"]; - GHAssertTrue(success,@"Erroneously changed request method"); -} - -- (void)testHTTPVersion -{ - NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/http-version"] autorelease]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request startSynchronous]; - - BOOL success = [[request responseString] isEqualToString:@"HTTP/1.1"]; - GHAssertTrue(success,@"Wrong HTTP version used (May fail when using a proxy that changes the HTTP version!)"); - - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseHTTPVersionOne:YES]; - [request startSynchronous]; - - success = [[request responseString] isEqualToString:@"HTTP/1.0"]; - GHAssertTrue(success,@"Wrong HTTP version used (May fail when using a proxy that changes the HTTP version!)"); -} - -- (void)testUserAgent -{ - // defaultUserAgentString will be nil if we haven't set a Bundle Name or Bundle Display Name - if ([ASIHTTPRequest defaultUserAgentString]) { - - // Returns the user agent it received in the response body - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/user-agent"]]; - [request startSynchronous]; - BOOL success = [[request responseString] isEqualToString:[ASIHTTPRequest defaultUserAgentString]]; - GHAssertTrue(success,@"Failed to set the correct user agent"); - } - - NSString *customUserAgent = @"Ferdinand Fuzzworth's Magic Tent of Mystery"; - - // Test specifying a custom user-agent for a single request - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/user-agent"]]; - [request addRequestHeader:@"User-Agent" value:customUserAgent]; - [request startSynchronous]; - BOOL success = [[request responseString] isEqualToString:customUserAgent]; - GHAssertTrue(success,@"Failed to set the correct user-agent for a single request"); - - // Test again using userAgent - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/user-agent"]]; - [request setUserAgent:customUserAgent]; - [request startSynchronous]; - success = [[request responseString] isEqualToString:customUserAgent]; - GHAssertTrue(success,@"Failed to set the correct user-agent for a single request"); - - // Test again to ensure user-agent not reused - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/user-agent"]]; - [request startSynchronous]; - success = ![[request responseString] isEqualToString:customUserAgent]; - GHAssertTrue(success,@"Re-used a user agent when we shouldn't have done so"); - - // Test setting a custom default user-agent string - [ASIHTTPRequest setDefaultUserAgentString:customUserAgent]; - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/user-agent"]]; - [request startSynchronous]; - success = [[request responseString] isEqualToString:customUserAgent]; - GHAssertTrue(success,@"Failed to set the correct user-agent when using a custom default"); - - [ASIHTTPRequest setDefaultUserAgentString:nil]; - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/user-agent"]]; - [request startSynchronous]; - success = ![[request responseString] isEqualToString:customUserAgent]; - GHAssertTrue(success,@"Failed to clear a custom default user-agent"); -} - -- (void)testAutomaticRedirection -{ - ASIHTTPRequest *request; - ASIFormDataRequest *request2; - BOOL success; - unsigned int i; - for (i=301; i<308; i++) { - - if (i > 304 && i < 307) { - continue; - } - NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://allseeing-i.com/ASIHTTPRequest/tests/redirect/%hi",i]]; - request = [ASIHTTPRequest requestWithURL:url]; - [request setShouldRedirect:NO]; - [request startSynchronous]; - if (i == 304) { // 304s will not contain a body, as per rfc2616. Will test 304 handling in a future test when we have etag support - continue; - } - success = [[request responseString] isEqualToString:[NSString stringWithFormat:@"Non-redirected content with %hi status code",i]]; - GHAssertTrue(success,[NSString stringWithFormat:@"Got the wrong content when not redirecting after a %hi",i]); - - request2 = [ASIFormDataRequest requestWithURL:url]; - [request2 setPostValue:@"Giant Monkey" forKey:@"lookbehindyou"]; - [request2 startSynchronous]; - - NSString *method = @"GET"; - if (i>304) { - method = @"POST"; - } - NSString *expectedString = [NSString stringWithFormat:@"Redirected as %@ after a %hi status code",method,i]; - if (i>304) { - expectedString = [NSString stringWithFormat:@"%@\r\nWatch out for the Giant Monkey!",expectedString]; - } - - success = [[request2 responseString] isEqualToString:expectedString]; - GHAssertTrue(success,[NSString stringWithFormat:@"Got the wrong content when redirecting after a %hi",i]); - - success = ([request2 responseStatusCode] == 200); - GHAssertTrue(success,@"Got the wrong status code (expected 200)"); - - } - - // Test RFC 2616 behaviour - for (i=301; i<303; i++) { - - NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://allseeing-i.com/ASIHTTPRequest/tests/redirect/%hi",i]]; - request2 = [ASIFormDataRequest requestWithURL:url]; - [request2 setPostValue:@"Giant Monkey" forKey:@"lookbehindyou"]; - [request2 setShouldUseRFC2616RedirectBehaviour:YES]; - [request2 startSynchronous]; - - success = ([request2 responseStatusCode] == 200); - GHAssertTrue(success,@"Got the wrong status code (expected 200)"); - - if (i == 303) { - success = ([request2 postLength] == 0 && ![request2 postBody] && [[request2 requestMethod] isEqualToString:@"GET"]); - GHAssertTrue(success,@"Failed to reset request to GET on 303 redirect"); - - success = [[request2 responseString] isEqualToString:[NSString stringWithFormat:@"Redirected as GET after a %hi status code",i]]; - GHAssertTrue(success,@"Failed to dump the post body on 303 redirect"); - - } else { - success = ([request2 postLength] > 0 || ![request2 postBody] || ![[request2 requestMethod] isEqualToString:@"POST"]); - GHAssertTrue(success,@"Failed to use the same request method and body for a redirect when using rfc2616 behaviour"); - - success = ([[request2 responseString] isEqualToString:[NSString stringWithFormat:@"Redirected as POST after a %hi status code\r\nWatch out for the Giant Monkey!",i]]); - GHAssertTrue(success,@"Failed to send the correct post body on redirect"); - } - } - - // Ensure the file contains only the body of the last request (after redirects) when downloading to a file - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/redirect/301"]]; - NSString *path = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"test.txt"]; - [request setDownloadDestinationPath:path]; - [request startSynchronous]; - NSString *result = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; - success = [result isEqualToString:@"Redirected as GET after a 301 status code"]; - GHAssertTrue(success,@"Failed to store just the body of the file request on redirect"); - - success = ([request originalURL] != [request url]); - GHAssertTrue(success,@"Failed to update request url on redirection"); - - success = ([[[request originalURL] absoluteString] isEqualToString:@"http://allseeing-i.com/ASIHTTPRequest/tests/redirect/301"]); - GHAssertTrue(success,@"Failed to preserve original url"); - - // Ensure user agent is preserved - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/redirect/301"]]; - [request addRequestHeader:@"User-Agent" value:@"test"]; - [request startSynchronous]; - success = ([[[request requestHeaders] objectForKey:@"User-Agent"] isEqualToString:@"test"]); - GHAssertTrue(success,@"Failed to preserve original user agent on redirect"); -} - -// Using a persistent connection for HTTP 305-307 would cause crashes on the redirect, not really sure why -// Since 305 (use proxy) wasn't properly supported anyway, 306 is unused, and clients are supposed to confirm redirects for 307, I've simply removed automatic redirect for these codes -- (void)test30xCrash -{ - int i; - for (i=305; i<308; i++) { - ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://allseeing-i.com/ASIHTTPRequest/tests/redirect/%hi",i]]]; - [request setPostValue:@"foo" forKey:@"eep"]; - [request setShouldRedirect:NO]; - [request startSynchronous]; - request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://allseeing-i.com/ASIHTTPRequest/tests/redirect/%hi",i]]]; - [request setPostValue:@"foo" forKey:@"eep"]; - [request startSynchronous]; - } -} - -- (void)testResumeChecksContentRangeHeader -{ - NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/no_resume"]; - NSString *temporaryPath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"foo.temp"]; - - [@"" writeToFile:temporaryPath atomically:NO encoding:NSUTF8StringEncoding error:NULL]; - - NSString *downloadPath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"foo.txt"]; - - // Download part of a large file that is returned after a redirect - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; - [request setTemporaryFileDownloadPath:temporaryPath]; - [request setDownloadDestinationPath:downloadPath]; - [request setAllowResumeForFileDownloads:YES]; - [request setAllowCompressedResponse:NO]; - [request setShouldAttemptPersistentConnection:NO]; - [request startAsynchronous]; - - // Cancel the request as soon as it has downloaded 64KB - while (1) { - sleep(0.5); - if ([request totalBytesRead] > 32*1024) { - [request cancel]; - break; - } - } - NSNumber *fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:temporaryPath error:NULL] objectForKey:NSFileSize]; - unsigned long long partialFileSize = [fileSize unsignedLongLongValue]; - BOOL success = (partialFileSize < 1036935); - GHAssertTrue(success,@"Downloaded whole file too quickly, cannot proceed with this test"); - - - // Resume the download - request = [ASIHTTPRequest requestWithURL:url]; - [request setTemporaryFileDownloadPath:temporaryPath]; - [request setDownloadDestinationPath:downloadPath]; - [request setAllowResumeForFileDownloads:YES]; - [request setAllowCompressedResponse:NO]; - - [request buildRequestHeaders]; - success = ([request partialDownloadSize] == partialFileSize); - GHAssertTrue(success,@"Failed to obtain correct partial dowload size"); - [request startAsynchronous]; - - while (1) { - sleep(0.5); - if ([request isFinished]) { - break; - } - } - - GHAssertNil([request error],@"Request failed, cannot proceed with this test"); - - success = (![[request responseHeaders] objectForKey:@"Content-Range"]); - GHAssertTrue(success,@"Got range header back, cannot proceed with this test"); - - NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:downloadPath error:NULL]; - fileSize = [attributes objectForKey:NSFileSize]; - success = ([fileSize intValue] == 1036935); - GHAssertTrue(success,@"Downloaded file has wrong length"); - - success = ([request partialDownloadSize] == 0); - GHAssertTrue(success,@"Failed to reset download size"); -} - - -- (void)testRedirectedResume -{ - [self performSelectorOnMainThread:@selector(runRedirectedResume) withObject:nil waitUntilDone:YES]; -} - -- (void)runRedirectedResume -{ - NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/redirect_resume"]; - NSString *temporaryPath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"foo.temp"]; - - [@"" writeToFile:temporaryPath atomically:NO encoding:NSUTF8StringEncoding error:NULL]; - - NSString *downloadPath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"foo.txt"]; - - // Download part of a large file that is returned after a redirect - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; - [request setTemporaryFileDownloadPath:temporaryPath]; - [request setDownloadDestinationPath:downloadPath]; - [request setAllowResumeForFileDownloads:YES]; - [request setAllowCompressedResponse:NO]; - [request startAsynchronous]; - - // Cancel the request as soon as it has downloaded 64KB - while (1) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]]; - if ([request totalBytesRead] > 32*1024) { - [request cancel]; - break; - } - } - NSNumber *fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:temporaryPath error:NULL] objectForKey:NSFileSize]; - unsigned long long partialFileSize = [fileSize unsignedLongLongValue]; - BOOL success = (partialFileSize < 1036935); - GHAssertTrue(success,@"Downloaded whole file too quickly, cannot proceed with this test"); - - - // Resume the download synchronously - request = [ASIHTTPRequest requestWithURL:url]; - [request setTemporaryFileDownloadPath:temporaryPath]; - [request setDownloadDestinationPath:downloadPath]; - [request setAllowResumeForFileDownloads:YES]; - [request setAllowCompressedResponse:NO]; - [request startSynchronous]; - - fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:downloadPath error:NULL] objectForKey:NSFileSize]; - success = ([fileSize intValue] == 1036935); - GHAssertTrue(success,@"Downloaded file has wrong length"); - - success = [[[request requestHeaders] objectForKey:@"Range"] isEqualToString:[NSString stringWithFormat:@"bytes=%llu-",partialFileSize]]; - GHAssertTrue(success,@"Restarted download when we should have resumed, or asked for the wrong segment of the file"); - -} - -- (void)testUploadContentLength -{ - //This url will return the contents of the Content-Length request header - NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/content-length"]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setPostBody:[NSMutableData dataWithLength:1024*32]]; - [request startSynchronous]; - - BOOL success = ([[request responseString] isEqualToString:[NSString stringWithFormat:@"%hu",(1024*32)]]); - GHAssertTrue(success,@"Sent wrong content length"); -} - -- (void)testDownloadContentLength -{ - NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/i/logo.png"] autorelease]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request startSynchronous]; - - BOOL success = ([request contentLength] == 27872); - GHAssertTrue(success,@"Got wrong content length"); -} - -- (void)testFileDownload -{ - NSString *path = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"testimage.png"]; - - NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/i/logo.png"] autorelease]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setDownloadDestinationPath:path]; - [request startSynchronous]; - -#if TARGET_OS_IPHONE - UIImage *image = [[[UIImage alloc] initWithContentsOfFile:path] autorelease]; -#else - NSImage *image = [[[NSImage alloc] initWithContentsOfFile:path] autorelease]; -#endif - - GHAssertNotNil(image,@"Failed to download data to a file"); -} - - -- (void)testCompressedResponseDownloadToFile -{ - NSString *path = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"testfile"]; - - NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/first"] autorelease]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setDownloadDestinationPath:path]; - [request startSynchronous]; - - NSString *tempPath = [request temporaryFileDownloadPath]; - GHAssertNil(tempPath,@"Failed to clean up temporary download file"); - - BOOL success = [[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL] isEqualToString:@"This is the expected content for the first string"]; - GHAssertTrue(success,@"Failed to download data to a file"); - - // Now test with inflating the response on the fly - NSError *error = nil; - [ASIHTTPRequest removeFileAtPath:path error:&error]; - if (error) { - GHFail(@"Failed to remove file, cannot proceed with test"); - } - - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setDownloadDestinationPath:path]; - [request setShouldWaitToInflateCompressedResponses:NO]; - [request startSynchronous]; - - tempPath = [request temporaryFileDownloadPath]; - GHAssertNil(tempPath,@"Failed to clean up temporary download file"); - - success = [[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL] isEqualToString:@"This is the expected content for the first string"]; - GHAssertTrue(success,@"Failed to download data to a file"); -} - -- (void)request:(ASIHTTPRequest *)request didGetMoreData:(NSData *)data -{ - [[self responseData] appendData:data]; -} - -- (void)downloadFinished:(ASIHTTPRequest *)request -{ - finished = YES; -} - -- (void)testCompressedResponseDelegateDataHandling -{ - finished = NO; - [self setResponseData:[NSMutableData data]]; - - NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_hound_of_the_baskervilles.text"] autorelease]; - - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request startSynchronous]; - - NSString *response = [request responseString]; - - // Now download again, using the delegate to handle the data - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setDelegate:self]; - [request setDidReceiveDataSelector:@selector(request:didGetMoreData:)]; - [request setDidFinishSelector:@selector(downloadFinished:)]; - [request setShouldWaitToInflateCompressedResponses:NO]; - [request startSynchronous]; - - while (!finished) { - sleep(1); - } - - NSString *delegateResponse = [[[NSString alloc] initWithBytes:[responseData bytes] length:[responseData length] encoding:[request responseEncoding]] autorelease]; - BOOL success = [delegateResponse isEqualToString:response]; - GHAssertTrue(success,@"Failed to correctly download the response using a delegate"); - - // Test again without compression - finished = NO; - [self setResponseData:[NSMutableData data]]; - - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setAllowCompressedResponse:NO]; - [request startSynchronous]; - - response = [request responseString]; - - // Now download again, using the delegate to handle the data - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setDelegate:self]; - [request setDidReceiveDataSelector:@selector(request:didGetMoreData:)]; - [request setDidFinishSelector:@selector(downloadFinished:)]; - [request setAllowCompressedResponse:NO]; - [request startSynchronous]; - - while (!finished) { - sleep(1); - } - - delegateResponse = [[[NSString alloc] initWithBytes:[responseData bytes] length:[responseData length] encoding:[request responseEncoding]] autorelease]; - success = [delegateResponse isEqualToString:response]; - GHAssertTrue(success,@"Failed to correctly download the response using a delegate"); -} - - -- (void)testDownloadProgress -{ - // We run tests that measure progress on the main thread because otherwise we can't depend on the progress delegate being notified before we need to test it's working - [self performSelectorOnMainThread:@selector(performDownloadProgressTest) withObject:nil waitUntilDone:YES]; -} - -- (void)performDownloadProgressTest -{ - progress = 0; - NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/i/logo.png"] autorelease]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setDownloadProgressDelegate:self]; - [request startSynchronous]; - - BOOL success = (progress == 1.0); - GHAssertTrue(success,@"Failed to properly increment download progress %f != 1.0",progress); - - progress = 0; - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel.txt"]]; - [request setDownloadProgressDelegate:self]; - [request startAsynchronous]; - - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]]; - - success = (progress != 1.0); - GHAssertTrue(success,@"Downloaded too quickly, cannot proceed with test"); - - success = (progress > 0); - GHAssertTrue(success,@"Either downloaded too slowly, or progress is not being correctly updated"); - - -} - -- (void)testUploadProgress -{ - // We run tests that measure progress on the main thread because otherwise we can't depend on the progress delegate being notified before we need to test it's working - [self performSelectorOnMainThread:@selector(performUploadProgressTest) withObject:nil waitUntilDone:YES]; -} - -- (void)performUploadProgressTest -{ - progress = 0; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ignore"]] autorelease]; - [request setPostBody:(NSMutableData *)[@"This is the request body" dataUsingEncoding:NSUTF8StringEncoding]]; - [request setUploadProgressDelegate:self]; - [request startSynchronous]; - - - BOOL success = (progress == 1.0); - GHAssertTrue(success,@"Failed to properly increment upload progress %f != 1.0",progress); -} - -- (void)testPostBodyStreamedFromDisk -{ - // We run tests that measure progress on the main thread because otherwise we can't depend on the progress delegate being notified before we need to test it's working - [self performSelectorOnMainThread:@selector(performPostBodyStreamedFromDiskTest) withObject:nil waitUntilDone:YES]; - -} - -- (void)performPostBodyStreamedFromDiskTest -{ - NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/print_request_body"]; - NSString *requestBody = @"This is the request body"; - NSString *requestContentPath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"testfile.txt"]; - [[requestBody dataUsingEncoding:NSUTF8StringEncoding] writeToFile:requestContentPath atomically:NO]; - - - // Test using a user-specified file as the request body (useful for PUT) - progress = 0; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setRequestMethod:@"PUT"]; - [request setShouldStreamPostDataFromDisk:YES]; - [request setUploadProgressDelegate:self]; - [request setPostBodyFilePath:requestContentPath]; - [request startSynchronous]; - - BOOL success = (progress == 1.0); - GHAssertTrue(success,@"Failed to properly increment upload progress %f != 1.0",progress); - - success = [[request responseString] isEqualToString:requestBody]; - GHAssertTrue(success,@"Failed upload the correct request body"); - - - // Test building a request body by appending data - progress = 0; - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setShouldStreamPostDataFromDisk:YES]; - [request setRequestMethod:@"PUT"]; - [request setUploadProgressDelegate:self]; - [request appendPostDataFromFile:requestContentPath]; - [request startSynchronous]; - - success = (progress == 1.0); - GHAssertTrue(success,@"Failed to properly increment upload progress %f != 1.0",progress); - - success = [[request responseString] isEqualToString:requestBody]; - GHAssertTrue(success,@"Failed upload the correct request body"); -} - -- (void)testCookies -{ - BOOL success; - - // Set setting a cookie - NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/set_cookie"] autorelease]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseCookiePersistence:YES]; - [request startSynchronous]; - NSString *html = [request responseString]; - success = [html isEqualToString:@"I have set a cookie"]; - GHAssertTrue(success,@"Failed to set a cookie"); - - // Test a cookie is stored in responseCookies - NSArray *cookies = [request responseCookies]; - GHAssertNotNil(cookies,@"Failed to store cookie data in responseCookies"); - - - // Test the cookie contains the correct data - NSHTTPCookie *cookie = nil; - BOOL foundCookie = NO; - for (cookie in cookies) { - if ([[cookie name] isEqualToString:@"ASIHTTPRequestTestCookie"]) { - foundCookie = YES; - success = [[cookie value] isEqualToString:@"This+is+the+value"]; - GHAssertTrue(success,@"Failed to store the correct value for a cookie"); - success = [[cookie domain] isEqualToString:@"allseeing-i.com"]; - GHAssertTrue(success,@"Failed to store the correct domain for a cookie"); - success = [[cookie path] isEqualToString:@"/ASIHTTPRequest/tests"]; - GHAssertTrue(success,@"Failed to store the correct path for a cookie"); - break; - } - } - GHAssertTrue(foundCookie,@"Failed store a particular cookie - can't continue with the rest of the tests"); - - if (!foundCookie) { - return; - } - // Test a cookie is presented when manually added to the request - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/read_cookie"] autorelease]; - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseCookiePersistence:NO]; - [request setRequestCookies:[NSMutableArray arrayWithObject:cookie]]; - [request startSynchronous]; - html = [request responseString]; - success = [html isEqualToString:@"I have 'This is the value' as the value of 'ASIHTTPRequestTestCookie'"]; - GHAssertTrue(success,@"Cookie not presented to the server with cookie persistence OFF"); - - // Test a cookie is presented from the persistent store - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/read_cookie"] autorelease]; - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseCookiePersistence:YES]; - [request startSynchronous]; - html = [request responseString]; - success = [html isEqualToString:@"I have 'This is the value' as the value of 'ASIHTTPRequestTestCookie'"]; - GHAssertTrue(success,@"Cookie not presented to the server with cookie persistence ON"); - - // Test removing a cookie - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/remove_cookie"] autorelease]; - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request startSynchronous]; - html = [request responseString]; - success = [html isEqualToString:@"I have removed a cookie"]; - GHAssertTrue(success,@"Failed to remove a cookie"); - - // Test making sure cookie was properly removed - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/read_cookie"] autorelease]; - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request startSynchronous]; - html = [request responseString]; - success = [html isEqualToString:@"No cookie exists"]; - GHAssertTrue(success,@"Cookie presented to the server when it should have been removed"); - - // Test setting a custom cookie works - NSDictionary *cookieProperties = [[[NSMutableDictionary alloc] init] autorelease]; - - // We'll add a line break to our cookie value to test it gets correctly encoded - [cookieProperties setValue:@"Test%0D%0AValue" forKey:NSHTTPCookieValue]; - [cookieProperties setValue:@"ASIHTTPRequestTestCookie" forKey:NSHTTPCookieName]; - [cookieProperties setValue:@"allseeing-i.com" forKey:NSHTTPCookieDomain]; - [cookieProperties setValue:[NSDate dateWithTimeIntervalSinceNow:60*60*4] forKey:NSHTTPCookieExpires]; - [cookieProperties setValue:@"/ASIHTTPRequest/tests" forKey:NSHTTPCookiePath]; - cookie = [[[NSHTTPCookie alloc] initWithProperties:cookieProperties] autorelease]; - - GHAssertNotNil(cookie,@"Failed to create a cookie - cookie value was not correctly encoded?"); - - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/read_cookie"] autorelease]; - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseCookiePersistence:NO]; - [request setRequestCookies:[NSMutableArray arrayWithObject:cookie]]; - [request startSynchronous]; - html = [request responseString]; - success = [html isEqualToString:@"I have 'Test\r\nValue' as the value of 'ASIHTTPRequestTestCookie'"]; - GHAssertTrue(success,@"Custom cookie not presented to the server with cookie persistence OFF"); - - - // Test removing all cookies works - [ASIHTTPRequest clearSession]; - NSArray *sessionCookies = [ASIHTTPRequest sessionCookies]; - success = ([sessionCookies count] == 0); - GHAssertTrue(success,@"Cookies not removed"); - - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/read_cookie"] autorelease]; - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseCookiePersistence:YES]; - [request startSynchronous]; - html = [request responseString]; - success = [html isEqualToString:@"No cookie exists"]; - GHAssertTrue(success,@"Cookie presented to the server when it should have been removed"); - - // Test fetching cookies for a relative url - fixes a problem where urls created with URLWithString:relativeToURL: wouldn't always read cookies from the persistent store - [ASIHTTPRequest clearSession]; - - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/set_cookie"] autorelease]; - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseCookiePersistence:YES]; - [request setUseSessionPersistence:NO]; - [request startSynchronous]; - - NSURL *originalURL = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/"]; - url = [NSURL URLWithString:@"read_cookie" relativeToURL:originalURL]; - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseCookiePersistence:YES]; - [request setUseSessionPersistence:NO]; - [request startSynchronous]; - html = [request responseString]; - NSLog(@"%@",html); - success = [html isEqualToString:@"I have 'This is the value' as the value of 'ASIHTTPRequestTestCookie'"]; - GHAssertTrue(success,@"Custom cookie not presented to the server with cookie persistence OFF"); - -} - -// Test fix for a crash if you tried to remove credentials that didn't exist -- (void)testRemoveCredentialsFromKeychain -{ - [ASIHTTPRequest removeCredentialsForHost:@"apple.com" port:0 protocol:@"http" realm:@"Nothing to see here"]; - [ASIHTTPRequest removeCredentialsForProxy:@"apple.com" port:0 realm:@"Nothing to see here"]; - -} - -- (void)testPreserveResponseWhenDownloadComplete -{ - NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/basic-authentication"]; - ASIHTTPRequest *request; - BOOL success; - - request = [ASIHTTPRequest requestWithURL:url]; - [request startSynchronous]; - - success = ([[request responseString] length]); - GHAssertTrue(success,@"Request removed the response body when we encountered an error, even though the download was complete"); - - NSString *downloadPath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"test.txt"]; - if ([[NSFileManager defaultManager] fileExistsAtPath:downloadPath]) { - [[NSFileManager defaultManager] removeItemAtPath:downloadPath error:NULL]; - } - - request = [ASIHTTPRequest requestWithURL:url]; - [request setDownloadDestinationPath:downloadPath]; - [request startSynchronous]; - - success = ([[[NSFileManager defaultManager] attributesOfItemAtPath:downloadPath error:NULL] fileSize]); - GHAssertTrue(success,@"Request removed or failed to copy the response to downloadDestinationPath"); -} - -- (void)testBasicAuthentication -{ - [ASIHTTPRequest removeCredentialsForHost:@"allseeing-i.com" port:0 protocol:@"http" realm:@"SECRET_STUFF"]; - [ASIHTTPRequest clearSession]; - - NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/basic-authentication"]; - ASIHTTPRequest *request; - BOOL success; - NSError *err; - - // Test authentication needed when no credentials supplied - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseKeychainPersistence:NO]; - [request startSynchronous]; - success = [[request error] code] == ASIAuthenticationErrorType; - GHAssertTrue(success,@"Failed to generate permission denied error with no credentials"); - - // Test wrong credentials supplied - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseKeychainPersistence:NO]; - [request setUsername:@"wrong"]; - [request setPassword:@"wrong"]; - [request startSynchronous]; - success = [[request error] code] == ASIAuthenticationErrorType; - GHAssertTrue(success,@"Failed to generate permission denied error with wrong credentials"); - - // Test correct credentials supplied - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseSessionPersistence:YES]; - [request setUseKeychainPersistence:YES]; - [request setShouldPresentCredentialsBeforeChallenge:NO]; - [request setUsername:@"secret_username"]; - [request setPassword:@"secret_password"]; - [request startSynchronous]; - err = [request error]; - GHAssertNil(err,@"Failed to supply correct username and password"); - - // Ensure credentials are not reused - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseSessionPersistence:NO]; - [request setUseKeychainPersistence:NO]; - [request startSynchronous]; - success = [[request error] code] == ASIAuthenticationErrorType; - GHAssertTrue(success,@"Reused credentials when we shouldn't have"); - - // Ensure credentials stored in the session are reused - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseSessionPersistence:YES]; - [request setUseKeychainPersistence:NO]; - [request startSynchronous]; - err = [request error]; - GHAssertNil(err,@"Failed to reuse credentials"); - - // Ensure new credentials are used in place of those in the session - request = [[[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/basic-authentication-new-credentials"]] autorelease]; - [request setUsername:@"secret_username_2"]; - [request setPassword:@"secret_password_2"]; - [request setUseSessionPersistence:YES]; - [request setUseKeychainPersistence:NO]; - [request startSynchronous]; - err = [request error]; - GHAssertNil(err,@"Failed to reuse credentials"); - - [ASIHTTPRequest clearSession]; - - // Ensure credentials stored in the session were wiped - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseKeychainPersistence:NO]; - [request startSynchronous]; - success = [[request error] code] == ASIAuthenticationErrorType; - GHAssertTrue(success,@"Failed to clear credentials"); - - // Ensure credentials stored in the keychain are reused - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseKeychainPersistence:YES]; - [request startSynchronous]; - err = [request error]; - GHAssertNil(err,@"Failed to use stored credentials"); - - [ASIHTTPRequest removeCredentialsForHost:@"allseeing-i.com" port:0 protocol:@"http" realm:@"SECRET_STUFF"]; - - // Ensure credentials stored in the keychain were wiped - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseKeychainPersistence:YES]; - [request setUseSessionPersistence:NO]; - [request startSynchronous]; - success = [[request error] code] == ASIAuthenticationErrorType; - GHAssertTrue(success,@"Failed to clear credentials"); - - // Tests shouldPresentCredentialsBeforeChallenge with credentials stored in the session - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseSessionPersistence:YES]; - [request startSynchronous]; - success = [request authenticationRetryCount] == 0; - GHAssertTrue(success,@"Didn't supply credentials before being asked for them when talking to the same server with shouldPresentCredentialsBeforeChallenge == YES"); - - // Ensure credentials stored in the session were not presented to the server before it asked for them - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseSessionPersistence:YES]; - [request setShouldPresentCredentialsBeforeChallenge:NO]; - [request startSynchronous]; - success = [request authenticationRetryCount] == 1; - GHAssertTrue(success,@"Supplied session credentials before being asked for them"); - - [ASIHTTPRequest clearSession]; - - // Test credentials set on the request are not sent before the server asks for them unless they are cached credentials from a previous request to this server - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseSessionPersistence:NO]; - [request setUsername:@"secret_username"]; - [request setPassword:@"secret_password"]; - [request setShouldPresentCredentialsBeforeChallenge:YES]; - [request startSynchronous]; - BOOL fail = [request authenticationRetryCount] == 0; - GHAssertFalse(fail,@"Sent Basic credentials even though request did not have kCFHTTPAuthenticationSchemeBasic set as authenticationScheme."); - - // Test basic credentials set on the request are sent before the server asks for them if we've explictly set the authentication scheme to basic - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseSessionPersistence:NO]; - [request setUsername:@"secret_username"]; - [request setPassword:@"secret_password"]; - [request setShouldPresentCredentialsBeforeChallenge:YES]; - [request setAuthenticationScheme:(NSString *)kCFHTTPAuthenticationSchemeBasic]; - [request startSynchronous]; - success = [request authenticationRetryCount] == 0; - GHAssertTrue(success,@"Didn't supply credentials before being asked for them, even though they were set on the request and shouldPresentCredentialsBeforeChallenge == YES"); - - // Test credentials set on the request aren't sent before the server asks for them - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseSessionPersistence:NO]; - [request setUsername:@"secret_username"]; - [request setPassword:@"secret_password"]; - [request setShouldPresentCredentialsBeforeChallenge:NO]; - [request startSynchronous]; - success = [request authenticationRetryCount] == 1; - GHAssertTrue(success,@"Supplied request credentials before being asked for them"); - - - // Test credentials presented before a challenge are stored in the session store - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUsername:@"secret_username"]; - [request setPassword:@"secret_password"]; - [request setShouldPresentCredentialsBeforeChallenge:YES]; - [request startSynchronous]; - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request startSynchronous]; - err = [request error]; - GHAssertNil(err,@"Failed to use stored credentials"); - - - // Ok, now let's test on a different server to sanity check that the credentials from out previous requests are not being used - url = [NSURL URLWithString:@"https://selfsigned.allseeing-i.com/ASIHTTPRequest/tests/basic-authentication"]; - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseSessionPersistence:YES]; - [request setUseKeychainPersistence:NO]; - [request setValidatesSecureCertificate:NO]; - [request startSynchronous]; - success = [[request error] code] == ASIAuthenticationErrorType; - GHAssertTrue(success,@"Reused credentials when we shouldn't have"); - -} - - - -- (void)testDigestAuthentication -{ - [ASIHTTPRequest removeCredentialsForHost:@"allseeing-i.com" port:0 protocol:@"http" realm:@"Keep out"]; - [ASIHTTPRequest clearSession]; - - NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/digest-authentication"] autorelease]; - ASIHTTPRequest *request; - BOOL success; - NSError *err; - - // Test authentication needed when no credentials supplied - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseKeychainPersistence:NO]; - [request startSynchronous]; - success = [[request error] code] == ASIAuthenticationErrorType; - GHAssertTrue(success,@"Failed to generate permission denied error with no credentials"); - - // Test wrong credentials supplied - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseKeychainPersistence:NO]; - [request setUsername:@"wrong"]; - [request setPassword:@"wrong"]; - [request startSynchronous]; - success = [[request error] code] == ASIAuthenticationErrorType; - GHAssertTrue(success,@"Failed to generate permission denied error with wrong credentials"); - - // Test correct credentials supplied - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseSessionPersistence:YES]; - [request setUseKeychainPersistence:YES]; - [request setUsername:@"secret_username"]; - [request setPassword:@"secret_password"]; - [request startSynchronous]; - err = [request error]; - GHAssertNil(err,@"Failed to supply correct username and password"); - - // Ensure credentials are not reused - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseSessionPersistence:NO]; - [request setUseKeychainPersistence:NO]; - [request startSynchronous]; - success = [[request error] code] == ASIAuthenticationErrorType; - GHAssertTrue(success,@"Reused credentials when we shouldn't have"); - - // Ensure credentials stored in the session are reused - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseSessionPersistence:YES]; - [request setUseKeychainPersistence:NO]; - [request startSynchronous]; - err = [request error]; - GHAssertNil(err,@"Failed to reuse credentials"); - - [ASIHTTPRequest clearSession]; - - // Ensure credentials stored in the session were wiped - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseKeychainPersistence:NO]; - [request startSynchronous]; - success = [[request error] code] == ASIAuthenticationErrorType; - GHAssertTrue(success,@"Failed to clear credentials"); - - // Ensure credentials stored in the keychain are reused - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseKeychainPersistence:YES]; - [request startSynchronous]; - err = [request error]; - GHAssertNil(err,@"Failed to reuse credentials"); - - [ASIHTTPRequest removeCredentialsForHost:@"allseeing-i.com" port:0 protocol:@"http" realm:@"Keep out"]; - - // Ensure credentials stored in the keychain were wiped - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseKeychainPersistence:YES]; - [request setUseSessionPersistence:NO]; - [request startSynchronous]; - success = [[request error] code] == ASIAuthenticationErrorType; - GHAssertTrue(success,@"Failed to clear credentials"); - - - // Test credentials set on the request are sent before the server asks for them - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUseSessionPersistence:YES]; - [request setShouldPresentCredentialsBeforeChallenge:YES]; - [request startSynchronous]; - success = [request authenticationRetryCount] == 0; - GHAssertTrue(success,@"Didn't supply credentials before being asked for them, even though they were set in the session and shouldPresentCredentialsBeforeChallenge == YES"); - -} - -- (void)testNTLMHandshake -{ - // This test connects to a script that masquerades as an NTLM server - // It tests that the handshake seems sane, but doesn't actually authenticate - - [ASIHTTPRequest clearSession]; - - NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/pretend-ntlm-handshake"]; - - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; - [request setUseKeychainPersistence:NO]; - [request setUseSessionPersistence:NO]; - [request startSynchronous]; - BOOL success = [[request error] code] == ASIAuthenticationErrorType; - GHAssertTrue(success,@"Failed to generate permission denied error with no credentials"); - - - request = [ASIHTTPRequest requestWithURL:url]; - [request setUseSessionPersistence:YES]; - [request setUseKeychainPersistence:NO]; - [request setUsername:@"king"]; - [request setPassword:@"fink"]; - [request setDomain:@"Castle.Kingdom"]; - [request startSynchronous]; - - GHAssertNil([request error],@"Got an error when credentials were supplied"); - - // NSProcessInfo returns a lower case string for host name, while CFNetwork will send a mixed case string for host name, so we'll compare by lowercasing everything - NSString *hostName = [[NSProcessInfo processInfo] hostName]; - NSString *expectedResponse = [[NSString stringWithFormat:@"You are %@ from %@/%@",@"king",@"Castle.Kingdom",hostName] lowercaseString]; - success = [[[request responseString] lowercaseString] isEqualToString:expectedResponse]; - GHAssertTrue(success,@"Failed to send credentials correctly? (Expected: '%@', got '%@')",expectedResponse,[[request responseString] lowercaseString]); -} - -- (void)testCompressedResponse -{ - // allseeing-i.com does not gzip png images - NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/i/logo.png"] autorelease]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request startSynchronous]; - NSString *encoding = [[request responseHeaders] objectForKey:@"Content-Encoding"]; - BOOL success = (!encoding || [encoding rangeOfString:@"gzip"].location != NSNotFound); - GHAssertTrue(success,@"Got incorrect request headers from server"); - - success = ([request rawResponseData] == [request responseData]); - GHAssertTrue(success,@"Attempted to uncompress data that was not compressed"); - - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/first"] autorelease]; - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request startSynchronous]; - success = ([request rawResponseData] != [request responseData]); - GHAssertTrue(success,@"Uncompressed data is the same as compressed data"); - - success = [[request responseString] isEqualToString:@"This is the expected content for the first string"]; - GHAssertTrue(success,@"Failed to decompress data correctly?"); - - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/first"] autorelease]; - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setShouldWaitToInflateCompressedResponses:NO]; - [request startSynchronous]; - success = ([request rawResponseData] == [request responseData]); - GHAssertTrue(success,@"Failed to populate rawResponseData with the inflated data"); -} - - -- (void)testPartialFetch -{ - // We run tests that measure progress on the main thread because otherwise we can't depend on the progress delegate being notified before we need to test it's working - [self performSelectorOnMainThread:@selector(performPartialFetchTest) withObject:nil waitUntilDone:YES]; - -} - -- (void)performPartialFetchTest -{ - NSString *downloadPath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"testfile.txt"]; - NSString *tempPath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"tempfile.txt"]; - NSString *partialContent = @"This file should be exactly 163 bytes long when encoded as UTF8, Unix line breaks with no BOM.\n"; - [partialContent writeToFile:tempPath atomically:NO encoding:NSASCIIStringEncoding error:nil]; - - progress = 0; - NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/test_partial_download.txt"] autorelease]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setDownloadDestinationPath:downloadPath]; - [request setTemporaryFileDownloadPath:tempPath]; - [request setAllowResumeForFileDownloads:YES]; - [request setAllowCompressedResponse:NO]; - [request setDownloadProgressDelegate:self]; - [request startSynchronous]; - - BOOL success = ([request contentLength] == 163-95); - GHAssertTrue(success,@"Failed to download a segment of the data"); - - NSString *content = [NSString stringWithContentsOfFile:downloadPath encoding:NSUTF8StringEncoding error:NULL]; - - NSString *newPartialContent = [content substringFromIndex:95]; - success = ([newPartialContent isEqualToString:@"This is the content we ought to be getting if we start from byte 95."]); - GHAssertTrue(success,@"Failed to append the correct data to the end of the file?"); - - success = (progress == 1.0); - GHAssertTrue(success,@"Failed to correctly display increment progress for a partial download"); -} - -// The '000' is to ensure this test runs first, as another test may connect to https://selfsigned.allseeing-i.com and accept the certificate -- (void)test000SSL -{ - NSURL *url = [NSURL URLWithString:@"https://selfsigned.allseeing-i.com"]; - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; - [request startSynchronous]; - - GHAssertNotNil([request error],@"Failed to generate an error for a self-signed certificate (Will fail on the second run in the same session!)"); - - // Just for testing the request generated a custom error description - don't do this! You should look at the domain / code of the underlyingError in your own programs. - BOOL success = ([[[request error] localizedDescription] rangeOfString:@"SSL problem"].location != NSNotFound); - GHAssertTrue(success,@"Generated the wrong error for a self signed cert"); - - // Turn off certificate validation, and try again - request = [ASIHTTPRequest requestWithURL:url]; - [request setValidatesSecureCertificate:NO]; - [request startSynchronous]; - - GHAssertNil([request error],@"Failed to accept a self-signed certificate"); -} - -- (void)testRedirectPreservesSession -{ - // Remove any old session cookies - [ASIHTTPRequest clearSession]; - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/session_redirect"]]; - [request startSynchronous]; - BOOL success = [[request responseString] isEqualToString:@"Take me to your leader"]; - GHAssertTrue(success,@"Failed to redirect preserving session cookies"); -} - -- (void)testTooMuchRedirection -{ - // This url will simply send a 302 redirect back to itself - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/one_infinite_loop"]]; - [request startSynchronous]; - GHAssertNotNil([request error],@"Failed to generate an error when redirection occurs too many times"); - BOOL success = ([[request error] code] == ASITooMuchRedirectionErrorType); - GHAssertTrue(success,@"Generated the wrong error for a redirection loop"); -} - -- (void)testRedirectToNewDomain -{ - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/redirect_to_new_domain"]]; - [request startSynchronous]; - BOOL success = [[[[request url] absoluteString] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"/"]] isEqualToString:@"http://www.apple.com"]; - GHAssertTrue(success,@"Failed to redirect to a different domain"); -} - -// Ensure request method changes to get -- (void)test303Redirect -{ - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/redirect_303"]]; - [request setRequestMethod:@"PUT"]; - [request appendPostData:[@"Fuzzy" dataUsingEncoding:NSUTF8StringEncoding]]; - [request startSynchronous]; - BOOL success = [[[request url] absoluteString] isEqualToString:@"http://allseeing-i.com/ASIHTTPRequest/tests/request-method"]; - GHAssertTrue(success,@"Failed to redirect to correct location"); - success = [[request responseString] isEqualToString:@"GET"]; - GHAssertTrue(success,@"Failed to use GET on new URL"); -} - -- (void)testCompressedBody -{ - - NSString *content = @"This is the test content. This is the test content. This is the test content. This is the test content."; - - // Test in memory compression / decompression - NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding]; - - - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/compressed_post_body"]]; - [request setRequestMethod:@"PUT"]; - [request setShouldCompressRequestBody:YES]; - [request setShouldStreamPostDataFromDisk:YES]; - [request setUploadProgressDelegate:self]; - [request appendPostData:data]; - [request startSynchronous]; - - BOOL success = ([[request responseString] isEqualToString:content]); - GHAssertTrue(success,@"Failed to compress the body, or server failed to decompress it"); - -} - - -// Ensure class convenience constructor returns an instance of our subclass -- (void)testSubclass -{ - ASIHTTPRequestSubclass *instance = [ASIHTTPRequestSubclass requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - BOOL success = [instance isKindOfClass:[ASIHTTPRequestSubclass class]]; - GHAssertTrue(success,@"Convenience constructor failed to return an instance of the correct class"); -} - - -- (void)testThrottlingDownloadBandwidth -{ - [ASIHTTPRequest setMaxBandwidthPerSecond:0]; - - // This content is around 128KB in size, and it won't be gzipped, so it should take more than 8 seconds to download at 14.5KB / second - // We'll test first without throttling - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_%28abridged%29.txt"]]; - NSDate *date = [NSDate date]; - [request startSynchronous]; - - NSTimeInterval interval =[date timeIntervalSinceNow]; - BOOL success = (interval > -7); - GHAssertTrue(success,@"Downloaded the file too slowly - either this is a bug, or your internet connection is too slow to run this test (must be able to download 128KB in less than 7 seconds, without throttling)"); - - // Now we'll test with throttling - [ASIHTTPRequest setMaxBandwidthPerSecond:ASIWWANBandwidthThrottleAmount]; - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_%28abridged%29.txt"]]; - date = [NSDate date]; - [request startSynchronous]; - - [ASIHTTPRequest setMaxBandwidthPerSecond:0]; - - interval =[date timeIntervalSinceNow]; - success = (interval < -7); - GHAssertTrue(success,@"Failed to throttle download"); - GHAssertNil([request error],@"Request generated an error - timeout?"); - -} - -- (void)testThrottlingUploadBandwidth -{ - [ASIHTTPRequest setMaxBandwidthPerSecond:0]; - - // Create a 64KB request body - NSData *data = [[[NSMutableData alloc] initWithLength:64*1024] autorelease]; - - // We'll test first without throttling - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ignore"]]; - [request appendPostData:data]; - NSDate *date = [NSDate date]; - [request startSynchronous]; - - NSTimeInterval interval =[date timeIntervalSinceNow]; - BOOL success = (interval > -3); - GHAssertTrue(success,@"Uploaded the data too slowly - either this is a bug, or your internet connection is too slow to run this test (must be able to upload 64KB in less than 3 seconds, without throttling)"); - - // Now we'll test with throttling - [ASIHTTPRequest setMaxBandwidthPerSecond:ASIWWANBandwidthThrottleAmount]; - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ignore"]]; - [request appendPostData:data]; - date = [NSDate date]; - [request startSynchronous]; - - [ASIHTTPRequest setMaxBandwidthPerSecond:0]; - - interval =[date timeIntervalSinceNow]; - success = (interval < -3); - GHAssertTrue(success,@"Failed to throttle upload"); - GHAssertNil([request error],@"Request generated an error - timeout?"); -} - - -- (void)testFetchToInvalidPath -{ - // Test gzipped content - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request setDownloadDestinationPath:@"/an/invalid/location.html"]; - [request startSynchronous]; - GHAssertNotNil([request error],@"Failed to generate an authentication when attempting to write to an invalid location"); - - //Test non-gzipped content - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/i/logo.png"]]; - [request setDownloadDestinationPath:@"/an/invalid/location.png"]; - [request startSynchronous]; - GHAssertNotNil([request error],@"Failed to generate an authentication when attempting to write to an invalid location"); -} - -- (void)testResponseStatusMessage -{ - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/the-meaning-of-life"]]; - [request startSynchronous]; - BOOL success = [[request responseStatusMessage] isEqualToString:@"HTTP/1.0 404 Not Found"]; - GHAssertTrue(success,@"Got wrong response status message"); -} - -- (void)testAsynchronous -{ - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/first"]]; - [request setTag:1]; - [request setDidFailSelector:@selector(asyncFail:)]; - [request setDidFinishSelector:@selector(asyncSuccess:)]; - [request setDelegate:self]; - [request startAsynchronous]; - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/second"]]; - [request setTag:2]; - [request setDidFailSelector:@selector(asyncFail:)]; - [request setDidFinishSelector:@selector(asyncSuccess:)]; - [request setDelegate:self]; - [request startAsynchronous]; - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/third"]]; - [request setTag:3]; - [request setDidFailSelector:@selector(asyncFail:)]; - [request setDidFinishSelector:@selector(asyncSuccess:)]; - [request setDelegate:self]; - [request startAsynchronous]; - - request = [ASIHTTPRequest requestWithURL:nil]; - [request setTag:4]; - [request setDidFailSelector:@selector(asyncFail:)]; - [request setDidFinishSelector:@selector(asyncSuccess:)]; - [request setDelegate:self]; - [request startAsynchronous]; -} - - -- (void)asyncFail:(ASIHTTPRequest *)request -{ - BOOL success = ([request tag] == 4); - GHAssertTrue(success,@"Wrong request failed"); -} - -- (void)asyncSuccess:(ASIHTTPRequest *)request -{ - BOOL success = ([request tag] != 4); - GHAssertTrue(success,@"Request succeeded when it should have failed"); - - switch ([request tag]) { - case 1: - success = [[request responseString] isEqualToString:@"This is the expected content for the first string"]; - break; - case 2: - success = [[request responseString] isEqualToString:@"This is the expected content for the second string"]; - break; - case 3: - success = [[request responseString] isEqualToString:@"This is the expected content for the third string"]; - break; - } - GHAssertTrue(success,@"Got wrong request content - very bad!"); - -} - - - -// Will be called on Mac OS -- (void)setDoubleValue:(double)newProgress; -{ - progress = (float)newProgress; -} - -// Will be called on iPhone OS -- (void)setProgress:(float)newProgress; -{ - progress = newProgress; -} - -#if TARGET_OS_IPHONE -- (void)testReachability -{ -#if REACHABILITY_20_API - NSLog(@"Using Reachability 2.0 API"); -#else - NSLog(@"Using Reachability 1.5 API"); -#endif - if ([ASIHTTPRequest isNetworkReachableViaWWAN]) { - NSLog(@"Connected via WWAN"); - } else { - NSLog(@"Not connected via WWAN"); - } -} -#endif - -- (void)testAutomaticRetry -{ - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request setTimeOutSeconds:0.001]; - [request setNumberOfTimesToRetryOnTimeout:5]; - [request startSynchronous]; - GHAssertNotNil([request error],@"Request failed to timeout, cannot proceed with test"); - BOOL success = ([request retryCount] == 5); - GHAssertTrue(success,@"Request failed to retry on timeout"); -} - -- (void)testCopy -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - ASIHTTPRequest *request2 = [request copy]; - - [pool release]; - - GHAssertNotNil(request2,@"Failed to create a copy"); - BOOL success = ([request2 retainCount] == 1); - GHAssertTrue(success,@"Failed to create a retained copy"); - - [request2 release]; -} - -- (void)testCloseConnection -{ - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/close-connection"]]; - [request startSynchronous]; - - BOOL success = ![request connectionCanBeReused]; - GHAssertTrue(success,@"Should not be able to re-use a request sent with Connection:close"); - - // Ensure we close the connection when authentication is needed - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/close-connection-auth-needed"]]; - [request startSynchronous]; - - success = ![request connectionCanBeReused]; - GHAssertTrue(success,@"Should not be able to re-use a request sent with Connection:close"); - -} - -- (void)testPersistentConnections -{ - // allseeing-i.com is configured to keep persistent connections alive for 2 seconds - - // Ensure we parse a keep-alive header - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - - BOOL success = ([request persistentConnectionTimeoutSeconds] == 60); - GHAssertTrue(success,@"Request failed to default to 60 seconds for connection timeout"); - - [request startSynchronous]; - - NSNumber *connectionId = [request connectionID]; - - success = ([request persistentConnectionTimeoutSeconds] == 2); - GHAssertTrue(success,@"Request failed to use time out set by server"); - - // Wait 3 seconds - connection should have timed out - sleep(3); - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request startSynchronous]; - - success = ([[request connectionID] intValue] != [connectionId intValue]); - GHAssertTrue(success,@"Reused a connection that should have timed out"); - - // Ensure persistent connections are turned off by default with POST/PUT and/or a request body - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request appendPostData:[@"Foo" dataUsingEncoding:NSUTF8StringEncoding]]; - [request startSynchronous]; - - success = ![request shouldAttemptPersistentConnection]; - GHAssertTrue(success,@"Used a persistent connection with a body"); - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request setRequestMethod:@"PUT"]; - [request startSynchronous]; - - success = ![request shouldAttemptPersistentConnection]; - GHAssertTrue(success,@"Used a persistent connection with PUT"); - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request setRequestMethod:@"POST"]; - [request startSynchronous]; - - success = ![request shouldAttemptPersistentConnection]; - GHAssertTrue(success,@"Used a persistent connection with POST"); - - // Ensure we can force a persistent connection - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request setRequestMethod:@"POST"]; - [request setShouldAttemptPersistentConnection:YES]; - [request startSynchronous]; - - success = [request shouldAttemptPersistentConnection]; - GHAssertTrue(success,@"Failed to use a persistent connection"); -} - -- (void)testRemoveUploadProgress -{ - [self performSelectorOnMainThread:@selector(runRemoveUploadProgressTest) withObject:nil waitUntilDone:YES]; -} - -- (void)runRemoveUploadProgressTest -{ - progress = 0; - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - NSData *data = [[[NSMutableData alloc] initWithLength:64*1024] autorelease]; - [request appendPostData:data]; - [request setRequestMethod:@"POST"]; - [request setUploadProgressDelegate:self]; - [request startSynchronous]; - - BOOL success = (progress == 1.0); - GHAssertTrue(success,@"Failed to set upload progress, cannot proceed with test"); - - [request removeUploadProgressSoFar]; - success = (progress == 0); - GHAssertTrue(success,@"Failed to set upload progress, cannot proceed with test"); -} - -- (void)testMimeType -{ - NSString *text = @"This is my content"; - NSString *filePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"testfile.txt"]; - [[text dataUsingEncoding:NSUTF8StringEncoding] writeToFile:filePath atomically:NO]; - - BOOL success = ([[ASIHTTPRequest mimeTypeForFileAtPath:filePath] isEqualToString:@"text/plain"]); - GHAssertTrue(success,@"Failed to detect the mime type for a file"); - - filePath = @"/nowhere"; - success = (![ASIHTTPRequest mimeTypeForFileAtPath:filePath]); - GHAssertTrue(success,@"Returned a mime type for a non-existent file"); - - filePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"testfile"]; - [[text dataUsingEncoding:NSUTF8StringEncoding] writeToFile:filePath atomically:NO]; - success = ([[ASIHTTPRequest mimeTypeForFileAtPath:filePath] isEqualToString:@"application/octet-stream"]); - GHAssertTrue(success,@"Failed to return the default mime type when a file has no extension"); -} - -- (void)testDelegateResponseDataHandling -{ - [self setResponseData:[NSMutableData dataWithLength:0]]; - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_%28young_readers_edition%29.txt"]]; - [request setDelegate:self]; - [request setDidReceiveDataSelector:@selector(theTestRequest:didReceiveData:)]; - [request setDidFinishSelector:@selector(theTestRequestFinished:)]; - [request startAsynchronous]; -} - -- (void)theTestRequestFinished:(ASIHTTPRequest *)request -{ - ASIHTTPRequest *request2 = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_%28young_readers_edition%29.txt"]]; - [request2 startSynchronous]; - NSString *firstResponse = [[[NSString alloc] initWithBytes:[[self responseData] bytes] length:[[self responseData] length] encoding:[request responseEncoding]] autorelease]; - BOOL success = [[request2 responseString] isEqualToString:firstResponse]; - GHAssertTrue(success,@"Failed to correctly download and store the response using a delegate"); -} - -- (void)theTestRequest:(ASIHTTPRequest *)request didReceiveData:(NSData *)data -{ - [[self responseData] appendData:data]; -} - - -- (void)testNilPortCredentialsMatching -{ - // Test for http://github.com/pokeb/asi-http-request/issues#issue/39 - [ASIHTTPRequest clearSession]; - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com:80/ASIHTTPRequest/tests/basic-authentication"]]; - [request setUsername:@"secret_username"]; - [request setPassword:@"secret_password"]; - [request startSynchronous]; - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/basic-authentication"]]; - [request startSynchronous]; - - // Now let's test the other way around - [ASIHTTPRequest clearSession]; - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com:/ASIHTTPRequest/tests/basic-authentication"]]; - [request setUsername:@"secret_username"]; - [request setPassword:@"secret_password"]; - [request startSynchronous]; - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com:80/ASIHTTPRequest/tests/basic-authentication"]]; - [request startSynchronous]; -} - - -- (void)testRFC1123DateParsing -{ - unsigned dateUnits = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit | NSWeekdayCalendarUnit; - NSCalendar *calendar = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease]; - [calendar setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; - NSString *dateString = @"Thu, 19 Nov 1981 08:52:01 GMT"; - NSDate *date = [ASIHTTPRequest dateFromRFC1123String:dateString]; - NSDateComponents *components = [calendar components:dateUnits fromDate:date]; - BOOL success = ([components year] == 1981 && [components month] == 11 && [components day] == 19 && [components weekday] == 5 && [components hour] == 8 && [components minute] == 52 && [components second] == 1); - GHAssertTrue(success,@"Failed to parse an RFC1123 date correctly"); - - dateString = @"4 May 2010 00:59 CET"; - date = [ASIHTTPRequest dateFromRFC1123String:dateString]; - components = [calendar components:dateUnits fromDate:date]; - success = ([components year] == 2010 && [components month] == 5 && [components day] == 3 && [components hour] == 23 && [components minute] == 59); - GHAssertTrue(success,@"Failed to parse an RFC1123 date correctly"); - -} - -- (void)testAccurateProgressFallback -{ - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request setAllowCompressedResponse:NO]; // A bit hacky - my server will send a chunked response (without content length) when we don't specify that we accept gzip - [request startSynchronous]; - - BOOL success = ([request showAccurateProgress] == NO); - GHAssertTrue(success,@"Request failed to fall back to simple progress"); - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/redirect_resume"]]; - [request startSynchronous]; - - success = ([request showAccurateProgress] == YES); - GHAssertTrue(success,@"Request fell back to simple progress when redirecting"); -} - -// Because of they way I implemented the server part of this test, I'm afraid you won't be able to run it yourself - -- (void)testResumeWithAutomaticTimeoutRetry -{ - printf("\nSkipping testResumeWithAutomaticTimeoutRetry - "); - return; - // Get the first part of the response - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/resume-with-timeout"]]; - [request setAllowCompressedResponse:NO]; - [request startSynchronous]; - - NSString *partialPath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"partial.txt"]; - [[request responseString] writeToFile:partialPath atomically:NO encoding:NSUTF8StringEncoding error:NULL]; - - NSString *completePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"complete.txt"]; - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/resume-with-timeout-finish"]]; - [request setAllowCompressedResponse:NO]; - [request setAllowResumeForFileDownloads:YES]; - [request setTemporaryFileDownloadPath:partialPath]; - [request setDownloadDestinationPath:completePath]; - [request setNumberOfTimesToRetryOnTimeout:1]; - [request startSynchronous]; - - NSString *expectedOutput = @""; - - char i; - for (i=0; i<3; i++) { - char *s; - s = (char *)malloc(1024*128); - memset(s, i+49, 1024*128); - expectedOutput = [expectedOutput stringByAppendingString:[[[NSString alloc] initWithBytes:s length:1024*128 encoding:NSUTF8StringEncoding] autorelease]]; - expectedOutput = [expectedOutput stringByAppendingString:@"\r\n"]; - free(s); - } - - BOOL success = [expectedOutput isEqualToString:[NSString stringWithContentsOfFile:completePath encoding:NSUTF8StringEncoding error:NULL]]; - GHAssertTrue(success, @"Failed to send the correct Range headers to the server when resuming after a timeout"); -} - -- (void)testChangeURLOnAuthenticationRetry -{ - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/basic-authentication"]]; - [request setDelegate:self]; - [request setValidatesSecureCertificate:NO]; - [request startAsynchronous]; -} - -- (void)changeURLFailed:(ASIHTTPRequest *)request -{ - GHFail(@"Request failed when changing url"); -} - -- (void)authenticationNeededForRequest:(ASIHTTPRequest *)request -{ - [request setUsername:@"foo"]; - [request setPassword:@"foo"]; - [request setDidFailSelector:@selector(changeURLFailed:)]; - [request setURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request retryUsingSuppliedCredentials]; -} - -- (void)testDelegateRedirectHandling -{ - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/redirect_to_ssl"]]; - [request setDelegate:self]; - [request setWillRedirectSelector:@selector(request:isGoingToRedirectToURL:)]; - [request setDidFailSelector:@selector(redirectURLTestFailed:)]; - [request setDidFinishSelector:@selector(redirectURLTestSucceeded:)]; - [request startAsynchronous]; -} - -- (void)redirectURLTestSucceeded:(ASIHTTPRequest *)request -{ - BOOL success = [[request url] isEqual:[NSURL URLWithString:@"http://allseeing-i.com"]]; - GHAssertTrue(success,@"Request failed to redirect to url specified by delegate"); -} - -- (void)redirectURLTestFailed:(ASIHTTPRequest *)request -{ - GHFail(@"Request failed, cannot proceed with test"); -} - -- (void)request:(ASIHTTPRequest *)request isGoingToRedirectToURL:(NSURL *)url -{ - [request redirectToURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; -} - -@synthesize responseData; -@end \ No newline at end of file diff --git a/socketio/Tests/ASINetworkQueueTests.h b/socketio/Tests/ASINetworkQueueTests.h deleted file mode 100755 index b059819..0000000 --- a/socketio/Tests/ASINetworkQueueTests.h +++ /dev/null @@ -1,83 +0,0 @@ -// -// ASINetworkQueueTests.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 08/11/2008. -// Copyright 2008 All-Seeing Interactive. All rights reserved. -// - -#import "ASITestCase.h" - -/* -IMPORTANT -Code that appears in these tests is not for general purpose use. -You should not use [networkQueue waitUntilAllOperationsAreFinished] or [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]] in your own software. -They are used here to force a queue to operate synchronously to simplify writing the tests. -IMPORTANT -*/ - -@class ASIHTTPRequest; -@class ASINetworkQueue; - -@interface ASINetworkQueueTests : ASITestCase { - ASIHTTPRequest *requestThatShouldFail; - BOOL complete; - BOOL request_didfail; - BOOL request_succeeded; - float progress; - int addedRequests; - - - NSOperationQueue *immediateCancelQueue; - NSMutableArray *failedRequests; - NSMutableArray *finishedRequests; - - ASINetworkQueue *releaseTestQueue; - ASINetworkQueue *cancelQueue; - - int authenticationPromptCount; - - ASINetworkQueue *postQueue; - - ASINetworkQueue *testNTLMQueue; - - ASINetworkQueue *addMoreRequestsQueue; - int requestsFinishedCount; - - BOOL started; - BOOL finished; - BOOL failed; - BOOL headFailed; - BOOL receivedResponseHeaders; - - int queueFinishedCallCount; -} -- (void)testFailure; -- (void)testFailureCancelsOtherRequests; -- (void)testDownloadProgress; -- (void)testUploadProgress; -- (void)testProgressWithAuthentication; -- (void)testWithNoListener; -- (void)testPartialResume; -- (void)testImmediateCancel; - -- (void)setProgress:(float)newProgress; -- (void)testSubclass; -- (void)testQueueReleaseOnRequestComplete; -- (void)testQueueReleaseOnQueueComplete; - -- (void)testMultipleDownloadsThrottlingBandwidth; -- (void)testMultipleUploadsThrottlingBandwidth; - -- (void)testDelegateAuthenticationCredentialsReuse; -- (void)testPOSTWithAuthentication; -- (void)testHEADFailure; -@property (retain) NSOperationQueue *immediateCancelQueue; -@property (retain) NSMutableArray *failedRequests; -@property (retain) NSMutableArray *finishedRequests; -@property (retain) ASINetworkQueue *releaseTestQueue; -@property (retain) ASINetworkQueue *cancelQueue; -@property (retain) ASINetworkQueue *postQueue; -@property (retain) ASINetworkQueue *testNTLMQueue; -@property (retain) ASINetworkQueue *addMoreRequestsQueue; -@end diff --git a/socketio/Tests/ASINetworkQueueTests.m b/socketio/Tests/ASINetworkQueueTests.m deleted file mode 100755 index 2fbee45..0000000 --- a/socketio/Tests/ASINetworkQueueTests.m +++ /dev/null @@ -1,1274 +0,0 @@ -// -// ASINetworkQueueTests.m -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 08/11/2008. -// Copyright 2008 All-Seeing Interactive. All rights reserved. -// - -#import "ASINetworkQueueTests.h" -#import "ASIHTTPRequest.h" -#import "ASINetworkQueue.h" -#import "ASIFormDataRequest.h" -#import -#import -/* -IMPORTANT -Code that appears in these tests is not for general purpose use. -You should not use [networkQueue waitUntilAllOperationsAreFinished] or [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]] in your own software. -They are used here to force a queue to operate synchronously to simplify writing the tests. -IMPORTANT -*/ - -// Used for subclass test -@interface ASINetworkQueueSubclass : ASINetworkQueue {} -@end -@implementation ASINetworkQueueSubclass -@end - -// Stop clang complaining about undeclared selectors -@interface ASINetworkQueueTests () -- (void)queueFinished:(ASINetworkQueue *)request; -- (void)addedRequestComplete:(ASIHTTPRequest *)request; -- (void)addAnotherRequest; -- (void)immediateCancelFail:(ASIHTTPRequest *)request; -- (void)immediateCancelFinish:(ASIHTTPRequest *)request; -- (void)finish:(ASIHTTPRequest *)request; -- (void)throttleFail:(ASIHTTPRequest *)request; -- (void)postDone:(ASIHTTPRequest *)request; -- (void)ntlmDone:(ASIHTTPRequest *)request; -- (void)ntlmFailed:(ASIHTTPRequest *)request; -- (void)runHEADFailureTest; -- (void)queueFailureFinish:(ASINetworkQueue *)request; -- (void)queueFailureFinishCallOnce:(ASINetworkQueue *)request; -- (void)request:(ASIHTTPRequest *)request isGoingToRedirectToURL:(NSURL *)url; -- (void)redirectURLTestFailed:(ASIHTTPRequest *)request; -- (void)redirectURLTestSucceeded:(ASIHTTPRequest *)request; -- (void)runDelegateMethodsTest; -- (void)delegateTestStarted:(ASIHTTPRequest *)request; -- (void)delegateTestFinished:(ASIHTTPRequest *)request; -- (void)delegateTestFailed:(ASIHTTPRequest *)request; -- (void)delegateTestRequest:(ASIHTTPRequest *)request receivedResponseHeaders:(NSDictionary *)headers; -- (void)addMoreRequestsQueueFinished:(ASINetworkQueue *)request; -- (void)requestFailedCancellingOthers:(ASINetworkQueue *)request; -- (void)fail:(ASIHTTPRequest *)request; -- (void)HEADFail:(ASIHTTPRequest *)request; -- (void)runTestQueueFinishedCalledOnFailureTest; -@end - -@implementation ASINetworkQueueTests - -- (void)testDelegateAuthenticationCredentialsReuse -{ - complete = NO; - authenticationPromptCount = 0; - - ASINetworkQueue *networkQueue = [ASINetworkQueue queue]; - [networkQueue setDelegate:self]; - [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; - - NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"reuse" forKey:@"test"]; - - int i; - for (i=0; i<5; i++) { - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/basic-authentication"]]; - [request setUserInfo:userInfo]; - [networkQueue addOperation:request]; - } - [networkQueue go]; - - while (!complete) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } -} - - -- (void)testDelegateMethods -{ - [self performSelectorOnMainThread:@selector(runDelegateMethodsTest) withObject:nil waitUntilDone:YES]; -} - -- (void)runDelegateMethodsTest -{ - started = NO; - finished = NO; - failed = NO; - - ASINetworkQueue *networkQueue = [ASINetworkQueue queue]; - [networkQueue setDelegate:self]; - [networkQueue setRequestDidStartSelector:@selector(delegateTestStarted:)]; - [networkQueue setRequestDidFinishSelector:@selector(delegateTestFinished:)]; - [networkQueue setRequestDidReceiveResponseHeadersSelector:@selector(delegateTestRequest:receivedResponseHeaders:)]; - - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [networkQueue addOperation:request]; - [networkQueue go]; - - [networkQueue waitUntilAllOperationsAreFinished]; - - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]]; - - GHAssertTrue(started,@"Failed to call the delegate method when the request started"); - GHAssertTrue(receivedResponseHeaders,@"Failed to call the delegate method when the request received response headers"); - GHAssertTrue(finished,@"Failed to call the delegate method when the request finished"); - - networkQueue = [ASINetworkQueue queue]; - [networkQueue setDelegate:self]; - [networkQueue setRequestDidFailSelector:@selector(delegateTestFailed:)]; - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_%28abridged%29.txt"]]; - [request setTimeOutSeconds:0.01]; - [networkQueue addOperation:request]; - [networkQueue go]; - - [networkQueue waitUntilAllOperationsAreFinished]; - - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]]; - - GHAssertTrue(failed,@"Failed to call the delegate method when the request failed"); - -} - -- (void)delegateTestStarted:(ASIHTTPRequest *)request -{ - started = YES; -} - -- (void)delegateTestRequest:(ASIHTTPRequest *)request receivedResponseHeaders:(NSDictionary *)responseHeaders -{ - GHAssertNotNil(responseHeaders,@"Called delegateTestResponseHeaders: when we have no headers"); - receivedResponseHeaders = YES; -} - -- (void)delegateTestFinished:(ASIHTTPRequest *)request -{ - finished = YES; -} - -- (void)delegateTestFailed:(ASIHTTPRequest *)request -{ - failed = YES; -} - - -- (void)testDownloadProgress -{ - complete = NO; - progress = 0; - - ASINetworkQueue *networkQueue = [ASINetworkQueue queue]; - [networkQueue setDownloadProgressDelegate:self]; - [networkQueue setDelegate:self]; - [networkQueue setShowAccurateProgress:NO]; - [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; - - int i; - for (i=0; i<5; i++) { - NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/i/logo.png"] autorelease]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [networkQueue addOperation:request]; - } - [networkQueue go]; - - while (!complete) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } - - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; - BOOL success = (progress == 1.0); - GHAssertTrue(success,@"Failed to increment progress properly"); - - - //Now test again with accurate progress - complete = NO; - progress = 0; - [networkQueue cancelAllOperations]; - [networkQueue setShowAccurateProgress:YES]; - - for (i=0; i<5; i++) { - NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/i/logo.png"] autorelease]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [networkQueue addOperation:request]; - } - [networkQueue go]; - - while (!complete) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } - - success = (progress == 1.0); - GHAssertTrue(success,@"Failed to increment progress properly"); - -} - -- (void)testAccurateProgressFallsBackToSimpleProgress -{ - - ASINetworkQueue *networkQueue = [ASINetworkQueue queue]; - [networkQueue setDownloadProgressDelegate:self]; - [networkQueue setDelegate:self]; - [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; - - // Test accurate progress falls back to simpler progress when responses have no content-length header - complete = NO; - progress = 0; - [networkQueue setShowAccurateProgress:YES]; - - int i; - for (i=0; i<5; i++) { - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request setAllowCompressedResponse:NO]; // A bit hacky - my server will send a chunked response (without content length) when we don't specify that we accept gzip - [networkQueue addOperation:request]; - } - [networkQueue go]; - - while (!complete) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } - - BOOL success = (progress == 1.0); - GHAssertTrue(success,@"Failed to increment progress properly"); - - [networkQueue reset]; - [networkQueue setDownloadProgressDelegate:self]; - [networkQueue setDelegate:self]; - [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; - - // This test will request gzipped content, but the content-length header we get on the HEAD request will be wrong, ASIHTTPRequest should fall back to simple progress - // This is to workaround an issue Apache has with HEAD requests for dynamically generated content when accepting gzip - it returns the content-length of a gzipped empty body - complete = NO; - progress = 0; - [networkQueue setShowAccurateProgress:YES]; - - for (i=0; i<5; i++) { - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [networkQueue addOperation:request]; - } - [networkQueue go]; - - while (!complete) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } - - success = (progress == 1.0); - GHAssertTrue(success,@"Failed to increment progress properly"); -} - -- (void)testAddingRequestsToQueueWhileInProgress -{ - [[self addMoreRequestsQueue] reset]; - [self setAddMoreRequestsQueue:[ASINetworkQueue queue]]; - [[self addMoreRequestsQueue] setDownloadProgressDelegate:self]; - [[self addMoreRequestsQueue] setDelegate:self]; - [[self addMoreRequestsQueue] setShowAccurateProgress:YES]; - [[self addMoreRequestsQueue]setQueueDidFinishSelector:@selector(addMoreRequestsQueueFinished:)]; - - requestsFinishedCount = 0; - - complete = NO; - progress = 0; - - int i; - for (i=0; i<5; i++) { - NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_(abridged).txt"] autorelease]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setDelegate:self]; - [request setDidFinishSelector:@selector(addedRequestComplete:)]; - [[self addMoreRequestsQueue] addOperation:request]; - } - [[self addMoreRequestsQueue] go]; - - // Add another request to the queue each second for 5 seconds - addedRequests = 0; - for (i=0; i<5; i++) { - [self performSelector:@selector(addAnotherRequest) withObject:nil afterDelay:i]; - } - - while (addedRequests < 5) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; - } - - // Must wait or subsequent tests will reset our progress - [[self addMoreRequestsQueue] waitUntilAllOperationsAreFinished]; -} - -- (void)addAnotherRequest -{ - addedRequests++; - NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_(abridged).txt"] autorelease]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setDelegate:self]; - [request setDidFinishSelector:@selector(addedRequestComplete:)]; - [[self addMoreRequestsQueue] addOperation:request]; -} - -- (void)addedRequestComplete:(ASIHTTPRequest *)request -{ - requestsFinishedCount++; -} - -- (void)addMoreRequestsQueueFinished:(ASINetworkQueue *)queue -{ - // This might get called multiple times if the queue finishes before all the requests can be added - // So we'll make sure they're all done first - while (requestsFinishedCount < 10) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]; - } - - BOOL success = (progress == 1.0); - GHAssertTrue(success,@"Failed to increment progress properly"); - - // The file we downloaded 10 times is 130050 bytes long - success = ([queue totalBytesToDownload] == 130050*10); - GHAssertTrue(success,@"Failed to increment total download size properly"); -} - - -- (void)uploadFailed:(ASIHTTPRequest *)request -{ - GHFail(@"Failed to upload some data, cannot continue with this test"); -} - -- (void)testUploadProgress -{ - complete = NO; - progress = 0; - - ASINetworkQueue *networkQueue = [[[ASINetworkQueue alloc] init] autorelease]; - [networkQueue setUploadProgressDelegate:self]; - [networkQueue setDelegate:self]; - [networkQueue setShowAccurateProgress:NO]; - [networkQueue setRequestDidFailSelector:@selector(uploadFailed:)]; - [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; - - NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ignore"]; - - int fileSizes[3] = {16,64,257}; - int i; - for (i=0; i<3; i++) { - NSData *data = [[[NSMutableData alloc] initWithLength:fileSizes[i]*1024] autorelease]; - NSString *path = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:[NSString stringWithFormat:@"file%hi",i]]; - [data writeToFile:path atomically:NO]; - ASIFormDataRequest *request = [[[ASIFormDataRequest alloc] initWithURL:url] autorelease]; - [request setFile:path forKey:@"file"]; - [networkQueue addOperation:request]; - } - - [networkQueue go]; - - while (!complete) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; - BOOL success = (progress == 1.0f); - GHAssertTrue(success,@"Failed to increment progress properly"); - - //Now test again with accurate progress - complete = NO; - progress = 0; - [networkQueue reset]; - [networkQueue setUploadProgressDelegate:self]; - [networkQueue setDelegate:self]; - [networkQueue setShowAccurateProgress:NO]; - [networkQueue setRequestDidFailSelector:@selector(uploadFailed:)]; - [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; - [networkQueue setShowAccurateProgress:YES]; - - for (i=0; i<3; i++) { - NSData *data = [[[NSMutableData alloc] initWithLength:fileSizes[i]*1024] autorelease]; - NSString *path = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:[NSString stringWithFormat:@"file%hi",i]]; - [data writeToFile:path atomically:NO]; - ASIFormDataRequest *request = [[[ASIFormDataRequest alloc] initWithURL:url] autorelease]; - [request setFile:path forKey:@"file"]; - [networkQueue addOperation:request]; - } - - [networkQueue go]; - - while (!complete) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; - success = (progress == 1.0f); - GHAssertTrue(success,@"Failed to increment progress properly"); - -} - -// Will be called on Mac OS -- (void)setDoubleValue:(double)newProgress; -{ - progress = (float)newProgress; -} - -// Will be called on iPhone OS -- (void)setProgress:(float)newProgress; -{ - progress = newProgress; -} - - -- (void)testFailure -{ - complete = NO; - - ASINetworkQueue *networkQueue = [ASINetworkQueue queue]; - [networkQueue setDelegate:self]; - [networkQueue setRequestDidFailSelector:@selector(requestFailed:)]; - [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; - [networkQueue setShouldCancelAllRequestsOnFailure:NO]; - - NSURL *url; - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/first"] autorelease]; - ASIHTTPRequest *request1 = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [networkQueue addOperation:request1]; - - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/second"] autorelease]; - ASIHTTPRequest *request2 = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [networkQueue addOperation:request2]; - - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/third"] autorelease]; - ASIHTTPRequest *request3 = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [networkQueue addOperation:request3]; - - url = [[[NSURL alloc] initWithString:@""] autorelease]; - requestThatShouldFail = [[ASIHTTPRequest alloc] initWithURL:url]; - [networkQueue addOperation:requestThatShouldFail]; - - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/broken"] autorelease]; - ASIHTTPRequest *request5 = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [networkQueue addOperation:request5]; - - [networkQueue go]; - - [networkQueue waitUntilAllOperationsAreFinished]; - - - BOOL success; - success = ([request1 error] == nil); - GHAssertTrue(success,@"Request 1 failed"); - - success = [[request1 responseString] isEqualToString:@"This is the expected content for the first string"]; - GHAssertTrue(success,@"Failed to download the correct data for request 1"); - - success = ([request2 error] == nil); - GHAssertTrue(success,@"Request 2 failed"); - - success = [[request2 responseString] isEqualToString:@"This is the expected content for the second string"]; - GHAssertTrue(success,@"Failed to download the correct data for request 2"); - - success = ([request3 error] == nil); - GHAssertTrue(success,@"Request 3 failed"); - - success = [[request3 responseString] isEqualToString:@"This is the expected content for the third string"]; - GHAssertTrue(success,@"Failed to download the correct data for request 3"); - - success = ([requestThatShouldFail error] != nil); - GHAssertTrue(success,@"Request 4 succeed when it should have failed"); - - success = ([request5 error] == nil); - GHAssertTrue(success,@"Request 5 failed"); - - success = ([request5 responseStatusCode] == 404); - GHAssertTrue(success,@"Failed to obtain the correct status code for request 5"); - - [requestThatShouldFail release]; - -} - - -- (void)testFailureCancelsOtherRequests -{ - complete = NO; - - ASINetworkQueue *networkQueue = [ASINetworkQueue queue]; - [networkQueue setDelegate:self]; - [networkQueue setRequestDidFailSelector:@selector(requestFailedCancellingOthers:)]; - [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; - - NSURL *url; - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/first"] autorelease]; - ASIHTTPRequest *request1 = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [networkQueue addOperation:request1]; - - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/second"] autorelease]; - ASIHTTPRequest *request2 = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [networkQueue addOperation:request2]; - - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/third"] autorelease]; - ASIHTTPRequest *request3 = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [networkQueue addOperation:request3]; - - url = [[[NSURL alloc] initWithString:@""] autorelease]; - requestThatShouldFail = [[ASIHTTPRequest alloc] initWithURL:url]; - [networkQueue addOperation:requestThatShouldFail]; - - [networkQueue go]; - - [networkQueue waitUntilAllOperationsAreFinished]; - - - [requestThatShouldFail release]; -} - - -- (void)requestFailedCancellingOthers:(ASIHTTPRequest *)request -{ - complete = YES; -} - -- (void)requestFailed:(ASIHTTPRequest *)request -{ - BOOL success = (request == requestThatShouldFail); - GHAssertTrue(success,@"Wrong request failed"); -} - -- (void)queueFinished:(ASINetworkQueue *)queue -{ - complete = YES; -} - -- (void)testProgressWithAuthentication -{ - complete = NO; - progress = 0; - - // Make sure we don't re-use credentials from previous tests - [ASIHTTPRequest clearSession]; - - ASINetworkQueue *networkQueue = [ASINetworkQueue queue]; - [networkQueue setDownloadProgressDelegate:self]; - [networkQueue setDelegate:self]; - [networkQueue setShowAccurateProgress:YES]; - [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; - [networkQueue setRequestDidFailSelector:@selector(requestFailedCancellingOthers:)]; - - NSURL *url; - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/basic-authentication"] autorelease]; - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUserInfo:[NSDictionary dictionaryWithObject:@"Don't bother" forKey:@"Shall I return any credentials?"]]; - [networkQueue addOperation:request]; - - [networkQueue go]; - - - while (!complete) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } - - NSError *error = [request error]; - GHAssertNotNil(error,@"The HEAD request failed, but it didn't tell the main request to fail"); - - complete = NO; - progress = 0; - networkQueue = [ASINetworkQueue queue]; - [networkQueue setDownloadProgressDelegate:self]; - [networkQueue setDelegate:self]; - [networkQueue setShowAccurateProgress:YES]; - [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; - [networkQueue setRequestDidFailSelector:@selector(requestFailed:)]; - - request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; - [request setUserInfo:[NSDictionary dictionaryWithObject:@"Don't bother" forKey:@"Shall I return any credentials?"]]; - [request setUsername:@"secret_username"]; - [request setPassword:@"secret_password"]; - [networkQueue addOperation:request]; - - [networkQueue go]; - - while (!complete) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } - - error = [request error]; - GHAssertNil(error,@"Failed to use authentication in a queue"); - -} - -- (void)testDelegateAuthentication -{ - complete = NO; - ASINetworkQueue *networkQueue = [ASINetworkQueue queue]; - [networkQueue setDelegate:self]; - [networkQueue setRequestDidFinishSelector:@selector(queueFinished:)]; - - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/basic-authentication"]]; - [networkQueue addOperation:request]; - - [networkQueue go]; - - while (!complete) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } - - NSError *error = [request error]; - GHAssertNil(error,@"Request failed"); -} - - -- (void)authenticationNeededForRequest:(ASIHTTPRequest *)request -{ - // We're using this method in multiple tests, so the code here is to act appropriatly for each one - - if ([[[request userInfo] objectForKey:@"test"] isEqualToString:@"ntlm"]) { - authenticationPromptCount++; - if (authenticationPromptCount == 5) { - [request setUsername:@"king"]; - [request setPassword:@"crown"]; - [request setDomain:@"CASTLE.KINGDOM"]; - } - [request retryUsingSuppliedCredentials]; - - } else if ([[[request userInfo] objectForKey:@"test"] isEqualToString:@"reuse"]) { - authenticationPromptCount++; - BOOL success = (authenticationPromptCount == 1); - GHAssertTrue(success,@"Delegate was asked for credentials more than once"); - - [request setUsername:@"secret_username"]; - [request setPassword:@"secret_password"]; - [request retryUsingSuppliedCredentials]; - - } else if ([[[request userInfo] objectForKey:@"test"] isEqualToString:@"delegate-auth-failure"]) { - authenticationPromptCount++; - if (authenticationPromptCount == 5) { - [request setUsername:@"secret_username"]; - [request setPassword:@"secret_password"]; - } else { - [request setUsername:@"wrong_username"]; - [request setPassword:@"wrong_password"]; - } - [request retryUsingSuppliedCredentials]; - - - // testProgressWithAuthentication will set a userInfo dictionary on the main request, to tell us not to supply credentials - } else if (![request mainRequest] || ![[request mainRequest] userInfo]) { - [request setUsername:@"secret_username"]; - [request setPassword:@"secret_password"]; - [request retryUsingSuppliedCredentials]; - } else { - [request cancelAuthentication]; - } -} - -- (void)requestFailedExpectedly:(ASIHTTPRequest *)request -{ - request_didfail = YES; - BOOL success = (request == requestThatShouldFail); - GHAssertTrue(success,@"Wrong request failed"); -} - -- (void)requestSucceededUnexpectedly:(ASIHTTPRequest *)request -{ - request_succeeded = YES; -} - -//Connect to a port the server isn't listening on, and the read stream won't be created (Test + Fix contributed by Michael Krause) -- (void)testWithNoListener -{ - request_succeeded = NO; - request_didfail = NO; - ASINetworkQueue *networkQueue = [ASINetworkQueue queue]; - [networkQueue setDownloadProgressDelegate:self]; - [networkQueue setDelegate:self]; - [networkQueue setShowAccurateProgress:YES]; - [networkQueue setRequestDidFailSelector:@selector(requestFailedExpectedly:)]; - [networkQueue setRequestDidFinishSelector:@selector(requestSucceededUnexpectedly:)]; - [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; - - NSURL *url; - url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com:9999/i/logo.png"] autorelease]; - requestThatShouldFail = [[ASIHTTPRequest alloc] initWithURL:url]; - [networkQueue addOperation:requestThatShouldFail]; - - [networkQueue go]; - [networkQueue waitUntilAllOperationsAreFinished]; - - // Give the queue time to notify us - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; - - // This test may fail if you are using a proxy and it returns a page when you try to connect to a bad port. - GHAssertTrue(!request_succeeded && request_didfail,@"Request to resource without listener succeeded but should have failed (May fail with proxy!)"); - -} - -- (void)testPartialResume -{ - complete = NO; - progress = 0; - - NSString *temporaryPath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"the_great_american_novel_%28young_readers_edition%29.txt.download"]; - if ([[NSFileManager defaultManager] fileExistsAtPath:temporaryPath]) { - [[NSFileManager defaultManager] removeItemAtPath:temporaryPath error:nil]; - } - - NSString *downloadPath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"the_great_american_novel_%28young_readers_edition%29.txt"]; - if ([[NSFileManager defaultManager] fileExistsAtPath:downloadPath]) { - [[NSFileManager defaultManager] removeItemAtPath:downloadPath error:nil]; - } - - NSURL *downloadURL = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_%28young_readers_edition%29.txt"]; - ASINetworkQueue *networkQueue = [ASINetworkQueue queue]; - [networkQueue setShouldCancelAllRequestsOnFailure:NO]; - - ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:downloadURL] autorelease]; - [request setDownloadDestinationPath:downloadPath]; - [request setTemporaryFileDownloadPath:temporaryPath]; - [request setAllowResumeForFileDownloads:YES]; - [networkQueue addOperation:request]; - [networkQueue go]; - - // Run until we have received a bit of data - while (1) { - usleep(250000); - if ([request error] || [request contentLength]) { - break; - } - } - - // Ok, let's tell the queue to stop - [networkQueue reset]; - [networkQueue setDownloadProgressDelegate:self]; - [networkQueue setShowAccurateProgress:YES]; - [networkQueue setDelegate:self]; - [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; - - complete = NO; - progress = 0; - - NSError *err = nil; - unsigned long long downloadedSoFar = [[[NSFileManager defaultManager] attributesOfItemAtPath:temporaryPath error:&err] fileSize]; - GHAssertNil(err,@"Got an error obtaining attributes on the file, this shouldn't happen"); - - BOOL success = (downloadedSoFar > 0); - GHAssertTrue(success,@"Failed to download part of the file, so we can't proceed with this test"); - - request = [[[ASIHTTPRequest alloc] initWithURL:downloadURL] autorelease]; - [request setDownloadDestinationPath:downloadPath]; - [request setTemporaryFileDownloadPath:temporaryPath]; - [request setAllowResumeForFileDownloads:YES]; - - [networkQueue addOperation:request]; - - [networkQueue go]; - - [networkQueue waitUntilAllOperationsAreFinished]; - - unsigned long long amountDownloaded = [[[NSFileManager defaultManager] attributesOfItemAtPath:downloadPath error:&err] fileSize]; - GHAssertNil(err,@"Got an error obtaining attributes on the file, this shouldn't happen"); - success = (amountDownloaded == 1036935); - GHAssertTrue(success,@"Failed to complete the download"); - - success = (progress == 1.0f); - GHAssertTrue(success,@"Failed to increment progress properly"); - - - - //Test the temporary file cleanup - downloadURL = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel.txt"]; - complete = NO; - progress = 0; - networkQueue = [ASINetworkQueue queue]; - [networkQueue setDownloadProgressDelegate:self]; - [networkQueue setShowAccurateProgress:YES]; - [networkQueue setDelegate:self]; - [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; - - request = [[[ASIHTTPRequest alloc] initWithURL:downloadURL] autorelease]; - [request setDownloadDestinationPath:downloadPath]; - [request setTemporaryFileDownloadPath:temporaryPath]; - [request setAllowResumeForFileDownloads:YES]; - [networkQueue addOperation:request]; - [networkQueue go]; - - // Run until we have received a bit of data - while (1) { - usleep(1000000); - if ([request error] || [request contentLength]) { - break; - } - } - [networkQueue cancelAllOperations]; - - success = ([[NSFileManager defaultManager] fileExistsAtPath:temporaryPath]); - GHAssertTrue(success,@"Temporary download file doesn't exist"); - - [request removeTemporaryDownloadFile]; - - success = (![[NSFileManager defaultManager] fileExistsAtPath:temporaryPath]); - GHAssertTrue(success,@"Temporary download file should have been deleted"); - - -} - -- (void)stopQueue:(id)sender -{ - complete = YES; -} - - - -// Not strictly an ASINetworkQueue test, but queue related -// As soon as one request finishes or fails, we'll cancel the others and ensure that no requests are both finished and failed -- (void)testImmediateCancel -{ - [self setFailedRequests:[NSMutableArray array]]; - [self setFinishedRequests:[NSMutableArray array]]; - [self setImmediateCancelQueue:[[[NSOperationQueue alloc] init] autorelease]]; - int i; - for (i=0; i<10; i++) { - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request setDelegate:self]; - [request setDidFailSelector:@selector(immediateCancelFail:)]; - [request setDidFinishSelector:@selector(immediateCancelFinish:)]; - [[self immediateCancelQueue] addOperation:request]; - } - -} - -- (void)immediateCancelFail:(ASIHTTPRequest *)request -{ - if ([[self failedRequests] containsObject:request]) { - GHFail(@"A request called its fail delegate method twice"); - } - if ([[self finishedRequests] containsObject:request]) { - GHFail(@"A request that had already finished called its fail delegate method"); - } - [[self failedRequests] addObject:request]; - if ([[self failedRequests] count]+[[self finishedRequests] count] > 25) { - GHFail(@"We got more than 25 delegate fail/finish calls - this shouldn't happen!"); - } - [[self immediateCancelQueue] cancelAllOperations]; - -} - -- (void)immediateCancelFinish:(ASIHTTPRequest *)request -{ - if ([[self finishedRequests] containsObject:request]) { - GHFail(@"A request called its finish delegate method twice"); - } - if ([[self failedRequests] containsObject:request]) { - GHFail(@"A request that had already failed called its finish delegate method"); - } - [[self finishedRequests] addObject:request]; - if ([[self failedRequests] count]+[[self finishedRequests] count] > 25) { - GHFail(@"We got more than 25 delegate fail/finish calls - this shouldn't happen!"); - } - [[self immediateCancelQueue] cancelAllOperations]; -} - -// Ensure class convenience constructor returns an instance of our subclass -- (void)testSubclass -{ - ASINetworkQueueSubclass *instance = [ASINetworkQueueSubclass queue]; - BOOL success = [instance isKindOfClass:[ASINetworkQueueSubclass class]]; - GHAssertTrue(success,@"Convenience constructor failed to return an instance of the correct class"); -} - - -// Test releasing the queue in a couple of ways - the purpose of these tests is really just to ensure we don't crash -- (void)testQueueReleaseOnRequestComplete -{ - [[self releaseTestQueue] cancelAllOperations]; - [self setReleaseTestQueue:[ASINetworkQueue queue]]; - int i; - for (i=0; i<5; i++) { - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request setDelegate:self]; - [request setDidFailSelector:@selector(fail:)]; - [request setDidFinishSelector:@selector(finish:)]; - [[self releaseTestQueue] addOperation:request]; - } -} - -- (void)fail:(ASIHTTPRequest *)request -{ - if ([[self releaseTestQueue] requestsCount] == 0) { - [self setReleaseTestQueue:nil]; - } -} - -- (void)finish:(ASIHTTPRequest *)request -{ - if ([[self releaseTestQueue] requestsCount] == 0) { - [self setReleaseTestQueue:nil]; - } -} - -- (void)testQueueReleaseOnQueueComplete -{ - [[self releaseTestQueue] cancelAllOperations]; - [self setReleaseTestQueue:[ASINetworkQueue queue]]; - [[self releaseTestQueue] setDelegate:self]; - int i; - for (i=0; i<5; i++) { - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [[self releaseTestQueue] addOperation:request]; - } -} - -- (void)queueComplete:(ASINetworkQueue *)queue -{ - [self setReleaseTestQueue:nil]; -} - -- (void)testMultipleDownloadsThrottlingBandwidth -{ - complete = NO; - - [ASIHTTPRequest setMaxBandwidthPerSecond:0]; - - ASINetworkQueue *networkQueue = [ASINetworkQueue queue]; - [networkQueue setDelegate:self]; - [networkQueue setRequestDidFailSelector:@selector(throttleFail:)]; - [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; - - // We'll test first without throttling - int i; - for (i=0; i<5; i++) { - // This image is around 18KB in size, for 90KB total download size - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/i/logo.png"]]; - [networkQueue addOperation:request]; - } - - NSDate *date = [NSDate date]; - [networkQueue go]; - - while (!complete) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } - - - NSTimeInterval interval =[date timeIntervalSinceNow]; - BOOL success = (interval > -6); - GHAssertTrue(success,@"Downloaded the data too slowly - either this is a bug, or your internet connection is too slow to run this test (must be able to download 90KB in less than 6 seconds, without throttling)"); - - - // Reset the queue - [networkQueue reset]; - [networkQueue setDelegate:self]; - [networkQueue setRequestDidFailSelector:@selector(throttleFail:)]; - [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; - complete = NO; - - // Now we'll test with throttling - [ASIHTTPRequest setMaxBandwidthPerSecond:ASIWWANBandwidthThrottleAmount]; - - for (i=0; i<5; i++) { - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/i/logo.png"]]; - [networkQueue addOperation:request]; - } - - date = [NSDate date]; - [networkQueue go]; - - while (!complete) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } - - [ASIHTTPRequest setMaxBandwidthPerSecond:0]; - - interval =[date timeIntervalSinceNow]; - success = (interval < -6); - GHAssertTrue(success,@"Failed to throttle upload"); - -} - -- (void)testMultipleUploadsThrottlingBandwidth -{ - complete = NO; - - [ASIHTTPRequest setMaxBandwidthPerSecond:0]; - - ASINetworkQueue *networkQueue = [ASINetworkQueue queue]; - [networkQueue setDelegate:self]; - [networkQueue setRequestDidFailSelector:@selector(throttleFail:)]; - [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; - - // Create a 16KB request body - NSData *data = [[[NSMutableData alloc] initWithLength:16*1024] autorelease]; - - // We'll test first without throttling - int i; - for (i=0; i<10; i++) { - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ignore"]]; - [request appendPostData:data]; - [networkQueue addOperation:request]; - } - - NSDate *date = [NSDate date]; - [networkQueue go]; - - while (!complete) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } - - - NSTimeInterval interval =[date timeIntervalSinceNow]; - BOOL success = (interval > -10); - GHAssertTrue(success,@"Uploaded the data too slowly - either this is a bug, or your internet connection is too slow to run this test (must be able to upload 160KB in less than 10 seconds, without throttling)"); - - // Reset the queue - [networkQueue reset]; - [networkQueue setDelegate:self]; - [networkQueue setRequestDidFailSelector:@selector(throttleFail:)]; - [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; - complete = NO; - - // Now we'll test with throttling - [ASIHTTPRequest setMaxBandwidthPerSecond:ASIWWANBandwidthThrottleAmount]; - - for (i=0; i<10; i++) { - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ignore"]]; - [request appendPostData:data]; - [networkQueue addOperation:request]; - } - - date = [NSDate date]; - [networkQueue go]; - - while (!complete) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } - - [ASIHTTPRequest setMaxBandwidthPerSecond:0]; - - interval =[date timeIntervalSinceNow]; - success = (interval < -10); - GHAssertTrue(success,@"Failed to throttle upload"); - -} - -- (void)throttleFail:(ASIHTTPRequest *)request -{ - GHAssertTrue(NO,@"Request failed, cannot continue with this test"); - [[request queue] cancelAllOperations]; -} - -// Test for a bug that used to exist where the temporary file used to store the request body would be removed when authentication failed -- (void)testPOSTWithAuthentication -{ - [[self postQueue] cancelAllOperations]; - [self setPostQueue:[ASINetworkQueue queue]]; - [[self postQueue] setRequestDidFinishSelector:@selector(postDone:)]; - [[self postQueue] setDelegate:self]; - - ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/Tests/post_with_authentication"]]; - [request setPostValue:@"This is the first item" forKey:@"first"]; - [request setData:[@"This is the second item" dataUsingEncoding:NSUTF8StringEncoding] forKey:@"second"]; - [[self postQueue] addOperation:request]; - [[self postQueue] go]; -} - -- (void)postDone:(ASIHTTPRequest *)request -{ - BOOL success = [[request responseString] isEqualToString:@"This is the first item\r\nThis is the second item"]; - GHAssertTrue(success,@"Didn't post correct data"); -} - -- (void)testDelegateAuthenticationFailure -{ - [[self postQueue] cancelAllOperations]; - [self setPostQueue:[ASINetworkQueue queue]]; - [[self postQueue] setRequestDidFinishSelector:@selector(postDone:)]; - [[self postQueue] setDelegate:self]; - - ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/Tests/post_with_authentication"]]; - [request setPostValue:@"This is the first item" forKey:@"first"]; - [request setData:[@"This is the second item" dataUsingEncoding:NSUTF8StringEncoding] forKey:@"second"]; - [request setUserInfo:[NSDictionary dictionaryWithObject:@"delegate-auth-failure" forKey:@"test"]]; - [[self postQueue] addOperation:request]; - [[self postQueue] go]; -} - -- (void)testNTLMMultipleFailure -{ - authenticationPromptCount = 0; - [ASIHTTPRequest clearSession]; - [[self testNTLMQueue] cancelAllOperations]; - [self setTestNTLMQueue:[ASINetworkQueue queue]]; - - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/pretend-ntlm-handshake"]]; - [request setUseKeychainPersistence:NO]; - [request setUseSessionPersistence:NO]; - [request setUserInfo:[NSDictionary dictionaryWithObject:@"ntlm" forKey:@"test"]]; - - [[self testNTLMQueue] setRequestDidFinishSelector:@selector(ntlmDone:)]; - [[self testNTLMQueue] setRequestDidFailSelector:@selector(ntlmFailed:)]; - [[self testNTLMQueue] setDelegate:self]; - [[self testNTLMQueue] addOperation:request]; - [[self testNTLMQueue] go]; -} - -- (void)ntlmFailed:(ASIHTTPRequest *)request -{ - GHFail(@"Failed to provide NTLM credentials (error was :%@)",[request error]); -} - -- (void)ntlmDone:(ASIHTTPRequest *)request -{ - GHAssertNil([request error],@"Got an error when credentials were supplied"); - - // NSProcessInfo returns a lower case string for host name, while CFNetwork will send a mixed case string for host name, so we'll compare by lowercasing everything - NSString *hostName = [[NSProcessInfo processInfo] hostName]; - NSString *expectedResponse = [[NSString stringWithFormat:@"You are %@ from %@/%@",@"king",@"Castle.Kingdom",hostName] lowercaseString]; - BOOL success = [[[request responseString] lowercaseString] isEqualToString:expectedResponse]; - GHAssertTrue(success,@"Failed to send credentials correctly? (Expected: '%@', got '%@')",expectedResponse,[[request responseString] lowercaseString]); -} - -- (void)testHEADFailure -{ - [self performSelectorOnMainThread:@selector(runHEADFailureTest) withObject:nil waitUntilDone:YES]; -} - -// Test for a bug where failing head requests would not notify the original request's delegate of the failure -- (void)runHEADFailureTest -{ - headFailed = NO; - ASINetworkQueue *queue = [ASINetworkQueue queue]; - [queue setShowAccurateProgress:YES]; - - [queue setDelegate:self]; - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://"]]; - [request setDelegate:self]; - [request setDidFailSelector:@selector(HEADFail:)]; - [queue addOperation:request]; - [queue go]; - - [queue waitUntilAllOperationsAreFinished]; - - // Hope the request gets around to notifying the delegate in time - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]]; - - GHAssertTrue(headFailed,@"Failed to notify the request's delegate"); -} - -- (void)HEADFail:(ASIHTTPRequest *)request -{ - headFailed = YES; -} - -- (void)testCopy -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - ASINetworkQueue *queue = [ASINetworkQueue queue]; - ASINetworkQueue *queue2 = [queue copy]; - GHAssertNotNil(queue2,@"Failed to create a copy"); - - [pool release]; - - BOOL success = ([queue2 retainCount] > 0); - GHAssertTrue(success,@"Failed to create a retained copy"); - - [queue2 release]; -} - -// Test for: http://allseeing-i.lighthouseapp.com/projects/27881/tickets/43-asinetworkqueue-and-setshouldcancelallrequestsonfailure-yes-will-not-trigger-queuefinished-selector-with-multiple-requests - -- (void)testQueueFinishedCalledOnFailure -{ - [self performSelectorOnMainThread:@selector(runTestQueueFinishedCalledOnFailureTest) withObject:nil waitUntilDone:YES]; -} -- (void)runTestQueueFinishedCalledOnFailureTest -{ - complete = NO; - ASINetworkQueue *networkQueue = [[ASINetworkQueue queue] retain]; - [networkQueue setDelegate:self]; - [networkQueue setQueueDidFinishSelector:@selector(queueFailureFinish:)]; - - NSUInteger i; - for (i=0; i<10; i++) { - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://there-is-no-spoon.allseeing-i.com"]]; - [networkQueue addOperation:request]; - } - - [networkQueue go]; - - NSDate *dateStarted = [NSDate date]; - while (!complete) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]]; - if ([dateStarted timeIntervalSinceNow] < -10) { - break; - } - } - GHAssertTrue(complete,@"Failed to call queue finished delegate method"); - - queueFinishedCallCount = 0; - complete = NO; - - [networkQueue release]; - networkQueue = [[ASINetworkQueue queue] retain]; - [networkQueue setDelegate:self]; - [networkQueue setQueueDidFinishSelector:@selector(queueFailureFinishCallOnce:)]; - [networkQueue setMaxConcurrentOperationCount:1]; - - for (i=0; i<10; i++) { - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://there-is-no-spoon.allseeing-i.com"]]; - [networkQueue addOperation:request]; - } - - [networkQueue go]; - - dateStarted = [NSDate date]; - while (!complete) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0f]]; - if ([dateStarted timeIntervalSinceNow] < -10) { - break; - } - } - BOOL success = (queueFinishedCallCount == 1); - GHAssertTrue(success,@"Called the queue finish method more/less than once"); - -} - -- (void)queueFailureFinishCallOnce:(ASINetworkQueue *)queue -{ - queueFinishedCallCount++; - complete = YES; -} - -- (void)queueFailureFinish:(ASINetworkQueue *)queue -{ - complete = YES; -} - - -- (void)testDelegateRedirectHandling -{ - ASINetworkQueue *networkQueue = [ASINetworkQueue queue]; - [networkQueue setDelegate:self]; - - [networkQueue setRequestWillRedirectSelector:@selector(request:isGoingToRedirectToURL:)]; - [networkQueue setRequestDidFailSelector:@selector(redirectURLTestFailed:)]; - [networkQueue setRequestDidFinishSelector:@selector(redirectURLTestSucceeded:)]; - - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/redirect_to_ssl"]]; - - [networkQueue addOperation:request]; - [networkQueue go]; -} - -- (void)redirectURLTestSucceeded:(ASIHTTPRequest *)request -{ - BOOL success = [[request url] isEqual:[NSURL URLWithString:@"http://allseeing-i.com"]]; - GHAssertTrue(success,@"Request failed to redirect to url specified by delegate"); -} - -- (void)redirectURLTestFailed:(ASIHTTPRequest *)request -{ - GHFail(@"Request failed, cannot proceed with test"); -} - -- (void)request:(ASIHTTPRequest *)request isGoingToRedirectToURL:(NSURL *)url -{ - [request redirectToURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; -} - -@synthesize immediateCancelQueue; -@synthesize failedRequests; -@synthesize finishedRequests; -@synthesize releaseTestQueue; -@synthesize cancelQueue; -@synthesize postQueue; -@synthesize testNTLMQueue; -@synthesize addMoreRequestsQueue; -@end diff --git a/socketio/Tests/ASIS3RequestTests.h b/socketio/Tests/ASIS3RequestTests.h deleted file mode 100644 index 84557d9..0000000 --- a/socketio/Tests/ASIS3RequestTests.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// ASIS3RequestTests.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 12/07/2009. -// Copyright 2009 All-Seeing Interactive. All rights reserved. -// - -#import "ASITestCase.h" - -@class ASINetworkQueue; - -@interface ASIS3RequestTests : ASITestCase { - ASINetworkQueue *networkQueue; - float progress; -} - -- (void)testAuthenticationHeaderGeneration; -- (void)testREST; -- (void)testFailure; -- (void)testListRequest; -- (void)testSubclasses; -- (void)createTestBucket; -- (void)testCopy; -- (void)testHTTPS; - -@property (retain,nonatomic) ASINetworkQueue *networkQueue; -@end diff --git a/socketio/Tests/ASIS3RequestTests.m b/socketio/Tests/ASIS3RequestTests.m deleted file mode 100644 index d747e01..0000000 --- a/socketio/Tests/ASIS3RequestTests.m +++ /dev/null @@ -1,860 +0,0 @@ -// -// ASIS3RequestTests.m -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 12/07/2009. -// Copyright 2009 All-Seeing Interactive. All rights reserved. -// - -#import "ASIS3RequestTests.h" -#import "ASINetworkQueue.h" -#import "ASIS3BucketObject.h" -#import "ASIS3ObjectRequest.h" -#import "ASIS3BucketRequest.h" -#import "ASIS3ServiceRequest.h" - -// Fill in these to run the tests that actually connect and manipulate objects on S3 -static NSString *secretAccessKey = @""; -static NSString *accessKey = @""; - -// You should run these tests on a bucket that does not yet exist -static NSString *bucket = @""; - -// Used for subclass test -@interface ASIS3ObjectRequestSubclass : ASIS3ObjectRequest {} -@end -@implementation ASIS3ObjectRequestSubclass; -@end -@interface ASIS3BucketRequestSubclass : ASIS3BucketRequest {} -@end -@implementation ASIS3BucketRequestSubclass; -@end -@interface ASIS3BucketObjectSubclass : ASIS3BucketObject {} -@end -@implementation ASIS3BucketObjectSubclass; -@end - -// Stop clang complaining about undeclared selectors -@interface ASIS3RequestTests () -- (void)GETRequestDone:(ASIHTTPRequest *)request; -- (void)GETRequestFailed:(ASIHTTPRequest *)request; -- (void)PUTRequestDone:(ASIHTTPRequest *)request; -- (void)PUTRequestFailed:(ASIHTTPRequest *)request; -- (void)DELETERequestDone:(ASIHTTPRequest *)request; -- (void)DELETERequestFailed:(ASIHTTPRequest *)request; -@end - -@implementation ASIS3RequestTests - -// All these tests are based on Amazon's examples at: http://docs.amazonwebservices.com/AmazonS3/2006-03-01/ -- (void)testAuthenticationHeaderGeneration -{ - NSString *exampleSecretAccessKey = @"uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o"; - NSString *exampleAccessKey = @"0PN5J17HBGZHT7JJ3X82"; - NSString *exampleBucket = @"johnsmith"; - - // Test list all my buckets - NSString *dateString = @"Wed, 28 Mar 2007 01:29:59 +0000"; - ASIS3ServiceRequest *serviceRequest = [ASIS3ServiceRequest serviceRequest]; - [serviceRequest setSecretAccessKey:exampleSecretAccessKey]; - [serviceRequest setAccessKey:exampleAccessKey]; - [serviceRequest setDateString:dateString]; - [serviceRequest buildRequestHeaders]; - BOOL success = [[[serviceRequest requestHeaders] valueForKey:@"Authorization"] isEqualToString:@"AWS 0PN5J17HBGZHT7JJ3X82:Db+gepJSUbZKwpx1FR0DLtEYoZA="]; - GHAssertTrue(success,@"Failed to generate the correct authorisation header for a GET service request"); - - // Test GET - NSString *key = @"photos/puppy.jpg"; - dateString = @"Tue, 27 Mar 2007 19:36:42 +0000"; - ASIS3ObjectRequest *request = [ASIS3ObjectRequest requestWithBucket:exampleBucket key:key]; - [request setDateString:dateString]; - [request setSecretAccessKey:exampleSecretAccessKey]; - [request setAccessKey:exampleAccessKey]; - [request buildRequestHeaders]; - success = [[[request requestHeaders] valueForKey:@"Authorization"] isEqualToString:@"AWS 0PN5J17HBGZHT7JJ3X82:xXjDGYUmKxnwqr5KXNPGldn5LbA="]; - GHAssertTrue(success,@"Failed to generate the correct authorisation header for a GET request"); - - // Test PUT - key = @"photos/puppy.jpg"; - dateString = @"Tue, 27 Mar 2007 21:15:45 +0000"; - request = [ASIS3ObjectRequest requestWithBucket:exampleBucket key:key]; - [request setRequestMethod:@"PUT"]; - [request setMimeType:@"image/jpeg"]; - [request setDateString:dateString]; - [request setSecretAccessKey:exampleSecretAccessKey]; - [request setAccessKey:exampleAccessKey]; - [request buildRequestHeaders]; - success = [[[request requestHeaders] valueForKey:@"Authorization"] isEqualToString:@"AWS 0PN5J17HBGZHT7JJ3X82:hcicpDDvL9SsO6AkvxqmIWkmOuQ="]; - GHAssertTrue(success,@"Failed to generate the correct authorisation header for a PUT request"); - - // Test List - dateString = @"Tue, 27 Mar 2007 19:42:41 +0000"; - ASIS3BucketRequest *listRequest = [ASIS3BucketRequest requestWithBucket:exampleBucket]; - [listRequest setPrefix:@"photos"]; - [listRequest setMaxResultCount:50]; - [listRequest setMarker:@"puppy"]; - [listRequest setDateString:dateString]; - [listRequest setSecretAccessKey:exampleSecretAccessKey]; - [listRequest setAccessKey:exampleAccessKey]; - [listRequest buildRequestHeaders]; - success = [[[listRequest requestHeaders] valueForKey:@"Authorization"] isEqualToString:@"AWS 0PN5J17HBGZHT7JJ3X82:jsRt/rhG+Vtp88HrYL706QhE4w4="]; - GHAssertTrue(success,@"Failed to generate the correct authorisation header for a list request"); - - // Test fetch ACL - dateString = @"Tue, 27 Mar 2007 19:44:46 +0000"; - listRequest = [ASIS3BucketRequest requestWithBucket:exampleBucket subResource:@"acl"]; - [listRequest setDateString:dateString]; - [listRequest setSecretAccessKey:exampleSecretAccessKey]; - [listRequest setAccessKey:exampleAccessKey]; - [listRequest buildRequestHeaders]; - success = [[[listRequest requestHeaders] valueForKey:@"Authorization"] isEqualToString:@"AWS 0PN5J17HBGZHT7JJ3X82:thdUi9VAkzhkniLj96JIrOPGi0g="]; - GHAssertTrue(success,@"Failed to generate the correct authorisation header for a list request"); - - // Test Unicode keys - // Comment out this test for now, as the S3 example is relying on mixed-case hex-encoded characters in the url, which isn't going to be easy to replicate -// exampleBucket = @"dictionary"; -// key = @"français/préfère"; -// dateString = @"Wed, 28 Mar 2007 01:49:49 +0000"; -// request = [ASIS3ObjectRequest requestWithBucket:exampleBucket key:key]; -// [request setDateString:dateString]; -// [request setSecretAccessKey:exampleSecretAccessKey]; -// [request setAccessKey:exampleAccessKey]; -// [request buildRequestHeaders]; -// success = [[[request requestHeaders] valueForKey:@"Authorization"] isEqualToString:@"AWS 0PN5J17HBGZHT7JJ3X82:dxhSBHoI6eVSPcXJqEghlUzZMnY="]; - //GHAssertTrue(success,@"Failed to generate the correct authorisation header for a list request"); -} - -- (void)testFailure -{ - // Needs expanding to cover more failure states - this is just a test to ensure Amazon's error description is being added to the error - - // We're actually going to try with the Amazon example details, but the request will fail because the date is old - NSString *exampleSecretAccessKey = @"uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o"; - NSString *exampleAccessKey = @"0PN5J17HBGZHT7JJ3X82"; - NSString *exampleBucket = @"johnsmith"; - NSString *key = @"photos/puppy.jpg"; - NSString *dateString = @"Tue, 27 Mar 2007 19:36:42 +0000"; - ASIS3Request *request = [ASIS3ObjectRequest requestWithBucket:exampleBucket key:key]; - [request setDateString:dateString]; - [request setSecretAccessKey:exampleSecretAccessKey]; - [request setAccessKey:exampleAccessKey]; - [request startSynchronous]; - GHAssertNotNil([request error],@"Failed to generate an error when the request was not correctly signed"); - - BOOL success = ([[request error] code] == ASIS3ResponseErrorType); - GHAssertTrue(success,@"Generated error had the wrong error code"); - - success = ([[[request error] localizedDescription] isEqualToString:@"The difference between the request time and the current time is too large."]); - GHAssertTrue(success,@"Generated error had the wrong description"); - - // Ensure a bucket request will correctly parse an error from S3 - request = [ASIS3BucketRequest requestWithBucket:exampleBucket]; - [request setDateString:dateString]; - [request setSecretAccessKey:exampleSecretAccessKey]; - [request setAccessKey:exampleAccessKey]; - [request startSynchronous]; - GHAssertNotNil([request error],@"Failed to generate an error when the request was not correctly signed"); - - success = ([[request error] code] == ASIS3ResponseErrorType); - GHAssertTrue(success,@"Generated error had the wrong error code"); - - success = ([[[request error] localizedDescription] isEqualToString:@"The difference between the request time and the current time is too large."]); - GHAssertTrue(success,@"Generated error had the wrong description"); - - // Ensure a service request will correctly parse an error from S3 - request = [ASIS3ServiceRequest serviceRequest]; - [request setDateString:dateString]; - [request setSecretAccessKey:exampleSecretAccessKey]; - [request setAccessKey:exampleAccessKey]; - [request startSynchronous]; - GHAssertNotNil([request error],@"Failed to generate an error when the request was not correctly signed"); - - success = ([[request error] code] == ASIS3ResponseErrorType); - GHAssertTrue(success,@"Generated error had the wrong error code"); - - success = ([[[request error] localizedDescription] isEqualToString:@"The difference between the request time and the current time is too large."]); - GHAssertTrue(success,@"Generated error had the wrong description"); -} - -- (void)createTestBucket -{ - // Test creating a bucket - ASIS3BucketRequest *bucketRequest = [ASIS3BucketRequest PUTRequestWithBucket:bucket]; - [bucketRequest setSecretAccessKey:secretAccessKey]; - [bucketRequest setAccessKey:accessKey]; - [bucketRequest startSynchronous]; - GHAssertNil([bucketRequest error],@"Failed to create a bucket"); -} - -// To run this test, uncomment and fill in your S3 access details -- (void)testREST -{ - [self createTestBucket]; - - BOOL success = (![secretAccessKey isEqualToString:@""] && ![accessKey isEqualToString:@""] && ![bucket isEqualToString:@""]); - GHAssertTrue(success,@"You need to supply your S3 access details to run the REST test (see the top of ASIS3RequestTests.m)"); - - // Test creating a bucket - ASIS3BucketRequest *bucketRequest = [ASIS3BucketRequest PUTRequestWithBucket:bucket]; - [bucketRequest setSecretAccessKey:secretAccessKey]; - [bucketRequest setAccessKey:accessKey]; - [bucketRequest startSynchronous]; - GHAssertNil([bucketRequest error],@"Failed to create a bucket"); - - // List buckets to make sure the bucket is there - ASIS3ServiceRequest *serviceRequest = [ASIS3ServiceRequest serviceRequest]; - [serviceRequest setSecretAccessKey:secretAccessKey]; - [serviceRequest setAccessKey:accessKey]; - [serviceRequest startSynchronous]; - GHAssertNil([serviceRequest error],@"Failed to fetch the list of buckets from S3"); - - BOOL foundBucket = NO; - for (ASIS3Bucket *theBucket in [serviceRequest buckets]) { - if ([[theBucket name] isEqualToString:bucket]) { - foundBucket = YES; - break; - } - } - GHAssertTrue(foundBucket,@"Failed to retrive the newly-created bucket in a list of buckets"); - - NSString *key = @"test"; - - // Create the file - NSString *text = @"This is my content"; - NSString *filePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"testfile.txt"]; - [[text dataUsingEncoding:NSUTF8StringEncoding] writeToFile:filePath atomically:NO]; - - // PUT the file - ASIS3ObjectRequest *request = [ASIS3ObjectRequest PUTRequestForFile:filePath withBucket:bucket key:key]; - [request setSecretAccessKey:secretAccessKey]; - [request setAccessKey:accessKey]; - [request setStorageClass:ASIS3StorageClassReducedRedundancy]; - [request startSynchronous]; - success = [[request responseString] isEqualToString:@""]; - GHAssertTrue(success,@"Failed to PUT a file to S3"); - - // GET the file - request = [ASIS3ObjectRequest requestWithBucket:bucket key:key]; - [request setSecretAccessKey:secretAccessKey]; - [request setAccessKey:accessKey]; - [request startSynchronous]; - success = [[request responseString] isEqualToString:@"This is my content"]; - GHAssertTrue(success,@"Failed to GET the correct data from S3"); - - // Test fetch subresource - request = [ASIS3ObjectRequest requestWithBucket:bucket key:key subResource:@"acl"]; - [request setSecretAccessKey:secretAccessKey]; - [request setAccessKey:accessKey]; - [request startSynchronous]; - success = ([[request responseString] rangeOfString:@" 0); - GHAssertTrue(success,@"Failed to create a retained copy"); - success = ([request2 isKindOfClass:[ASIS3Request class]]); - GHAssertTrue(success,@"Copy is of wrong class"); - - [request2 release]; - - pool = [[NSAutoreleasePool alloc] init]; - - - ASIS3BucketRequest *request3 = [ASIS3BucketRequest requestWithBucket:@"foo"]; - ASIS3BucketRequest *request4 = [request3 copy]; - GHAssertNotNil(request4,@"Failed to create a copy"); - - [pool release]; - - success = ([request4 retainCount] > 0); - GHAssertTrue(success,@"Failed to create a retained copy"); - success = ([request4 isKindOfClass:[ASIS3BucketRequest class]]); - GHAssertTrue(success,@"Copy is of wrong class"); - - [request4 release]; - - pool = [[NSAutoreleasePool alloc] init]; - - - ASIS3BucketObject *bucketObject = [ASIS3BucketObject objectWithBucket:@"foo"]; - ASIS3BucketObject *bucketObject2 = [bucketObject copy]; - GHAssertNotNil(bucketObject2,@"Failed to create a copy"); - - [pool release]; - - success = ([bucketObject2 retainCount] > 0); - GHAssertTrue(success,@"Failed to create a retained copy"); - - [bucketObject2 release]; -} - - -- (void)testHTTPS -{ - [ASIS3Request setSharedAccessKey:accessKey]; - [ASIS3Request setSharedSecretAccessKey:secretAccessKey]; - - // Create a bucket - ASIS3Request *request = [ASIS3BucketRequest PUTRequestWithBucket:bucket]; - [request setRequestScheme:ASIS3RequestSchemeHTTPS]; - [request startSynchronous]; - GHAssertNil([request error],@"Failed to create a bucket"); - - // PUT something in it - NSString *key = @"king"; - request = [ASIS3ObjectRequest PUTRequestForData:[@"fink" dataUsingEncoding:NSUTF8StringEncoding] withBucket:bucket key:key]; - [request setRequestScheme:ASIS3RequestSchemeHTTPS]; - [request startSynchronous]; - BOOL success = [[request responseString] isEqualToString:@""]; - GHAssertTrue(success,@"Failed to PUT some data into S3"); - - // GET it - request = [ASIS3ObjectRequest requestWithBucket:bucket key:key]; - [request setRequestScheme:ASIS3RequestSchemeHTTPS]; - [request startSynchronous]; - success = [[request responseString] isEqualToString:@"fink"]; - GHAssertTrue(success,@"Failed to GET the correct data from S3"); - - // DELETE it - request = [ASIS3ObjectRequest DELETERequestWithBucket:bucket key:@"king"]; - [request setRequestScheme:ASIS3RequestSchemeHTTPS]; - [request startSynchronous]; - success = [[request responseString] isEqualToString:@""]; - GHAssertTrue(success,@"Failed to DELETE the object from S3"); - - // Delete the bucket - request = [ASIS3BucketRequest DELETERequestWithBucket:bucket]; - [request setRequestScheme:ASIS3RequestSchemeHTTPS]; - [request startSynchronous]; - GHAssertNil([request error],@"Failed to delete a bucket"); - - [ASIS3Request setSharedAccessKey:nil]; - [ASIS3Request setSharedSecretAccessKey:nil]; -} - -// Ideally this test would actually parse the ACL XML and check it, but for now it just makes sure S3 doesn't return an error -- (void)testCannedACLs -{ - [ASIS3Request setSharedAccessKey:accessKey]; - [ASIS3Request setSharedSecretAccessKey:secretAccessKey]; - - // Create a bucket - ASIS3Request *request = [ASIS3BucketRequest PUTRequestWithBucket:bucket]; - [request setRequestScheme:ASIS3RequestSchemeHTTPS]; - [request startSynchronous]; - GHAssertNil([request error],@"Failed to create a bucket"); - - NSArray *ACLs = [NSArray arrayWithObjects:ASIS3AccessPolicyPrivate,ASIS3AccessPolicyPublicRead,ASIS3AccessPolicyPublicReadWrite,ASIS3AccessPolicyAuthenticatedRead,ASIS3AccessPolicyBucketOwnerRead,ASIS3AccessPolicyBucketOwnerFullControl,nil]; - - for (NSString *cannedACL in ACLs) { - // PUT object - NSString *key = @"king"; - request = [ASIS3ObjectRequest PUTRequestForData:[@"fink" dataUsingEncoding:NSUTF8StringEncoding] withBucket:bucket key:key]; - [request setAccessPolicy:cannedACL]; - [request startSynchronous]; - GHAssertNil([request error],@"Failed to PUT some data into S3"); - - // GET object ACL - request = [ASIS3ObjectRequest requestWithBucket:bucket key:key subResource:@"acl"]; - [request startSynchronous]; - GHAssertNil([request error],@"Failed to fetch the object"); - } - - // DELETE it - request = [ASIS3ObjectRequest DELETERequestWithBucket:bucket key:@"king"]; - [request setRequestScheme:ASIS3RequestSchemeHTTPS]; - [request startSynchronous]; - BOOL success = [[request responseString] isEqualToString:@""]; - GHAssertTrue(success,@"Failed to DELETE the object from S3"); - - // Delete the bucket - request = [ASIS3BucketRequest DELETERequestWithBucket:bucket]; - [request setRequestScheme:ASIS3RequestSchemeHTTPS]; - [request startSynchronous]; - GHAssertNil([request error],@"Failed to delete a bucket"); - - [ASIS3Request setSharedAccessKey:nil]; - [ASIS3Request setSharedSecretAccessKey:nil]; -} - - -@synthesize networkQueue; - -@end diff --git a/socketio/Tests/ASITestCase.h b/socketio/Tests/ASITestCase.h deleted file mode 100644 index 2d33540..0000000 --- a/socketio/Tests/ASITestCase.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// ASITestCase.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 26/07/2009. -// Copyright 2009 All-Seeing Interactive. All rights reserved. -// - -#import - -#if TARGET_OS_IPHONE -#import -#else -#import -#endif - -@interface ASITestCase : GHTestCase { -} -- (NSString *)filePathForTemporaryTestFiles; -@end diff --git a/socketio/Tests/ASITestCase.m b/socketio/Tests/ASITestCase.m deleted file mode 100644 index cc5df58..0000000 --- a/socketio/Tests/ASITestCase.m +++ /dev/null @@ -1,23 +0,0 @@ -// -// ASITestCase.m -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 26/07/2009. -// Copyright 2009 All-Seeing Interactive. All rights reserved. -// - -#import "ASITestCase.h" - - -@implementation ASITestCase - -- (NSString *)filePathForTemporaryTestFiles -{ - NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"ASIHTTPRequest Test Files"]; - if (![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:NULL]) { - [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:NO attributes:nil error:NULL]; - } - return path; -} - -@end diff --git a/socketio/Tests/ASIWebPageRequestTests.h b/socketio/Tests/ASIWebPageRequestTests.h deleted file mode 100644 index 6b16050..0000000 --- a/socketio/Tests/ASIWebPageRequestTests.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// ASIWebPageRequestTests.h -// Mac -// -// Created by Ben Copsey on 06/01/2011. -// Copyright 2011 All-Seeing Interactive. All rights reserved. -// - -#import -#import "ASITestCase.h" - -@interface ASIWebPageRequestTests : ASITestCase { - -} - -@end diff --git a/socketio/Tests/ASIWebPageRequestTests.m b/socketio/Tests/ASIWebPageRequestTests.m deleted file mode 100644 index f9f1149..0000000 --- a/socketio/Tests/ASIWebPageRequestTests.m +++ /dev/null @@ -1,41 +0,0 @@ -// -// ASIWebPageRequestTests.m -// Mac -// -// Created by Ben Copsey on 06/01/2011. -// Copyright 2011 All-Seeing Interactive. All rights reserved. -// - -#import "ASIWebPageRequestTests.h" -#import "ASIWebPageRequest.h" - -@implementation ASIWebPageRequestTests - -- (void)testEncoding -{ - NSArray *encodings = [NSArray arrayWithObjects:@"us-ascii",@"iso-8859-1",@"utf-16",@"utf-8",nil]; - NSArray *expectedResponses = [NSArray arrayWithObjects:@"Hi there",@"Olá",@"你好",@"今日は",nil]; - NSUInteger i; - for (i=0; i<[encodings count]; i++) { - ASIWebPageRequest *request = [ASIWebPageRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://allseeing-i.com/ASIHTTPRequest/tests/asiwebpagerequest/character-encoding/%@",[encodings objectAtIndex:i]]]]; - [request setUserInfo:[NSDictionary dictionaryWithObject:[expectedResponses objectAtIndex:i] forKey:@"expected-response"]]; - [request setDelegate:self]; - [request setUrlReplacementMode:ASIReplaceExternalResourcesWithLocalURLs]; - [request startAsynchronous]; - } -} - -- (void)requestFinished:(ASIHTTPRequest *)request -{ - if ([[request userInfo] objectForKey:@"expected-response"]) { - BOOL success = ([[request responseString] rangeOfString:[[request userInfo] objectForKey:@"expected-response"]].location != NSNotFound); - GHAssertTrue(success,@"Response HTML used wrong encoding"); - } -} - -- (void)requestFailed:(ASIHTTPRequest *)request -{ - GHAssertNil([request error],@"Request failed, cannot proceed with test"); -} - -@end diff --git a/socketio/Tests/BlocksTests.h b/socketio/Tests/BlocksTests.h deleted file mode 100644 index f216475..0000000 --- a/socketio/Tests/BlocksTests.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// BlocksTests.h -// Mac -// -// Created by Ben Copsey on 18/10/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// - -#import -#import "ASITestCase.h" - -@interface BlocksTests : ASITestCase { - -} - -@end diff --git a/socketio/Tests/BlocksTests.m b/socketio/Tests/BlocksTests.m deleted file mode 100644 index e66f0af..0000000 --- a/socketio/Tests/BlocksTests.m +++ /dev/null @@ -1,105 +0,0 @@ -// -// BlocksTests.m -// Mac -// -// Created by Ben Copsey on 18/10/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// - -#import "BlocksTests.h" -#import "ASIHTTPRequest.h" - - -@implementation BlocksTests - -// ASIHTTPRequest always calls blocks on the main thread (just like it does with delegate methods) -// So, we'll force this request to run on the main thread so we can rely on blocks having been called before the request returns -- (BOOL)shouldRunOnMainThread { return YES; } - -#if NS_BLOCKS_AVAILABLE -#if TARGET_OS_IPHONE -// It isn't safe to allow the view to deallocate on a thread other than the main thread / web thread, so this test is designed to cause a crash semi-reliably -- (void)testBlockMainThreadSafety -{ - NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com"]; - UIWebView *webView = [[[UIWebView alloc] initWithFrame:CGRectMake(0,0,200,200)] autorelease]; - __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; - [request setCompletionBlock:^ {[webView loadHTMLString:[request responseString] baseURL:url]; }]; - [request startAsynchronous]; -} -#endif - -- (void)testBlocks -{ - NSData *dataToSend = [@"This is my post body" dataUsingEncoding:NSUTF8StringEncoding]; - - __block BOOL started = NO; - __block BOOL receivedHeaders = NO; - __block BOOL complete = NO; - __block BOOL failed = NO; - __block unsigned long long totalBytesReceived = 0; - __block unsigned long long totalDownloadSize = 0; - __block unsigned long long totalBytesSent = 0; - __block unsigned long long totalUploadSize = 0; - NSMutableData *dataReceived = [NSMutableData data]; - - // There's actually no need for us to use '__block' here, because we aren't using the request inside any of our blocks, but it's good to get into the habit of doing this anyway. - __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/blocks"]]; - [request setStartedBlock:^{ - started = YES; - }]; - [request setHeadersReceivedBlock:^(NSDictionary *headers) { - receivedHeaders = YES; - }]; - [request setCompletionBlock:^{ - complete = YES; - }]; - [request setFailedBlock:^{ - failed = YES; - }]; - [request setBytesReceivedBlock:^(unsigned long long length, unsigned long long total) { - totalBytesReceived += length; - }]; - [request setDownloadSizeIncrementedBlock:^(long long length){ - totalDownloadSize += length; - }]; - [request setBytesSentBlock:^(unsigned long long length, unsigned long long total) { - totalBytesSent += length; - }]; - [request setUploadSizeIncrementedBlock:^(long long length){ - totalUploadSize += length; - }]; - [request setDataReceivedBlock:^(NSData *data){ - [dataReceived appendData:data]; - }]; - - [request setRequestMethod:@"PUT"]; - [request appendPostData:dataToSend]; - [request startSynchronous]; - - GHAssertFalse(failed,@"Request failed, cannot proceed with test"); - GHAssertTrue(started,@"Failed to call started block"); - GHAssertTrue(receivedHeaders,@"Failed to call received headers block"); - GHAssertTrue(complete,@"Failed to call completed block"); - - BOOL success = (totalBytesReceived == 457); - GHAssertTrue(success,@"Failed to call bytes received block, or got wrong amount of data"); - success = (totalDownloadSize == 457); - GHAssertTrue(success,@"Failed to call download size increment block"); - - success = (totalBytesSent == [dataToSend length]); - GHAssertTrue(success,@"Failed to call bytes sent block"); - success = (totalUploadSize == [dataToSend length]); - GHAssertTrue(success,@"Failed to call upload size increment block"); - - - request = [ASIHTTPRequest requestWithURL:nil]; - [request setFailedBlock:^{ - failed = YES; - }]; - [request startSynchronous]; - GHAssertTrue(failed,@"Failed to call request failure block"); -} -#endif - -@end diff --git a/socketio/Tests/ClientCertificateTests.h b/socketio/Tests/ClientCertificateTests.h deleted file mode 100644 index 68b622b..0000000 --- a/socketio/Tests/ClientCertificateTests.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// ClientCertificateTests.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 18/08/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// - -// Currently, these tests only work on iOS - it looks like the method for parsing the PKCS12 file would need to be ported - -#import -#import -#import "ASITestCase.h" - -@interface ClientCertificateTests : ASITestCase { - -} -- (void)testClientCertificate; -+ (BOOL)extractIdentity:(SecIdentityRef *)outIdentity andTrust:(SecTrustRef*)outTrust fromPKCS12Data:(NSData *)inPKCS12Data; - -@end diff --git a/socketio/Tests/ClientCertificateTests.m b/socketio/Tests/ClientCertificateTests.m deleted file mode 100644 index 2cf79da..0000000 --- a/socketio/Tests/ClientCertificateTests.m +++ /dev/null @@ -1,76 +0,0 @@ -// -// ClientCertificateTests.m -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 18/08/2010. -// Copyright 2010 All-Seeing Interactive. All rights reserved. -// - -#import "ClientCertificateTests.h" -#import "ASIHTTPRequest.h" - -@implementation ClientCertificateTests - -- (void)testClientCertificate -{ - // This test will fail the second time it is run, I presume the certificate is being cached somewhere - - // This url requires we present a client certificate to connect to it - NSURL *url = [NSURL URLWithString:@"https://clientcertificate.allseeing-i.com:8080/ASIHTTPRequest/tests/first"]; - - // First, let's attempt to connect to the url without supplying a certificate - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; - - // We have to turn off validation for these tests, as the server has a self-signed certificate - [request setValidatesSecureCertificate:NO]; - [request startSynchronous]; - - GHAssertNotNil([request error],@"Request succeeded even though we presented no certificate, cannot proceed with test"); - - // Now, let's grab the certificate (included in the resources of the test app) - SecIdentityRef identity = NULL; - SecTrustRef trust = NULL; - NSData *PKCS12Data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"]]; - [ClientCertificateTests extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data]; - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"https://clientcertificate.allseeing-i.com:8080/ASIHTTPRequest/tests/first"]]; - - // In this case, we have no need to add extra certificates, just the one inside the indentity will be used - [request setClientCertificateIdentity:identity]; - [request setValidatesSecureCertificate:NO]; - [request startSynchronous]; - - // Make sure the request got the correct content - GHAssertNil([request error],@"Request failed with error %@",[request error]); - BOOL success = [[request responseString] isEqualToString:@"This is the expected content for the first string"]; - GHAssertTrue(success,@"Request failed to download the correct content"); -} - -// Based on code from http://developer.apple.com/mac/library/documentation/Security/Conceptual/CertKeyTrustProgGuide/iPhone_Tasks/iPhone_Tasks.html - -+ (BOOL)extractIdentity:(SecIdentityRef *)outIdentity andTrust:(SecTrustRef*)outTrust fromPKCS12Data:(NSData *)inPKCS12Data -{ - OSStatus securityError = errSecSuccess; - - NSDictionary *optionsDictionary = [NSDictionary dictionaryWithObject:@"" forKey:(id)kSecImportExportPassphrase]; - - CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL); - securityError = SecPKCS12Import((CFDataRef)inPKCS12Data,(CFDictionaryRef)optionsDictionary,&items); - - if (securityError == 0) { - CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0); - const void *tempIdentity = NULL; - tempIdentity = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemIdentity); - *outIdentity = (SecIdentityRef)tempIdentity; - const void *tempTrust = NULL; - tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust); - *outTrust = (SecTrustRef)tempTrust; - } else { - NSLog(@"Failed with error code %d",(int)securityError); - return NO; - } - return YES; -} - - -@end diff --git a/socketio/Tests/GHUnitTestMain.m b/socketio/Tests/GHUnitTestMain.m deleted file mode 100644 index f947dca..0000000 --- a/socketio/Tests/GHUnitTestMain.m +++ /dev/null @@ -1,89 +0,0 @@ -// -// GHUnitTestMain.m -// GHUnit -// -// Created by Gabriel Handford on 2/22/09. -// Copyright 2009. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import - -#import -#import -#import - -// Default exception handler -void exceptionHandler(NSException *exception) { - NSLog(@"%@\n%@", [exception reason], GHUStackTraceFromException(exception)); -} - -int main(int argc, char *argv[]) { - - /*! - For debugging: - Go into the "Get Info" contextual menu of your (test) executable (inside the "Executables" group in the left panel of XCode). - Then go in the "Arguments" tab. You can add the following environment variables: - - Default: Set to: - NSDebugEnabled NO "YES" - NSZombieEnabled NO "YES" - NSDeallocateZombies NO "YES" - NSHangOnUncaughtException NO "YES" - - NSEnableAutoreleasePool YES "NO" - NSAutoreleaseFreedObjectCheckEnabled NO "YES" - NSAutoreleaseHighWaterMark 0 non-negative integer - NSAutoreleaseHighWaterResolution 0 non-negative integer - - For info on these varaiables see NSDebug.h; http://theshadow.uw.hu/iPhoneSDKdoc/Foundation.framework/NSDebug.h.html - - For malloc debugging see: http://developer.apple.com/mac/library/documentation/Performance/Conceptual/ManagingMemory/Articles/MallocDebug.html - */ - - NSSetUncaughtExceptionHandler(&exceptionHandler); - - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - // Register any special test case classes - //[[GHTesting sharedInstance] registerClassName:@"GHSpecialTestCase"]; - - int retVal = 0; - // If GHUNIT_CLI is set we are using the command line interface and run the tests - // Otherwise load the GUI app - if (getenv("GHUNIT_CLI")) { - retVal = [GHTestRunner run]; - } else { - // To run all tests (from ENV) - GHTestApp *app = [[GHTestApp alloc] init]; - // To run a different test suite: - //GHTestSuite *suite = [GHTestSuite suiteWithTestFilter:@"GHSlowTest,GHAsyncTestCaseTest"]; - //GHTestApp *app = [[GHTestApp alloc] initWithSuite:suite]; - // Or set global: - //GHUnitTest = @"GHSlowTest"; - [NSApp run]; - [app release]; - } - [pool release]; - return retVal; -} diff --git a/socketio/Tests/PerformanceTests.h b/socketio/Tests/PerformanceTests.h deleted file mode 100644 index 90bd49c..0000000 --- a/socketio/Tests/PerformanceTests.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// PerformanceTests.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 17/12/2009. -// Copyright 2009 All-Seeing Interactive. All rights reserved. -// - -#import -#import "ASITestCase.h" - -@interface PerformanceTests : ASITestCase { - NSURL *testURL; - - NSDate *testStartDate; - int requestsComplete; - NSMutableArray *responseData; - unsigned long bytesDownloaded; -} - -- (void)testASIHTTPRequestAsyncPerformance; -- (void)testNSURLConnectionAsyncPerformance; - -@property (retain,nonatomic) NSURL *testURL; -@property (retain,nonatomic) NSDate *testStartDate; -@property (assign,nonatomic) int requestsComplete; -@property (retain,nonatomic) NSMutableArray *responseData; -@end diff --git a/socketio/Tests/PerformanceTests.m b/socketio/Tests/PerformanceTests.m deleted file mode 100644 index 1165d5b..0000000 --- a/socketio/Tests/PerformanceTests.m +++ /dev/null @@ -1,234 +0,0 @@ -// -// PerformanceTests.m -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 17/12/2009. -// Copyright 2009 All-Seeing Interactive. All rights reserved. -// - -#import "PerformanceTests.h" -#import "ASIHTTPRequest.h" - -// IMPORTANT - these tests need to be run one at a time! - -@interface NSURLConnectionSubclass : NSURLConnection { - int tag; -} -@property (assign) int tag; -@end -@implementation NSURLConnectionSubclass -@synthesize tag; -@end - -// Stop clang complaining about undeclared selectors -@interface PerformanceTests () -- (void)runSynchronousASIHTTPRequests; -- (void)runSynchronousNSURLConnections; -- (void)startASIHTTPRequests; -- (void)startASIHTTPRequestsWithQueue; -- (void)startNSURLConnections; -@end - - -@implementation PerformanceTests - -- (void)setUp -{ - [self setTestURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_%28abridged%29.txt"]]; - //[self setTestURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; -} - -- (void)testASIHTTPRequestSynchronousPerformance -{ - [self performSelectorOnMainThread:@selector(runSynchronousASIHTTPRequests) withObject:nil waitUntilDone:YES]; -} - - -- (void)runSynchronousASIHTTPRequests -{ - int runTimes = 10; - NSTimeInterval times[runTimes]; - int i; - for (i=0; i worstTime) { - worstTime = times[i]; - } - totalTime += times[i]; - } - NSLog(@"Ran %i requests in %f seconds (average time: %f secs / best time: %f secs / worst time: %f secs)",runTimes,totalTime,totalTime/runTimes,bestTime,worstTime); -} - - -- (void)testNSURLConnectionSynchronousPerformance -{ - [self performSelectorOnMainThread:@selector(runSynchronousNSURLConnections) withObject:nil waitUntilDone:YES]; -} - - -- (void)runSynchronousNSURLConnections -{ - int runTimes = 10; - NSTimeInterval times[runTimes]; - int i; - for (i=0; i worstTime) { - worstTime = times[i]; - } - totalTime += times[i]; - } - NSLog(@"Ran %i requests in %f seconds (average time: %f secs / best time: %f secs / worst time: %f secs)",runTimes,totalTime,totalTime/runTimes,bestTime,worstTime); -} - - -- (void)testASIHTTPRequestAsyncPerformance -{ - [self performSelectorOnMainThread:@selector(startASIHTTPRequests) withObject:nil waitUntilDone:NO]; -} - -- (void)testQueuedASIHTTPRequestAsyncPerformance -{ - [self performSelectorOnMainThread:@selector(startASIHTTPRequestsWithQueue) withObject:nil waitUntilDone:NO]; -} - - -- (void)startASIHTTPRequests -{ - bytesDownloaded = 0; - [self setRequestsComplete:0]; - [self setTestStartDate:[NSDate date]]; - int i; - for (i=0; i<10; i++) { - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:testURL]; - //Send the same headers as NSURLRequest - [request addRequestHeader:@"Pragma" value:@"no-cache"]; - [request addRequestHeader:@"Accept" value:@"*/*"]; - [request addRequestHeader:@"Accept-Language" value:@"en/us"]; - [request setDelegate:self]; - [request startAsynchronous]; - } -} - -- (void)startASIHTTPRequestsWithQueue -{ - bytesDownloaded = 0; - [self setRequestsComplete:0]; - [self setTestStartDate:[NSDate date]]; - int i; - NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease]; - [queue setMaxConcurrentOperationCount:4]; - for (i=0; i<10; i++) { - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:testURL]; - //Send the same headers as NSURLRequest - [request addRequestHeader:@"Pragma" value:@"no-cache"]; - [request addRequestHeader:@"Accept" value:@"*/*"]; - [request addRequestHeader:@"Accept-Language" value:@"en/us"]; - [request setUseCookiePersistence:NO]; - [request setUseSessionPersistence:NO]; - [request setDelegate:self]; - [queue addOperation:request]; - } -} - -- (void)requestFailed:(ASIHTTPRequest *)request -{ - GHFail(@"Cannot proceed with ASIHTTPRequest test - a request failed"); -} - -- (void)requestFinished:(ASIHTTPRequest *)request -{ - bytesDownloaded += [[request responseData] length]; - requestsComplete++; - if (requestsComplete == 10) { - NSLog(@"ASIHTTPRequest: Completed 10 (downloaded %lu bytes) requests in %f seconds",bytesDownloaded,[[NSDate date] timeIntervalSinceDate:[self testStartDate]]); - } -} - -- (void)testNSURLConnectionAsyncPerformance -{ - [self performSelectorOnMainThread:@selector(startNSURLConnections) withObject:nil waitUntilDone:NO]; -} - -- (void)startNSURLConnections -{ - bytesDownloaded = 0; - [self setRequestsComplete:0]; - [self setTestStartDate:[NSDate date]]; - [self setResponseData:[NSMutableArray arrayWithCapacity:5]]; - - int i; - for (i=0; i<10; i++) { - NSURLRequest *request = [NSURLRequest requestWithURL:testURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10]; - [[self responseData] addObject:[NSMutableData data]]; - NSURLConnectionSubclass *connection = [[[NSURLConnectionSubclass alloc] initWithRequest:request delegate:self startImmediately:YES] autorelease]; - [connection setTag:i]; - } -} - -- (void)connection:(NSURLConnectionSubclass *)connection didReceiveResponse:(NSURLResponse *)response -{ -} - -- (void)connection:(NSURLConnectionSubclass *)connection didFailWithError:(NSError *)error -{ - GHFail(@"Cannot proceed with NSURLConnection test - a request failed"); -} - -- (void)connection:(NSURLConnectionSubclass *)connection didReceiveData:(NSData *)data -{ - [[[self responseData] objectAtIndex:[connection tag]] appendData:data]; - -} - -- (void)connectionDidFinishLoading:(NSURLConnectionSubclass *)connection -{ - bytesDownloaded += [[responseData objectAtIndex:[connection tag]] length]; - requestsComplete++; - if (requestsComplete == 10) { - NSLog(@"NSURLConnection: Completed 10 (downloaded %lu bytes) requests in %f seconds",bytesDownloaded,[[NSDate date] timeIntervalSinceDate:[self testStartDate]]); - } -} - -@synthesize testURL; -@synthesize requestsComplete; -@synthesize testStartDate; -@synthesize responseData; -@end diff --git a/socketio/Tests/ProxyTests.h b/socketio/Tests/ProxyTests.h deleted file mode 100644 index 1205298..0000000 --- a/socketio/Tests/ProxyTests.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// ProxyTests.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 02/08/2009. -// Copyright 2009 All-Seeing Interactive. All rights reserved. -// - -#import -#import "ASITestCase.h" -@class ASINetworkQueue; - -// Proxy tests must be run separately from other tests, using the proxy you specify -// Some tests require an authenticating proxy to function - -@interface ProxyTests : ASITestCase { - ASINetworkQueue *queue; - BOOL complete; -} -- (void)testProxy; -- (void)testProxyAutodetect; -- (void)testProxyWithSuppliedAuthenticationCredentials; -- (void)testDoubleAuthentication; -- (void)testProxyForHTTPS; - -@property (retain) ASINetworkQueue *queue; -@property (assign) BOOL complete; - -@end diff --git a/socketio/Tests/ProxyTests.m b/socketio/Tests/ProxyTests.m deleted file mode 100644 index 7435fbb..0000000 --- a/socketio/Tests/ProxyTests.m +++ /dev/null @@ -1,203 +0,0 @@ -// -// ProxyTests.m -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 02/08/2009. -// Copyright 2009 All-Seeing Interactive. All rights reserved. -// - -#import "ProxyTests.h" -#import "ASIHTTPRequest.h" -#import "ASINetworkQueue.h" - -// Fill in these to run the proxy tests -static NSString *proxyHost = @""; -static int proxyPort = 0; -static NSString *proxyUsername = @""; -static NSString *proxyPassword = @""; - -// Stop clang complaining about undeclared selectors -@interface ProxyTests () -- (void)requestDone:(ASIHTTPRequest *)request; -- (void)requestFailed:(ASIHTTPRequest *)request; -@end - - -@implementation ProxyTests - -- (void)testProxyForHTTPS -{ - // Also test we are case-insensitive comparing our scheme - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"HTTPS://selfsigned.allseeing-i.com/"]]; - [request setValidatesSecureCertificate:NO]; - [request startSynchronous]; - BOOL success = ([[request responseString] rangeOfString:@"All-Seeing Interactive"].location != NSNotFound); - GHAssertTrue(success,@"Failed to connect to an HTTPS URL using a proxy"); -} - -- (void)testAutoConfigureWithPAC -{ - - NSString *pacurl = @"file:///non-existent.pac"; - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request setPACurl:[NSURL URLWithString:pacurl]]; - [request startSynchronous]; - GHAssertNil([request proxyHost],@"Shouldn't use a proxy here"); - GHAssertNil([request error],@"Request failed when unable to fetch PAC (should assume no proxy instead)"); - - // To run this test, specify the location of the pac script that is available at http://developer.apple.com/samplecode/CFProxySupportTool/listing1.html - pacurl = @"file:///Users/ben/Desktop/test.pac"; - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request setPACurl:[NSURL URLWithString:pacurl]]; - [request startSynchronous]; - - BOOL success = [[request proxyHost] isEqualToString:@"proxy1.apple.com"]; - GHAssertTrue(success,@"Failed to use the correct proxy"); - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://www.apple.com"]]; - [request setPACurl:[NSURL URLWithString:pacurl]]; - [request startSynchronous]; - GHAssertNil([request proxyHost],@"Used a proxy when the script told us to go direct"); -} - -- (void)testAutoConfigureWithSystemPAC -{ - // To run this test, specify the pac script above in your network settings - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request startSynchronous]; - - BOOL success = [[request proxyHost] isEqualToString:@"proxy1.apple.com"]; - GHAssertTrue(success,@"Failed to use the correct proxy"); - - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://www.apple.com"]]; - [request startSynchronous]; - GHAssertNil([request proxyHost],@"Used a proxy when the script told us to go direct"); -} - -- (void)testProxy -{ - BOOL success = (![proxyHost isEqualToString:@""] && proxyPort > 0); - GHAssertTrue(success,@"You need to supply the details of your proxy to run the proxy autodetect test"); - - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request setProxyHost:proxyHost]; - [request setProxyPort:proxyPort]; - [request startSynchronous]; - - // Check data is as expected - NSRange notFound = NSMakeRange(NSNotFound, 0); - success = !NSEqualRanges([[request responseString] rangeOfString:@"All-Seeing Interactive"],notFound); - GHAssertTrue(success,@"Failed to download the correct data, navigating the proxy"); -} - -- (void)testProxyAutodetect -{ - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request startSynchronous]; - - BOOL success = ([request proxyHost] && [request proxyPort]); - GHAssertTrue(success,@"Failed to detect the proxy"); -} - - -- (void)testProxyWithSuppliedAuthenticationCredentials -{ - BOOL success = (![proxyHost isEqualToString:@""] && proxyPort > 0 && ![proxyUsername isEqualToString:@""] && ![proxyPassword isEqualToString:@""]); - GHAssertTrue(success,@"You need to supply the details of your authenticating proxy to run the proxy authentication test"); - - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [request setProxyHost:proxyHost]; - [request setProxyPort:proxyPort]; - [request setProxyUsername:proxyUsername]; - [request setProxyPassword:proxyPassword]; - [request startSynchronous]; - - // Check data is as expected - NSRange notFound = NSMakeRange(NSNotFound, 0); - success = !NSEqualRanges([[request responseString] rangeOfString:@"All-Seeing Interactive"],notFound); - GHAssertTrue(success,@"Failed to download the correct data, navigating the proxy"); -} - -- (void)testProxyWithDelegateSupplyingCredentials -{ - [self setComplete:NO]; - BOOL success = (![proxyHost isEqualToString:@""] && proxyPort > 0 && ![proxyUsername isEqualToString:@""] && ![proxyPassword isEqualToString:@""]); - GHAssertTrue(success,@"You need to supply the details of your authenticating proxy to run the proxy authentication test"); - - [[self queue] cancelAllOperations]; - [self setQueue:[ASINetworkQueue queue]]; - [[self queue] setDelegate:self]; - [[self queue] setRequestDidFinishSelector:@selector(requestFinished:)]; - [[self queue] setRequestDidFailSelector:@selector(requestFailed:)]; - - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; - [[self queue] addOperation:request]; - - [queue go]; - - while (![self complete]) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } -} - -- (void)requestFinished:(ASIHTTPRequest *)request -{ - [self setComplete:YES]; - // Check data is as expected - NSRange notFound = NSMakeRange(NSNotFound, 0); - BOOL success = !NSEqualRanges([[request responseString] rangeOfString:@"All-Seeing Interactive"],notFound); - GHAssertTrue(success,@"Failed to download the correct data, navigating the proxy"); -} - -- (void)requestFailed:(ASIHTTPRequest *)request -{ - [self setComplete:YES]; - GHAssertTrue(0,@"Request failed when it shouldn't have done so"); -} - -- (void)proxyAuthenticationNeededForRequest:(ASIHTTPRequest *)request -{ - [request setProxyUsername:proxyUsername]; - [request setProxyPassword:proxyPassword]; - [request retryUsingSuppliedCredentials]; -} - - -- (void)testDoubleAuthentication -{ - [self setComplete:NO]; - BOOL success = (![proxyHost isEqualToString:@""] && proxyPort > 0 && ![proxyUsername isEqualToString:@""] && ![proxyPassword isEqualToString:@""]); - GHAssertTrue(success,@"You need to supply the details of your authenticating proxy to run the proxy authentication test"); - - [[self queue] cancelAllOperations]; - [self setQueue:[ASINetworkQueue queue]]; - [[self queue] setDelegate:self]; - [[self queue] setRequestDidFinishSelector:@selector(requestDone:)]; - [[self queue] setRequestDidFailSelector:@selector(requestFailed:)]; - - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/basic-authentication"]]; - [[self queue] addOperation:request]; - - [queue go]; - - while (![self complete]) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } -} - -- (void)requestDone:(ASIHTTPRequest *)request -{ - [self setComplete:YES]; -} - -- (void)authenticationNeededForRequest:(ASIHTTPRequest *)request -{ - [request setUsername:@"secret_username"]; - [request setPassword:@"secret_password"]; - [request retryUsingSuppliedCredentials]; -} - - -@synthesize queue; -@synthesize complete; -@end diff --git a/socketio/Tests/StressTests.h b/socketio/Tests/StressTests.h deleted file mode 100644 index cc643cd..0000000 --- a/socketio/Tests/StressTests.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// StressTests.h -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 30/10/2009. -// Copyright 2009 All-Seeing Interactive. All rights reserved. -// - -#import -#import "ASITestCase.h" - -@class ASIHTTPRequest; - - -@interface MyDelegate : NSObject { - ASIHTTPRequest *request; -} -@property (retain) ASIHTTPRequest *request; -@end - -@interface StressTests : ASITestCase { - float progress; - ASIHTTPRequest *cancelRequest; - NSDate *cancelStartDate; - MyDelegate *delegate; - NSLock *createRequestLock; -} - -- (void)testCancelQueue; - -- (void)testCancelStressTest; -- (void)performCancelRequest; - -- (void)testRedirectStressTest; -- (void)performRedirectRequest; - -- (void)testSetDelegate; -- (void)performSetDelegateRequest; - -- (void)setProgress:(float)newProgress; - -@property (retain) ASIHTTPRequest *cancelRequest; -@property (retain) NSDate *cancelStartDate; -@property (retain) MyDelegate *delegate; -@property (retain) NSLock *createRequestLock; -@end diff --git a/socketio/Tests/StressTests.m b/socketio/Tests/StressTests.m deleted file mode 100644 index 1ae0b00..0000000 --- a/socketio/Tests/StressTests.m +++ /dev/null @@ -1,192 +0,0 @@ -// -// StressTests.m -// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest -// -// Created by Ben Copsey on 30/10/2009. -// Copyright 2009 All-Seeing Interactive. All rights reserved. -// - -/* -IMPORTANT -All these tests depend on you running a local webserver on port 80 -These tests create 1000s of requests in a very short space of time - DO NOT RUN THESE TESTS ON A REMOTE WEBSERVER -IMPORTANT -*/ - -#import "StressTests.h" -#import "ASIHTTPRequest.h" -#import "ASINetworkQueue.h" - - - -@implementation MyDelegate; -- (void)dealloc -{ - [request setDelegate:nil]; - [request release]; - [super dealloc]; -} -@synthesize request; -@end - -// Stop clang complaining about undeclared selectors -@interface StressTests () -- (void)cancelRedirectRequest; -- (void)cancelSetDelegateRequest; -@end - - - -@implementation StressTests - -// A test for a potential crasher that used to exist when requests were cancelled -// We aren't testing a specific condition here, but rather attempting to trigger a crash -- (void)testCancelQueue -{ - ASINetworkQueue *queue = [ASINetworkQueue queue]; - - // Increase the risk of this crash - [queue setMaxConcurrentOperationCount:25]; - int i; - for (i=0; i<100; i++) { - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://127.0.0.1"]]; - [queue addOperation:request]; - } - [queue go]; - [queue cancelAllOperations]; - - // Run the test again with requests running on a background thread - queue = [ASINetworkQueue queue]; - - [queue setMaxConcurrentOperationCount:25]; - for (i=0; i<100; i++) { - ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://127.0.0.1"]]; - [queue addOperation:request]; - } - [queue go]; - [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]]; - [queue cancelAllOperations]; - -} - -// This test looks for thread-safety problems with cancelling requests -// It will run for 30 seconds, creating a request, then cancelling it and creating another as soon as it gets some indication of progress - -- (void)testCancelStressTest -{ - [self setCancelStartDate:[NSDate dateWithTimeIntervalSinceNow:30]]; - [self performCancelRequest]; - while ([[self cancelStartDate] timeIntervalSinceNow] > 0) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } - NSLog(@"Stress test: DONE"); -} - -- (void)performCancelRequest -{ - [self setCancelRequest:[ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://127.0.0.1/ASIHTTPRequest/tests/the_great_american_novel.txt"]]]; - if ([[self cancelStartDate] timeIntervalSinceNow] > 0) { - [[self cancelRequest] setDownloadProgressDelegate:self]; - [[self cancelRequest] setShowAccurateProgress:YES]; - NSLog(@"Stress test: Start request %@",[self cancelRequest]); - [[self cancelRequest] startAsynchronous]; - } -} - - -// Another stress test that looks from problems when redirecting - -- (void)testRedirectStressTest -{ - [self setCancelStartDate:[NSDate dateWithTimeIntervalSinceNow:30]]; - [self performRedirectRequest]; - while ([[self cancelStartDate] timeIntervalSinceNow] > 0) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } - NSLog(@"Redirect stress test: DONE"); -} - -- (void)performRedirectRequest -{ - [self setCancelRequest:[ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://127.0.0.1/ASIHTTPRequest/tests/one_infinite_loop"]]]; - if ([[self cancelStartDate] timeIntervalSinceNow] > 0) { - NSLog(@"Redirect stress test: Start request %@",[self cancelRequest]); - [[self cancelRequest] startAsynchronous]; - [self performSelector:@selector(cancelRedirectRequest) withObject:nil afterDelay:0.2]; - } -} - -- (void)cancelRedirectRequest -{ - NSLog(@"Redirect stress test: Cancel request %@",[self cancelRequest]); - [[self cancelRequest] cancel]; - [self performRedirectRequest]; -} - -// Ensures we can set the delegate while the request is running without problems -- (void)testSetDelegate -{ - [self setCreateRequestLock:[[[NSLock alloc] init] autorelease]]; - [self setCancelStartDate:[NSDate dateWithTimeIntervalSinceNow:30]]; - [self performSetDelegateRequest]; - while ([[self cancelStartDate] timeIntervalSinceNow] > 0) { - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]]; - } - NSLog(@"Set delegate stress test: DONE"); -} - -- (void)performSetDelegateRequest -{ - [self setDelegate:nil]; - - [createRequestLock lock]; - [self setCancelRequest:[ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://127.0.0.1/ASIHTTPRequest/tests/the_great_american_novel.txt"]]]; - if ([[self cancelStartDate] timeIntervalSinceNow] > 0) { - [self setDelegate:[[[MyDelegate alloc] init] autorelease]]; - [[self delegate] setRequest:[self cancelRequest]]; - [[self cancelRequest] setDelegate:delegate]; - [[self cancelRequest] setShowAccurateProgress:YES]; - NSLog(@"Set delegate stress test: Start request %@",[self cancelRequest]); - [[self cancelRequest] startAsynchronous]; - [self performSelectorInBackground:@selector(cancelSetDelegateRequest) withObject:nil]; - } - [createRequestLock unlock]; -} - -- (void)cancelSetDelegateRequest -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - [self performSetDelegateRequest]; - [pool release]; -} - - -- (void)setDoubleValue:(double)newProgress -{ - [self setProgress:(float)newProgress]; -} - -- (void)setProgress:(float)newProgress -{ - progress = newProgress; - - // For cancel test - if (newProgress > 0 && [self cancelRequest]) { - - NSLog(@"Stress test: Cancel request %@",[self cancelRequest]); - [[self cancelRequest] cancel]; - - [self performSelector:@selector(performCancelRequest) withObject:nil afterDelay:0.2]; - [self setCancelRequest:nil]; - } -} - - - - - -@synthesize cancelRequest; -@synthesize cancelStartDate; -@synthesize delegate; -@synthesize createRequestLock; -@end diff --git a/socketio/en.lproj/MainWindow.xib b/socketio/en.lproj/MainWindow.xib deleted file mode 100644 index 1d68820..0000000 --- a/socketio/en.lproj/MainWindow.xib +++ /dev/null @@ -1,444 +0,0 @@ - - - - 1024 - 10D571 - 786 - 1038.29 - 460.00 - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 112 - - - YES - - - - YES - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - YES - - YES - - - YES - - - - YES - - IBFilesOwner - IBCocoaTouchFramework - - - IBFirstResponder - IBCocoaTouchFramework - - - IBCocoaTouchFramework - - - socketioViewController - - - 1 - - IBCocoaTouchFramework - NO - - - - 292 - {320, 480} - - 1 - MSAxIDEAA - - NO - NO - - IBCocoaTouchFramework - YES - - - - - YES - - - delegate - - - - 4 - - - - viewController - - - - 11 - - - - window - - - - 14 - - - - - YES - - 0 - - - - - - -1 - - - File's Owner - - - 3 - - - socketio App Delegate - - - -2 - - - - - 10 - - - - - 12 - - - - - - - YES - - YES - -1.CustomClassName - -2.CustomClassName - 10.CustomClassName - 10.IBEditorWindowLastContentRect - 10.IBPluginDependency - 12.IBEditorWindowLastContentRect - 12.IBPluginDependency - 3.CustomClassName - 3.IBPluginDependency - - - YES - UIApplication - UIResponder - socketioViewController - {{234, 376}, {320, 480}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - {{525, 346}, {320, 480}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - socketioAppDelegate - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - - YES - - - YES - - - - - YES - - - YES - - - - 15 - - - - YES - - UIWindow - UIView - - IBUserSource - - - - - socketioAppDelegate - NSObject - - YES - - YES - viewController - window - - - YES - socketioViewController - UIWindow - - - - YES - - YES - viewController - window - - - YES - - viewController - socketioViewController - - - window - UIWindow - - - - - IBProjectSource - socketioAppDelegate.h - - - - socketioAppDelegate - NSObject - - IBUserSource - - - - - socketioViewController - UIViewController - - IBProjectSource - socketioViewController.h - - - - - YES - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSError.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSFileManager.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyValueCoding.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyValueObserving.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyedArchiver.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSObject.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSRunLoop.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSThread.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSURL.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSURLConnection.h - - - - NSObject - - IBFrameworkSource - UIKit.framework/Headers/UIAccessibility.h - - - - NSObject - - IBFrameworkSource - UIKit.framework/Headers/UINibLoading.h - - - - NSObject - - IBFrameworkSource - UIKit.framework/Headers/UIResponder.h - - - - UIApplication - UIResponder - - IBFrameworkSource - UIKit.framework/Headers/UIApplication.h - - - - UIResponder - NSObject - - - - UISearchBar - UIView - - IBFrameworkSource - UIKit.framework/Headers/UISearchBar.h - - - - UISearchDisplayController - NSObject - - IBFrameworkSource - UIKit.framework/Headers/UISearchDisplayController.h - - - - UIView - - IBFrameworkSource - UIKit.framework/Headers/UITextField.h - - - - UIView - UIResponder - - IBFrameworkSource - UIKit.framework/Headers/UIView.h - - - - UIViewController - - IBFrameworkSource - UIKit.framework/Headers/UINavigationController.h - - - - UIViewController - - IBFrameworkSource - UIKit.framework/Headers/UIPopoverController.h - - - - UIViewController - - IBFrameworkSource - UIKit.framework/Headers/UISplitViewController.h - - - - UIViewController - - IBFrameworkSource - UIKit.framework/Headers/UITabBarController.h - - - - UIViewController - UIResponder - - IBFrameworkSource - UIKit.framework/Headers/UIViewController.h - - - - UIWindow - UIView - - IBFrameworkSource - UIKit.framework/Headers/UIWindow.h - - - - - 0 - IBCocoaTouchFramework - - com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - - - - com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 - - - YES - socketio.xcodeproj - 3 - 112 - - diff --git a/socketio/en.lproj/socketioViewController.xib b/socketio/en.lproj/socketioViewController.xib deleted file mode 100644 index ea21486..0000000 --- a/socketio/en.lproj/socketioViewController.xib +++ /dev/null @@ -1,271 +0,0 @@ - - - - 1056 - 11B26 - 1617 - 1138 - 566.00 - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 534 - - - YES - IBUITextView - IBUIView - IBUITextField - IBProxyObject - - - YES - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - YES - - YES - - - - - YES - - IBFilesOwner - IBCocoaTouchFramework - - - IBFirstResponder - IBCocoaTouchFramework - - - - 274 - - YES - - - 274 - {320, 429} - - - - _NS:613 - - 1 - MSAxIDEAA - - YES - YES - IBCocoaTouchFramework - - - 2 - IBCocoaTouchFramework - - - - - 292 - {{0, 429}, {320, 31}} - - - _NS:294 - NO - YES - IBCocoaTouchFramework - 0 - - 3 - - 3 - MAA - - 2 - - - YES - 17 - - 7 - IBCocoaTouchFramework - - - - {{0, 20}, {320, 460}} - - - - - 3 - MC43NQA - - - NO - - IBCocoaTouchFramework - - - - - YES - - - view - - - - 7 - - - - txtview - - - - 10 - - - - txtfield - - - - 11 - - - - - YES - - 0 - - - - - - -1 - - - File's Owner - - - -2 - - - - - 6 - - - YES - - - - - - - 8 - - - - - 9 - - - - - - - YES - - YES - -1.CustomClassName - -1.IBPluginDependency - -2.CustomClassName - -2.IBPluginDependency - 6.IBPluginDependency - 8.IBPluginDependency - 9.IBPluginDependency - - - YES - socketioViewController - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - UIResponder - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - - YES - - - - - - YES - - - - - 11 - - - - YES - - socketioViewController - UIViewController - - YES - - YES - txtfield - txtview - - - YES - UITextField - UITextView - - - - YES - - YES - txtfield - txtview - - - YES - - txtfield - UITextField - - - txtview - UITextView - - - - - IBProjectSource - ./Classes/socketioViewController.h - - - - - 0 - IBCocoaTouchFramework - - com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 - - - YES - 3 - 534 - - diff --git a/socketio/main.m b/socketio/main.m deleted file mode 100644 index d5685e5..0000000 --- a/socketio/main.m +++ /dev/null @@ -1,17 +0,0 @@ -// -// main.m -// socketio -// -// Created by Htain Lin Shwe on 25/9/11. -// Copyright 2011 __MyCompanyName__. All rights reserved. -// - -#import - -int main(int argc, char *argv[]) -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - int retVal = UIApplicationMain(argc, argv, nil, nil); - [pool release]; - return retVal; -} diff --git a/socketio/socketio-Prefix.pch b/socketio/socketio-Prefix.pch deleted file mode 100644 index 3caf861..0000000 --- a/socketio/socketio-Prefix.pch +++ /dev/null @@ -1,14 +0,0 @@ -// -// Prefix header for all source files of the 'socketio' target in the 'socketio' project -// - -#import - -#ifndef __IPHONE_3_0 -#warning "This project uses features only available in iPhone SDK 3.0 and later." -#endif - -#ifdef __OBJC__ - #import - #import -#endif diff --git a/socketio/socketioAppDelegate.h b/socketio/socketioAppDelegate.h deleted file mode 100644 index 9011ed2..0000000 --- a/socketio/socketioAppDelegate.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// socketioAppDelegate.h -// socketio -// -// Created by Htain Lin Shwe on 25/9/11. -// Copyright 2011 __MyCompanyName__. All rights reserved. -// - -#import - -@class socketioViewController; - -@interface socketioAppDelegate : NSObject - -@property (nonatomic, retain) IBOutlet UIWindow *window; - -@property (nonatomic, retain) IBOutlet socketioViewController *viewController; - -@end diff --git a/socketio/socketioAppDelegate.m b/socketio/socketioAppDelegate.m deleted file mode 100644 index 0a48cfe..0000000 --- a/socketio/socketioAppDelegate.m +++ /dev/null @@ -1,73 +0,0 @@ -// -// socketioAppDelegate.m -// socketio -// -// Created by Htain Lin Shwe on 25/9/11. -// Copyright 2011 __MyCompanyName__. All rights reserved. -// - -#import "socketioAppDelegate.h" - -#import "socketioViewController.h" - -@implementation socketioAppDelegate - -@synthesize window = _window; -@synthesize viewController = _viewController; - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - // Override point for customization after application launch. - - self.window.rootViewController = self.viewController; - [self.window makeKeyAndVisible]; - return YES; -} - -- (void)applicationWillResignActive:(UIApplication *)application -{ - /* - 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. - 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. - */ -} - -- (void)applicationDidEnterBackground:(UIApplication *)application -{ - /* - 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. - If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. - */ -} - -- (void)applicationWillEnterForeground:(UIApplication *)application -{ - /* - 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. - */ -} - -- (void)applicationDidBecomeActive:(UIApplication *)application -{ - /* - 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. - */ -} - -- (void)applicationWillTerminate:(UIApplication *)application -{ - /* - Called when the application is about to terminate. - Save data if appropriate. - See also applicationDidEnterBackground:. - */ -} - -- (void)dealloc -{ - [_window release]; - [_viewController release]; - [super dealloc]; -} - -@end diff --git a/socketio/socketioViewController.h b/socketio/socketioViewController.h deleted file mode 100644 index f7b9ab8..0000000 --- a/socketio/socketioViewController.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// socketioViewController.h -// socketio -// -// Created by Htain Lin Shwe on 25/9/11. -// Copyright 2011 __MyCompanyName__. All rights reserved. -// - -#import -#import "SocketIO.h" -@interface socketioViewController : UIViewController -@property(nonatomic,retain) IBOutlet UITextView* txtview; -@property(nonatomic,retain) IBOutlet UITextField* txtfield; -@property(nonatomic,retain) SocketIO* socektio; -@end diff --git a/socketio/socketioViewController.m b/socketio/socketioViewController.m deleted file mode 100644 index 6d3b9e9..0000000 --- a/socketio/socketioViewController.m +++ /dev/null @@ -1,70 +0,0 @@ -// -// socketioViewController.m -// socketio -// -// Created by Htain Lin Shwe on 25/9/11. -// Copyright 2011 __MyCompanyName__. All rights reserved. -// - -#import "socketioViewController.h" -#import "SBJson.h" - -#define SOCKET @"192.168.1.200" -#define SOCKET_PORT 3000 -@implementation socketioViewController -@synthesize socektio; -@synthesize txtview,txtfield; - --(void)dealloc { - [socektio release]; - [super dealloc]; -} -- (void)didReceiveMemoryWarning -{ - // Releases the view if it doesn't have a superview. - [super didReceiveMemoryWarning]; - - // Release any cached data, images, etc that aren't in use. -} - -#pragma mark - View lifecycle - - -// Implement viewDidLoad to do additional setup after loading the view, typically from a nib. -- (void)viewDidLoad -{ - [super viewDidLoad]; - socektio = [[SocketIO alloc] initWithDelegate:self]; - [socektio connectToHost:SOCKET onPort:SOCKET_PORT]; -} - - -- (void)viewDidUnload -{ - [super viewDidUnload]; - // Release any retained subviews of the main view. - // e.g. self.myOutlet = nil; -} - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - // Return YES for supported orientations - return (interfaceOrientation == UIInterfaceOrientationPortrait); -} - - -#pragma mark - API -- (void) socketIO:(SocketIO *)socket didReceiveEvent:(SocketIOPacket *)packet -{ - //NSLog(@"didReceiveMessage() >>> data: %@", packet.data); - NSDictionary* data=[packet.data JSONValue]; - NSArray* arr=[data objectForKey:@"args"]; - NSString* str=[NSString stringWithFormat:@"\n[%@]:%@",[arr objectAtIndex:0],[arr objectAtIndex:1]]; - txtview.text = [txtview.text stringByAppendingString:str]; - -} - -//- (void) socketIO:(SocketIO *)socket didReceiveJSON:(SocketIOPacket *)packet { -// NSLog(@"JSON didReceiveMessage() >>> data: %@", packet.data); -//} -@end diff --git a/sockio-server/app.js b/sockio-server/app.js new file mode 100644 index 0000000..344afb1 --- /dev/null +++ b/sockio-server/app.js @@ -0,0 +1,39 @@ +//require necessary modules +var http = require('http') + , express = require('express') + , socketIO = require("socket.io") + , path = require('path'); + +//initialize our application +var app = express(); +app.use(express.static(path.join(__dirname, 'assets'))); +var server = http.createServer(app).listen(3000); +var io = socketIO.listen(server); + +//settings +var settings = { + 'view_directory': '/views' +} + + +app.get('/', function(request, response){ + response.sendfile(__dirname + settings.view_directory + '/index.html') +}); + + +//chat using socket.io +io.sockets.on('connection', function(client){ + //when client sends a join event + client.on('join', function(data){ + client.set('nickname', data); + client.broadcast.emit('message', { message: data + " just joined!", nickname: "Server Announcement" }); + }); + + //when client sends a message + client.on('message', function(data){ + client.get('nickname', function(err, nickname){ + client.broadcast.emit('message', { message: data, nickname: nickname }); + }); + }); + +}) \ No newline at end of file diff --git a/sockio-server/assets/css/bootstrap.min.css b/sockio-server/assets/css/bootstrap.min.css new file mode 100644 index 0000000..a553c4f --- /dev/null +++ b/sockio-server/assets/css/bootstrap.min.css @@ -0,0 +1,9 @@ +/*! + * Bootstrap v3.0.0 + * + * Copyright 2013 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world by @mdo and @fat. + *//*! normalize.css v2.1.0 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{margin:.67em 0;font-size:2em}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}hr{height:0;-moz-box-sizing:content-box;box-sizing:content-box}mark{color:#000;background:#ff0}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid #c0c0c0}legend{padding:0;border:0}button,input,select,textarea{margin:0;font-family:inherit;font-size:100%}button,input{line-height:normal}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}button[disabled],html input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{padding:0;box-sizing:border-box}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}@page{margin:2cm .5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.table td,.table th{background-color:#fff!important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}*,*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:62.5%;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.428571429;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}button,input,select[multiple],textarea{background-image:none}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}img{vertical-align:middle}.img-responsive{display:block;height:auto;max-width:100%}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;height:auto;max-width:100%;padding:4px;line-height:1.428571429;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);border:0}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16.099999999999998px;font-weight:200;line-height:1.4}@media(min-width:768px){.lead{font-size:21px}}small{font-size:85%}cite{font-style:normal}.text-muted{color:#999}.text-primary{color:#428bca}.text-warning{color:#c09853}.text-danger{color:#b94a48}.text-success{color:#468847}.text-info{color:#3a87ad}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:500;line-height:1.1}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small{font-weight:normal;line-height:1;color:#999}h1,h2,h3{margin-top:20px;margin-bottom:10px}h4,h5,h6{margin-top:10px;margin-bottom:10px}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}h1 small,.h1 small{font-size:24px}h2 small,.h2 small{font-size:18px}h3 small,.h3 small,h4 small,.h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-bottom:20px}dt,dd{line-height:1.428571429}dt{font-weight:bold}dd{margin-left:0}@media(min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}.dl-horizontal dd:before,.dl-horizontal dd:after{display:table;content:" "}.dl-horizontal dd:after{clear:both}.dl-horizontal dd:before,.dl-horizontal dd:after{display:table;content:" "}.dl-horizontal dd:after{clear:both}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{font-size:17.5px;font-weight:300;line-height:1.25}blockquote p:last-child{margin-bottom:0}blockquote small{display:block;line-height:1.428571429;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:1.428571429}code,pre{font-family:Monaco,Menlo,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;white-space:nowrap;background-color:#f9f2f4;border-radius:4px}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.428571429;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.container:before,.container:after{display:table;content:" "}.container:after{clear:both}.container:before,.container:after{display:table;content:" "}.container:after{clear:both}.row{margin-right:-15px;margin-left:-15px}.row:before,.row:after{display:table;content:" "}.row:after{clear:both}.row:before,.row:after{display:table;content:" "}.row:after{clear:both}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12,.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12,.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12,.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11{float:left}.col-xs-1{width:8.333333333333332%}.col-xs-2{width:16.666666666666664%}.col-xs-3{width:25%}.col-xs-4{width:33.33333333333333%}.col-xs-5{width:41.66666666666667%}.col-xs-6{width:50%}.col-xs-7{width:58.333333333333336%}.col-xs-8{width:66.66666666666666%}.col-xs-9{width:75%}.col-xs-10{width:83.33333333333334%}.col-xs-11{width:91.66666666666666%}.col-xs-12{width:100%}@media(min-width:768px){.container{max-width:750px}.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11{float:left}.col-sm-1{width:8.333333333333332%}.col-sm-2{width:16.666666666666664%}.col-sm-3{width:25%}.col-sm-4{width:33.33333333333333%}.col-sm-5{width:41.66666666666667%}.col-sm-6{width:50%}.col-sm-7{width:58.333333333333336%}.col-sm-8{width:66.66666666666666%}.col-sm-9{width:75%}.col-sm-10{width:83.33333333333334%}.col-sm-11{width:91.66666666666666%}.col-sm-12{width:100%}.col-sm-push-1{left:8.333333333333332%}.col-sm-push-2{left:16.666666666666664%}.col-sm-push-3{left:25%}.col-sm-push-4{left:33.33333333333333%}.col-sm-push-5{left:41.66666666666667%}.col-sm-push-6{left:50%}.col-sm-push-7{left:58.333333333333336%}.col-sm-push-8{left:66.66666666666666%}.col-sm-push-9{left:75%}.col-sm-push-10{left:83.33333333333334%}.col-sm-push-11{left:91.66666666666666%}.col-sm-pull-1{right:8.333333333333332%}.col-sm-pull-2{right:16.666666666666664%}.col-sm-pull-3{right:25%}.col-sm-pull-4{right:33.33333333333333%}.col-sm-pull-5{right:41.66666666666667%}.col-sm-pull-6{right:50%}.col-sm-pull-7{right:58.333333333333336%}.col-sm-pull-8{right:66.66666666666666%}.col-sm-pull-9{right:75%}.col-sm-pull-10{right:83.33333333333334%}.col-sm-pull-11{right:91.66666666666666%}.col-sm-offset-1{margin-left:8.333333333333332%}.col-sm-offset-2{margin-left:16.666666666666664%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-4{margin-left:33.33333333333333%}.col-sm-offset-5{margin-left:41.66666666666667%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-7{margin-left:58.333333333333336%}.col-sm-offset-8{margin-left:66.66666666666666%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-10{margin-left:83.33333333333334%}.col-sm-offset-11{margin-left:91.66666666666666%}}@media(min-width:992px){.container{max-width:970px}.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11{float:left}.col-md-1{width:8.333333333333332%}.col-md-2{width:16.666666666666664%}.col-md-3{width:25%}.col-md-4{width:33.33333333333333%}.col-md-5{width:41.66666666666667%}.col-md-6{width:50%}.col-md-7{width:58.333333333333336%}.col-md-8{width:66.66666666666666%}.col-md-9{width:75%}.col-md-10{width:83.33333333333334%}.col-md-11{width:91.66666666666666%}.col-md-12{width:100%}.col-md-push-0{left:auto}.col-md-push-1{left:8.333333333333332%}.col-md-push-2{left:16.666666666666664%}.col-md-push-3{left:25%}.col-md-push-4{left:33.33333333333333%}.col-md-push-5{left:41.66666666666667%}.col-md-push-6{left:50%}.col-md-push-7{left:58.333333333333336%}.col-md-push-8{left:66.66666666666666%}.col-md-push-9{left:75%}.col-md-push-10{left:83.33333333333334%}.col-md-push-11{left:91.66666666666666%}.col-md-pull-0{right:auto}.col-md-pull-1{right:8.333333333333332%}.col-md-pull-2{right:16.666666666666664%}.col-md-pull-3{right:25%}.col-md-pull-4{right:33.33333333333333%}.col-md-pull-5{right:41.66666666666667%}.col-md-pull-6{right:50%}.col-md-pull-7{right:58.333333333333336%}.col-md-pull-8{right:66.66666666666666%}.col-md-pull-9{right:75%}.col-md-pull-10{right:83.33333333333334%}.col-md-pull-11{right:91.66666666666666%}.col-md-offset-0{margin-left:0}.col-md-offset-1{margin-left:8.333333333333332%}.col-md-offset-2{margin-left:16.666666666666664%}.col-md-offset-3{margin-left:25%}.col-md-offset-4{margin-left:33.33333333333333%}.col-md-offset-5{margin-left:41.66666666666667%}.col-md-offset-6{margin-left:50%}.col-md-offset-7{margin-left:58.333333333333336%}.col-md-offset-8{margin-left:66.66666666666666%}.col-md-offset-9{margin-left:75%}.col-md-offset-10{margin-left:83.33333333333334%}.col-md-offset-11{margin-left:91.66666666666666%}}@media(min-width:1200px){.container{max-width:1170px}.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11{float:left}.col-lg-1{width:8.333333333333332%}.col-lg-2{width:16.666666666666664%}.col-lg-3{width:25%}.col-lg-4{width:33.33333333333333%}.col-lg-5{width:41.66666666666667%}.col-lg-6{width:50%}.col-lg-7{width:58.333333333333336%}.col-lg-8{width:66.66666666666666%}.col-lg-9{width:75%}.col-lg-10{width:83.33333333333334%}.col-lg-11{width:91.66666666666666%}.col-lg-12{width:100%}.col-lg-push-0{left:auto}.col-lg-push-1{left:8.333333333333332%}.col-lg-push-2{left:16.666666666666664%}.col-lg-push-3{left:25%}.col-lg-push-4{left:33.33333333333333%}.col-lg-push-5{left:41.66666666666667%}.col-lg-push-6{left:50%}.col-lg-push-7{left:58.333333333333336%}.col-lg-push-8{left:66.66666666666666%}.col-lg-push-9{left:75%}.col-lg-push-10{left:83.33333333333334%}.col-lg-push-11{left:91.66666666666666%}.col-lg-pull-0{right:auto}.col-lg-pull-1{right:8.333333333333332%}.col-lg-pull-2{right:16.666666666666664%}.col-lg-pull-3{right:25%}.col-lg-pull-4{right:33.33333333333333%}.col-lg-pull-5{right:41.66666666666667%}.col-lg-pull-6{right:50%}.col-lg-pull-7{right:58.333333333333336%}.col-lg-pull-8{right:66.66666666666666%}.col-lg-pull-9{right:75%}.col-lg-pull-10{right:83.33333333333334%}.col-lg-pull-11{right:91.66666666666666%}.col-lg-offset-0{margin-left:0}.col-lg-offset-1{margin-left:8.333333333333332%}.col-lg-offset-2{margin-left:16.666666666666664%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-4{margin-left:33.33333333333333%}.col-lg-offset-5{margin-left:41.66666666666667%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-7{margin-left:58.333333333333336%}.col-lg-offset-8{margin-left:66.66666666666666%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-10{margin-left:83.33333333333334%}.col-lg-offset-11{margin-left:91.66666666666666%}}table{max-width:100%;background-color:transparent}th{text-align:left}.table{width:100%;margin-bottom:20px}.table thead>tr>th,.table tbody>tr>th,.table tfoot>tr>th,.table thead>tr>td,.table tbody>tr>td,.table tfoot>tr>td{padding:8px;line-height:1.428571429;vertical-align:top;border-top:1px solid #ddd}.table thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table caption+thead tr:first-child th,.table colgroup+thead tr:first-child th,.table thead:first-child tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed thead>tr>th,.table-condensed tbody>tr>th,.table-condensed tfoot>tr>th,.table-condensed thead>tr>td,.table-condensed tbody>tr>td,.table-condensed tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class*="col-"]{display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{display:table-cell;float:none}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8;border-color:#d6e9c6}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td{background-color:#d0e9c6;border-color:#c9e2b3}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede;border-color:#eed3d7}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td{background-color:#ebcccc;border-color:#e6c1c7}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3;border-color:#fbeed5}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td{background-color:#faf2cc;border-color:#f8e5be}@media(max-width:768px){.table-responsive{width:100%;margin-bottom:15px;overflow-x:scroll;overflow-y:hidden;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0;background-color:#fff}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>thead>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>thead>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}select[multiple],select[size]{height:auto}select optgroup{font-family:inherit;font-size:inherit;font-style:inherit}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}input[type="number"]::-webkit-outer-spin-button,input[type="number"]::-webkit-inner-spin-button{height:auto}.form-control:-moz-placeholder{color:#999}.form-control::-moz-placeholder{color:#999}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.428571429;color:#555;vertical-align:middle;background-color:#fff;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee}textarea.form-control{height:auto}.form-group{margin-bottom:15px}.radio,.checkbox{display:block;min-height:20px;padding-left:20px;margin-top:10px;margin-bottom:10px;vertical-align:middle}.radio label,.checkbox label{display:inline;margin-bottom:0;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{float:left;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;font-weight:normal;vertical-align:middle;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="checkbox"][disabled],.radio[disabled],.radio-inline[disabled],.checkbox[disabled],.checkbox-inline[disabled],fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"],fieldset[disabled] .radio,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm{height:auto}.input-lg{height:45px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg{height:45px;line-height:45px}textarea.input-lg{height:auto}.has-warning .help-block,.has-warning .control-label{color:#c09853}.has-warning .form-control{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.has-warning .input-group-addon{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.has-error .help-block,.has-error .control-label{color:#b94a48}.has-error .form-control{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.has-error .input-group-addon{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.has-success .help-block,.has-success .control-label{color:#468847}.has-success .form-control{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.has-success .input-group-addon{color:#468847;background-color:#dff0d8;border-color:#468847}.form-control-static{padding-top:7px;margin-bottom:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media(min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block}.form-inline .radio,.form-inline .checkbox{display:inline-block;padding-left:0;margin-top:0;margin-bottom:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:none;margin-left:0}}.form-horizontal .control-label,.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}.form-horizontal .form-group:before,.form-horizontal .form-group:after{display:table;content:" "}.form-horizontal .form-group:after{clear:both}.form-horizontal .form-group:before,.form-horizontal .form-group:after{display:table;content:" "}.form-horizontal .form-group:after{clear:both}@media(min-width:768px){.form-horizontal .control-label{text-align:right}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:normal;line-height:1.428571429;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;border:1px solid transparent;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#333;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{pointer-events:none;cursor:not-allowed;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{color:#333;background-color:#ebebeb;border-color:#adadad}.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{color:#fff;background-color:#3276b1;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{color:#fff;background-color:#ed9c28;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{color:#fff;background-color:#d2322d;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{color:#fff;background-color:#47a447;border-color:#398439}.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{color:#fff;background-color:#39b3d7;border-color:#269abc}.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-link{font-weight:normal;color:#428bca;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999;text-decoration:none}.btn-lg{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-xs{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs{padding:1px 5px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;transition:height .35s ease}@font-face{font-family:'Glyphicons Halflings';src:url('../fonts/glyphicons-halflings-regular.eot');src:url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),url('../fonts/glyphicons-halflings-regular.woff') format('woff'),url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'),url('../fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';-webkit-font-smoothing:antialiased;font-style:normal;font-weight:normal;line-height:1}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-print:before{content:"\e045"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-briefcase:before{content:"\1f4bc"}.glyphicon-calendar:before{content:"\1f4c5"}.glyphicon-pushpin:before{content:"\1f4cc"}.glyphicon-paperclip:before{content:"\1f4ce"}.glyphicon-camera:before{content:"\1f4f7"}.glyphicon-lock:before{content:"\1f512"}.glyphicon-bell:before{content:"\1f514"}.glyphicon-bookmark:before{content:"\1f516"}.glyphicon-fire:before{content:"\1f525"}.glyphicon-wrench:before{content:"\1f527"}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid #000;border-right:4px solid transparent;border-bottom:0 dotted;border-left:4px solid transparent;content:""}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.428571429;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#fff;text-decoration:none;background-color:#428bca}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#428bca;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.428571429;color:#999}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0 dotted;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media(min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}}.btn-default .caret{border-top-color:#333}.btn-primary .caret,.btn-success .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret{border-top-color:#fff}.dropup .btn-default .caret{border-bottom-color:#333}.dropup .btn-primary .caret,.dropup .btn-success .caret,.dropup .btn-warning .caret,.dropup .btn-danger .caret,.dropup .btn-info .caret{border-bottom-color:#fff}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:0}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar:before,.btn-toolbar:after{display:table;content:" "}.btn-toolbar:after{clear:both}.btn-toolbar:before,.btn-toolbar:after{display:table;content:" "}.btn-toolbar:after{clear:both}.btn-toolbar .btn-group{float:left}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group,.btn-toolbar>.btn-group+.btn-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group-xs>.btn{padding:5px 10px;padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after{display:table;content:" "}.btn-group-vertical>.btn-group:after{clear:both}.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after{display:table;content:" "}.btn-group-vertical>.btn-group:after{clear:both}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-right-radius:0;border-bottom-left-radius:4px;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child>.btn:last-child,.btn-group-vertical>.btn-group:first-child>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;border-collapse:separate;table-layout:fixed}.btn-group-justified .btn{display:table-cell;float:none;width:1%}[data-toggle="buttons"]>.btn>input[type="radio"],[data-toggle="buttons"]>.btn>input[type="checkbox"]{display:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group.col{float:none;padding-right:0;padding-left:0}.input-group .form-control{width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:45px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:45px;line-height:45px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:normal;line-height:1;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-4px}.input-group-btn>.btn:hover,.input-group-btn>.btn:active{z-index:2}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav:before,.nav:after{display:table;content:" "}.nav:after{clear:both}.nav:before,.nav:after{display:table;content:" "}.nav:after{clear:both}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.428571429;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center}@media(min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}}.nav-tabs.nav-justified>li>a{margin-right:0;border-bottom:1px solid #ddd}.nav-tabs.nav-justified>.active>a{border-bottom-color:#fff}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:5px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center}@media(min-width:768px){.nav-justified>li{display:table-cell;width:1%}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-bottom:1px solid #ddd}.nav-tabs-justified>.active>a{border-bottom-color:#fff}.tabbable:before,.tabbable:after{display:table;content:" "}.tabbable:after{clear:both}.tabbable:before,.tabbable:after{display:table;content:" "}.tabbable:after{clear:both}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.nav .caret{border-top-color:#428bca;border-bottom-color:#428bca}.nav a:hover .caret{border-top-color:#2a6496;border-bottom-color:#2a6496}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;z-index:1000;min-height:50px;margin-bottom:20px;border:1px solid transparent}.navbar:before,.navbar:after{display:table;content:" "}.navbar:after{clear:both}.navbar:before,.navbar:after{display:table;content:" "}.navbar:after{clear:both}@media(min-width:768px){.navbar{border-radius:4px}}.navbar-header:before,.navbar-header:after{display:table;content:" "}.navbar-header:after{clear:both}.navbar-header:before,.navbar-header:after{display:table;content:" "}.navbar-header:after{clear:both}@media(min-width:768px){.navbar-header{float:left}}.navbar-collapse{max-height:340px;padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse:before,.navbar-collapse:after{display:table;content:" "}.navbar-collapse:after{clear:both}.navbar-collapse:before,.navbar-collapse:after{display:table;content:" "}.navbar-collapse:after{clear:both}.navbar-collapse.in{overflow-y:auto}@media(min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-collapse .navbar-nav.navbar-left:first-child{margin-left:-15px}.navbar-collapse .navbar-nav.navbar-right:last-child{margin-right:-15px}.navbar-collapse .navbar-text:last-child{margin-right:0}}.container>.navbar-header,.container>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media(min-width:768px){.container>.navbar-header,.container>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{border-width:0 0 1px}@media(min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;border-width:0 0 1px}@media(min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;z-index:1030}.navbar-fixed-bottom{bottom:0;margin-bottom:0}.navbar-brand{float:left;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}@media(min-width:768px){.navbar>.container .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;border:1px solid transparent;border-radius:4px}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media(min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media(max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media(min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}@media(min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}@media(min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;padding-left:0;margin-top:0;margin-bottom:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{float:none;margin-left:0}}@media(max-width:767px){.navbar-form .form-group{margin-bottom:5px}}@media(min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-nav.pull-right>li>.dropdown-menu,.navbar-nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-text{float:left;margin-top:15px;margin-bottom:15px}@media(min-width:768px){.navbar-text{margin-right:15px;margin-left:15px}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#ccc}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e6e6e6}.navbar-default .navbar-nav>.dropdown>a:hover .caret,.navbar-default .navbar-nav>.dropdown>a:focus .caret{border-top-color:#333;border-bottom-color:#333}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.open>a .caret,.navbar-default .navbar-nav>.open>a:hover .caret,.navbar-default .navbar-nav>.open>a:focus .caret{border-top-color:#555;border-bottom-color:#555}.navbar-default .navbar-nav>.dropdown>a .caret{border-top-color:#777;border-bottom-color:#777}@media(max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#999}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .navbar-nav>li>a{color:#999}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.dropdown>a:hover .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-nav>.dropdown>a .caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .navbar-nav>.open>a .caret,.navbar-inverse .navbar-nav>.open>a:hover .caret,.navbar-inverse .navbar-nav>.open>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}@media(max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#999}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#999}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.428571429;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{background-color:#eee}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;cursor:default;background-color:#428bca;border-color:#428bca}.pagination>.disabled>span,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager:before,.pager:after{display:table;content:" "}.pager:after{clear:both}.pager:before,.pager:after{display:table;content:" "}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.label[href]:hover,.label[href]:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.label-default{background-color:#999}.label-default[href]:hover,.label-default[href]:focus{background-color:#808080}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;background-color:#999;border-radius:10px}.badge:empty{display:none}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.btn .badge{position:relative;top:-1px}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px;margin-bottom:30px;font-size:21px;font-weight:200;line-height:2.1428571435;color:inherit;background-color:#eee}.jumbotron h1{line-height:1;color:inherit}.jumbotron p{line-height:1.4}.container .jumbotron{border-radius:6px}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1{font-size:63px}}.thumbnail{display:inline-block;display:block;height:auto;max-width:100%;padding:4px;line-height:1.428571429;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.thumbnail>img{display:block;height:auto;max-width:100%}a.thumbnail:hover,a.thumbnail:focus{border-color:#428bca}.thumbnail>img{margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable{padding-right:35px}.alert-dismissable .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#356635}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#2d6987}.alert-warning{color:#c09853;background-color:#fcf8e3;border-color:#fbeed5}.alert-warning hr{border-top-color:#f8e5be}.alert-warning .alert-link{color:#a47e3c}.alert-danger{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger hr{border-top-color:#e6c1c7}.alert-danger .alert-link{color:#953b39}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar{background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:40px 40px}.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;background-color:#f5f5f5}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#e1edf7}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-body:before,.panel-body:after{display:table;content:" "}.panel-body:after{clear:both}.panel-body:before,.panel-body:after{display:table;content:" "}.panel-body:after{clear:both}.panel>.list-group{margin-bottom:0}.panel>.list-group .list-group-item{border-width:1px 0}.panel>.list-group .list-group-item:first-child{border-top-right-radius:0;border-top-left-radius:0}.panel>.list-group .list-group-item:last-child{border-bottom:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.panel>.table{margin-bottom:0}.panel>.panel-body+.table{border-top:1px solid #ddd}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-title{margin-top:0;margin-bottom:0;font-size:16px}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-group .panel{margin-bottom:0;overflow:hidden;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse .panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse .panel-body{border-top-color:#ddd}.panel-default>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse .panel-body{border-top-color:#428bca}.panel-primary>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse .panel-body{border-top-color:#d6e9c6}.panel-success>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#d6e9c6}.panel-warning{border-color:#fbeed5}.panel-warning>.panel-heading{color:#c09853;background-color:#fcf8e3;border-color:#fbeed5}.panel-warning>.panel-heading+.panel-collapse .panel-body{border-top-color:#fbeed5}.panel-warning>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#fbeed5}.panel-danger{border-color:#eed3d7}.panel-danger>.panel-heading{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.panel-danger>.panel-heading+.panel-collapse .panel-body{border-top-color:#eed3d7}.panel-danger>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#eed3d7}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse .panel-body{border-top-color:#bce8f1}.panel-info>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#bce8f1}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}body.modal-open,.modal-open .navbar-fixed-top,.modal-open .navbar-fixed-bottom{margin-right:15px}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;display:none;overflow:auto;overflow-y:scroll}.modal.fade .modal-dialog{-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);transform:translate(0,-25%);-webkit-transition:-webkit-transform .3s ease-out;-moz-transition:-moz-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}.modal-dialog{z-index:1050;width:auto;padding:10px;margin-right:auto;margin-left:auto}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);background-clip:padding-box}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1030;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{min-height:16.428571429px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.428571429}.modal-body{position:relative;padding:20px}.modal-footer{padding:19px 20px 20px;margin-top:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer:before,.modal-footer:after{display:table;content:" "}.modal-footer:after{clear:both}.modal-footer:before,.modal-footer:after{display:table;content:" "}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}@media screen and (min-width:768px){.modal-dialog{right:auto;left:50%;width:600px;padding-top:30px;padding-bottom:30px}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}}.tooltip{position:absolute;z-index:1030;display:block;font-size:12px;line-height:1.4;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.9;filter:alpha(opacity=90)}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.top-right .tooltip-arrow{right:5px;bottom:0;border-top-color:#000;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-bottom-color:#000;border-width:0 5px 5px}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-bottom-color:#000;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);background-clip:padding-box}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow{border-width:11px}.popover .arrow:after{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#fff;border-bottom-width:0;content:" "}.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#fff;border-left-width:0;content:" "}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#fff;border-top-width:0;content:" "}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#fff;border-right-width:0;content:" "}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;height:auto;max-width:100%;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);opacity:.5;filter:alpha(opacity=50)}.carousel-control.left{background-image:-webkit-gradient(linear,0 top,100% top,from(rgba(0,0,0,0.5)),to(rgba(0,0,0,0.0001)));background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,0.5) 0),color-stop(rgba(0,0,0,0.0001) 100%));background-image:-moz-linear-gradient(left,rgba(0,0,0,0.5) 0,rgba(0,0,0,0.0001) 100%);background-image:linear-gradient(to right,rgba(0,0,0,0.5) 0,rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000',endColorstr='#00000000',GradientType=1)}.carousel-control.right{right:0;left:auto;background-image:-webkit-gradient(linear,0 top,100% top,from(rgba(0,0,0,0.0001)),to(rgba(0,0,0,0.5)));background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,0.0001) 0),color-stop(rgba(0,0,0,0.5) 100%));background-image:-moz-linear-gradient(left,rgba(0,0,0,0.0001) 0,rgba(0,0,0,0.5) 100%);background-image:linear-gradient(to right,rgba(0,0,0,0.0001) 0,rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000',endColorstr='#80000000',GradientType=1)}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;left:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;margin-left:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;margin-left:-15px;font-size:30px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after{display:table;content:" "}.clearfix:after{clear:both}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.affix{position:fixed}@-ms-viewport{width:device-width}@media screen and (max-width:400px){@-ms-viewport{width:320px}}.hidden{display:none!important;visibility:hidden!important}.visible-xs{display:none!important}tr.visible-xs{display:none!important}th.visible-xs,td.visible-xs{display:none!important}@media(max-width:767px){.visible-xs{display:block!important}tr.visible-xs{display:table-row!important}th.visible-xs,td.visible-xs{display:table-cell!important}}@media(min-width:768px) and (max-width:991px){.visible-xs.visible-sm{display:block!important}tr.visible-xs.visible-sm{display:table-row!important}th.visible-xs.visible-sm,td.visible-xs.visible-sm{display:table-cell!important}}@media(min-width:992px) and (max-width:1199px){.visible-xs.visible-md{display:block!important}tr.visible-xs.visible-md{display:table-row!important}th.visible-xs.visible-md,td.visible-xs.visible-md{display:table-cell!important}}@media(min-width:1200px){.visible-xs.visible-lg{display:block!important}tr.visible-xs.visible-lg{display:table-row!important}th.visible-xs.visible-lg,td.visible-xs.visible-lg{display:table-cell!important}}.visible-sm{display:none!important}tr.visible-sm{display:none!important}th.visible-sm,td.visible-sm{display:none!important}@media(max-width:767px){.visible-sm.visible-xs{display:block!important}tr.visible-sm.visible-xs{display:table-row!important}th.visible-sm.visible-xs,td.visible-sm.visible-xs{display:table-cell!important}}@media(min-width:768px) and (max-width:991px){.visible-sm{display:block!important}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}}@media(min-width:992px) and (max-width:1199px){.visible-sm.visible-md{display:block!important}tr.visible-sm.visible-md{display:table-row!important}th.visible-sm.visible-md,td.visible-sm.visible-md{display:table-cell!important}}@media(min-width:1200px){.visible-sm.visible-lg{display:block!important}tr.visible-sm.visible-lg{display:table-row!important}th.visible-sm.visible-lg,td.visible-sm.visible-lg{display:table-cell!important}}.visible-md{display:none!important}tr.visible-md{display:none!important}th.visible-md,td.visible-md{display:none!important}@media(max-width:767px){.visible-md.visible-xs{display:block!important}tr.visible-md.visible-xs{display:table-row!important}th.visible-md.visible-xs,td.visible-md.visible-xs{display:table-cell!important}}@media(min-width:768px) and (max-width:991px){.visible-md.visible-sm{display:block!important}tr.visible-md.visible-sm{display:table-row!important}th.visible-md.visible-sm,td.visible-md.visible-sm{display:table-cell!important}}@media(min-width:992px) and (max-width:1199px){.visible-md{display:block!important}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}}@media(min-width:1200px){.visible-md.visible-lg{display:block!important}tr.visible-md.visible-lg{display:table-row!important}th.visible-md.visible-lg,td.visible-md.visible-lg{display:table-cell!important}}.visible-lg{display:none!important}tr.visible-lg{display:none!important}th.visible-lg,td.visible-lg{display:none!important}@media(max-width:767px){.visible-lg.visible-xs{display:block!important}tr.visible-lg.visible-xs{display:table-row!important}th.visible-lg.visible-xs,td.visible-lg.visible-xs{display:table-cell!important}}@media(min-width:768px) and (max-width:991px){.visible-lg.visible-sm{display:block!important}tr.visible-lg.visible-sm{display:table-row!important}th.visible-lg.visible-sm,td.visible-lg.visible-sm{display:table-cell!important}}@media(min-width:992px) and (max-width:1199px){.visible-lg.visible-md{display:block!important}tr.visible-lg.visible-md{display:table-row!important}th.visible-lg.visible-md,td.visible-lg.visible-md{display:table-cell!important}}@media(min-width:1200px){.visible-lg{display:block!important}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}}.hidden-xs{display:block!important}tr.hidden-xs{display:table-row!important}th.hidden-xs,td.hidden-xs{display:table-cell!important}@media(max-width:767px){.hidden-xs{display:none!important}tr.hidden-xs{display:none!important}th.hidden-xs,td.hidden-xs{display:none!important}}@media(min-width:768px) and (max-width:991px){.hidden-xs.hidden-sm{display:none!important}tr.hidden-xs.hidden-sm{display:none!important}th.hidden-xs.hidden-sm,td.hidden-xs.hidden-sm{display:none!important}}@media(min-width:992px) and (max-width:1199px){.hidden-xs.hidden-md{display:none!important}tr.hidden-xs.hidden-md{display:none!important}th.hidden-xs.hidden-md,td.hidden-xs.hidden-md{display:none!important}}@media(min-width:1200px){.hidden-xs.hidden-lg{display:none!important}tr.hidden-xs.hidden-lg{display:none!important}th.hidden-xs.hidden-lg,td.hidden-xs.hidden-lg{display:none!important}}.hidden-sm{display:block!important}tr.hidden-sm{display:table-row!important}th.hidden-sm,td.hidden-sm{display:table-cell!important}@media(max-width:767px){.hidden-sm.hidden-xs{display:none!important}tr.hidden-sm.hidden-xs{display:none!important}th.hidden-sm.hidden-xs,td.hidden-sm.hidden-xs{display:none!important}}@media(min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}tr.hidden-sm{display:none!important}th.hidden-sm,td.hidden-sm{display:none!important}}@media(min-width:992px) and (max-width:1199px){.hidden-sm.hidden-md{display:none!important}tr.hidden-sm.hidden-md{display:none!important}th.hidden-sm.hidden-md,td.hidden-sm.hidden-md{display:none!important}}@media(min-width:1200px){.hidden-sm.hidden-lg{display:none!important}tr.hidden-sm.hidden-lg{display:none!important}th.hidden-sm.hidden-lg,td.hidden-sm.hidden-lg{display:none!important}}.hidden-md{display:block!important}tr.hidden-md{display:table-row!important}th.hidden-md,td.hidden-md{display:table-cell!important}@media(max-width:767px){.hidden-md.hidden-xs{display:none!important}tr.hidden-md.hidden-xs{display:none!important}th.hidden-md.hidden-xs,td.hidden-md.hidden-xs{display:none!important}}@media(min-width:768px) and (max-width:991px){.hidden-md.hidden-sm{display:none!important}tr.hidden-md.hidden-sm{display:none!important}th.hidden-md.hidden-sm,td.hidden-md.hidden-sm{display:none!important}}@media(min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}tr.hidden-md{display:none!important}th.hidden-md,td.hidden-md{display:none!important}}@media(min-width:1200px){.hidden-md.hidden-lg{display:none!important}tr.hidden-md.hidden-lg{display:none!important}th.hidden-md.hidden-lg,td.hidden-md.hidden-lg{display:none!important}}.hidden-lg{display:block!important}tr.hidden-lg{display:table-row!important}th.hidden-lg,td.hidden-lg{display:table-cell!important}@media(max-width:767px){.hidden-lg.hidden-xs{display:none!important}tr.hidden-lg.hidden-xs{display:none!important}th.hidden-lg.hidden-xs,td.hidden-lg.hidden-xs{display:none!important}}@media(min-width:768px) and (max-width:991px){.hidden-lg.hidden-sm{display:none!important}tr.hidden-lg.hidden-sm{display:none!important}th.hidden-lg.hidden-sm,td.hidden-lg.hidden-sm{display:none!important}}@media(min-width:992px) and (max-width:1199px){.hidden-lg.hidden-md{display:none!important}tr.hidden-lg.hidden-md{display:none!important}th.hidden-lg.hidden-md,td.hidden-lg.hidden-md{display:none!important}}@media(min-width:1200px){.hidden-lg{display:none!important}tr.hidden-lg{display:none!important}th.hidden-lg,td.hidden-lg{display:none!important}}.visible-print{display:none!important}tr.visible-print{display:none!important}th.visible-print,td.visible-print{display:none!important}@media print{.visible-print{display:block!important}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}.hidden-print{display:none!important}tr.hidden-print{display:none!important}th.hidden-print,td.hidden-print{display:none!important}} \ No newline at end of file diff --git a/sockio-server/assets/css/custom.css b/sockio-server/assets/css/custom.css new file mode 100644 index 0000000..1dce55e --- /dev/null +++ b/sockio-server/assets/css/custom.css @@ -0,0 +1,57 @@ +html, body, .chat-body { + height: 100%; +} + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -100px; /* the bottom margin is the negative value of the footer's height */ +} +.footer, .push { + height: 100px; /* .push must be the same height as .footer */ + clear: both; +} + +.bordered{ + border: 1px solid #000; +} + +.chatroom{ + min-height: 100%; +} + +ul#chatbox{ + list-style: none; +} + +ul#chatbox li.message{ + margin-bottom: 10px; + display: block; + position: relative; + min-height: 30px; + padding: 5px 15px; + border-radius: 10px; + background-color: red; + color: white; +} + +ul#chatbox li.message .name{ + font-style: italic; + border-bottom: 1px dotted #000; +} + +ul#chatbox li.self{ + text-align: right; + background-color: blue; +} + +#chat_form{ + position: fixed; + bottom: 0px; + width:90%; +} + +#chat_input{ + width:90%; +} \ No newline at end of file diff --git a/sockio-server/assets/js/jquery-1.10.1.min.js b/sockio-server/assets/js/jquery-1.10.1.min.js new file mode 100644 index 0000000..e407e76 --- /dev/null +++ b/sockio-server/assets/js/jquery-1.10.1.min.js @@ -0,0 +1,6 @@ +/*! jQuery v1.10.1 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=jquery-1.10.1.min.map +*/ +(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.1",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=lt(),k=lt(),E=lt(),S=!1,A=function(){return 0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=bt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+xt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return At(e.replace(z,"$1"),t,n,i)}function st(e){return K.test(e+"")}function lt(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function ut(e){return e[b]=!0,e}function ct(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function pt(e,t,n){e=e.split("|");var r,i=e.length,a=n?null:t;while(i--)(r=o.attrHandle[e[i]])&&r!==t||(o.attrHandle[e[i]]=a)}function ft(e,t){var n=e.getAttributeNode(t);return n&&n.specified?n.value:e[t]===!0?t.toLowerCase():null}function dt(e,t){return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}function ht(e){return"input"===e.nodeName.toLowerCase()?e.defaultValue:t}function gt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function mt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function yt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function vt(e){return ut(function(t){return t=+t,ut(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.parentWindow;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.frameElement&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ct(function(e){return e.innerHTML="",pt("type|href|height|width",dt,"#"===e.firstChild.getAttribute("href")),pt(B,ft,null==e.getAttribute("disabled")),e.className="i",!e.getAttribute("className")}),r.input=ct(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")}),pt("value",ht,r.attributes&&r.input),r.getElementsByTagName=ct(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ct(function(e){return e.innerHTML="
",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ct(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=st(n.querySelectorAll))&&(ct(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ct(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=st(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ct(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=st(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},r.sortDetached=ct(function(e){return 1&e.compareDocumentPosition(n.createElement("div"))}),A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return gt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?gt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:ut,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=bt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?ut(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ut(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?ut(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ut(function(e){return function(t){return at(e,t).length>0}}),contains:ut(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:ut(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:vt(function(){return[0]}),last:vt(function(e,t){return[t-1]}),eq:vt(function(e,t,n){return[0>n?n+t:n]}),even:vt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:vt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:vt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:vt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=mt(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=yt(n);function bt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function xt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function wt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function Tt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function Ct(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function Nt(e,t,n,r,i,o){return r&&!r[b]&&(r=Nt(r)),i&&!i[b]&&(i=Nt(i,o)),ut(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||St(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:Ct(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=Ct(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=Ct(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function kt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=wt(function(e){return e===t},s,!0),p=wt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[wt(Tt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return Nt(l>1&&Tt(f),l>1&&xt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&kt(e.slice(l,r)),i>r&&kt(e=e.slice(r)),i>r&&xt(e))}f.push(n)}return Tt(f)}function Et(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=Ct(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?ut(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=bt(e)),n=t.length;while(n--)o=kt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Et(i,r))}return o};function St(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function At(e,t,n,i){var a,s,u,c,p,f=bt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&xt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}o.pseudos.nth=o.pseudos.eq;function jt(){}jt.prototype=o.filters=o.pseudos,o.setFilters=new jt,r.sortStable=b.split("").sort(A).join("")===b,p(),[0,0].sort(A),r.detectDuplicates=S,x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!l||i&&!u||(n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
t
",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null) +}),n=s=l=u=r=o=null,t}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=x(this),l=t,u=e.match(T)||[];while(o=u[a++])l=r?l:!s.hasClass(o),s[l?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
","
"]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); +u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("'; +html += '
'; +html += '
'; +html += '
Upload File
'; +html += '
Want to upload multiple files at once? Please upgrade to the latest Flash Player, then reload this page. For some reason our Flash based uploader did not load, so you are currently using our single file uploader.
'; +html += spacer(1,20) + '
'; +var url = zero_client.targetURL; +if (url.indexOf('?') > -1) url += '&'; else url += '?'; +url += 'format=jshtml&onafter=' + escape('window.parent.upload_basic_finish(response);'); +Debug.trace('upload', "Prepping basic upload: " + url); +html += '
'; +html += '
'; +html += '
'; +html += '

'; +html += ''; +html += ''; +html += ''; +html += '
' + large_icon_button('x', 'Cancel', "hide_popup_dialog()") + ' ' + large_icon_button('page_white_get.png', 'Upload', "upload_basic_go()") + '
'; +html += '
'; +html += ''; +html += '
'; +html += ''; +session.hooks.keys[ESC_KEY] = 'hide_popup_dialog'; +show_popup_dialog(528, 200, html); +} +function upload_basic_go() { +$('f_upload_basic').submit(); +$('d_upload_form').hide(); +$('d_upload_progress').show(); +} +function upload_basic_finish(response) { +Debug.trace('upload', "Basic upload complete: " + dumper(response)); +setTimeout( 'upload_basic_finish_2()', 100 ); +} +function upload_basic_finish_2() { +$('i_upload_basic').src = 'blank.html'; +setTimeout( 'upload_basic_finish_3()', 100 ); +} +function upload_basic_finish_3() { +hide_popup_dialog(); +delete session.progress; +show_progress_dialog( 0, 'Finishing Upload...', true ); +fire_callback( session.upload_callback ); +} +function upload_destroy() { +if (zero_client) { +zero_client.destroy(); +delete ZeroUpload.clients[ zero_client.id ]; +zero_client = null; +} +} +function prep_upload(dom_id, url, callback, types) { +session.upload_callback = callback; +if (url) { +if (url.indexOf('?') > -1) url += '&'; else url += '?'; +url += 'session=' + session.cookie.get('effect_session_id'); +} +upload_destroy(); +zero_client = new ZeroUpload.Client(); +if (url) zero_client.setURL( url ); +zero_client.setHandCursor( true ); +if (types) zero_client.setFileTypes( types[0], types[1] ); +zero_client.addEventListener( 'queueStart', uploadQueueStart ); +zero_client.addEventListener( 'fileStart', uploadFileStart ); +zero_client.addEventListener( 'progress', uploadProgress ); +zero_client.addEventListener( 'fileComplete', uploadFileComplete ); +zero_client.addEventListener( 'queueComplete', uploadQueueComplete ); +zero_client.addEventListener( 'error', uploadError ); +zero_client.addEventListener( 'debug', function(client, eventName, args) { +Debug.trace('upload', "Caught event: " + eventName); +} ); +if (dom_id) { +Debug.trace('upload', "Gluing ZeroUpload to: " + dom_id); +zero_client.glue( dom_id ); +} +} +Class.create( 'Debug', { +__static: { +enabled: false, +categories: { all: 1 }, +buffer: [], +max_rows: 5000, +win: null, +ie: !!navigator.userAgent.match(/MSIE/), +ie6: !!navigator.userAgent.match(/MSIE\D+6/), +init: function() { +Debug.enabled = true; +Debug.trace( 'debug', 'Debug log start' ); +var html = '

'; +if (Debug.ie) { +setTimeout( function() { +document.body.insertAdjacentHTML('beforeEnd', +'
' + html + '
' +); +}, 1000 ); +} +else { +var div = document.createElement('DIV'); +div.id = 'd_debug'; +div.setAttribute('id', 'd_debug'); +div.style.position = Debug.ie6 ? 'absolute' : 'fixed'; +div.style.zIndex = '101'; +div.style.left = '0px'; +div.style.top = '0px'; +div.style.width = '100%'; +div.innerHTML = html; +document.getElementsByTagName('body')[0].appendChild(div); +} +}, +show: function() { +if (!Debug.win || Debug.win.closed) { +Debug.trace('debug', "Opening debug window"); +Debug.win = window.open( '', 'DebugWindow', 'width=600,height=500,menubar=no,resizable=yes,scrollbars=yes,location=no,status=no,toolbar=no,directories=no' ); +if (!Debug.win) return alert("Failed to open window. Popup blocker maybe?"); +var doc = Debug.win.document; +doc.open(); +doc.writeln( 'Debug Log' ); +doc.writeln( '
' ); +doc.writeln( '
' ); +doc.writeln( '
' ); +doc.writeln( '' ); +doc.writeln( '' ); +doc.writeln( '
' ); +doc.writeln( '' ); +doc.close(); +} +Debug.win.focus(); +}, +console_execute: function() { +var cmd = Debug.win.document.getElementById('fe_command'); +if (cmd.value.length) { +Debug.trace( 'console', cmd.value ); +try { +Debug.trace( 'console', '' + eval(cmd.value) ); +} +catch (e) { +Debug.trace( 'error', 'JavaScript Interpreter Exception: ' + e.toString() ); +} +} +}, +get_time_stamp: function(now) { +var date = new Date( now * 1000 ); +var hh = date.getHours(); if (hh < 10) hh = "0" + hh; +var mi = date.getMinutes(); if (mi < 10) mi = "0" + mi; +var ss = date.getSeconds(); if (ss < 10) ss = "0" + ss; +var sss = '' + date.getMilliseconds(); while (sss.length < 3) sss = "0" + sss; +return '' + hh + ':' + mi + ':' + ss + '.' + sss; +}, +refresh_console: function() { +if (!Debug.win || Debug.win.closed) return; +var div = Debug.win.document.getElementById('d_debug_log'); +if (div) { +var row = null; +while ( row = Debug.buffer.shift() ) { +var time_stamp = Debug.get_time_stamp(row.time); +var msg = row.msg; +msg = msg.replace(/\t/g, "    "); +msg = msg.replace(//g, ">"); +msg = msg.replace(/\n/g, "
\n"); +var html = ''; +var sty = 'float:left; font-family: Consolas, Courier, mono; font-size: 12px; cursor:default; margin-right:10px; margin-bottom:1px; padding:2px;'; +html += '
' + time_stamp + '
'; +html += '
' + row.cat + '
'; +html += '
' + msg + '
'; +html += '
'; +var chunk = Debug.win.document.createElement('DIV'); +chunk.style['float'] = 'none'; +chunk.innerHTML = html; +div.appendChild(chunk); +} +var cmd = Debug.win.document.getElementById('fe_command'); +cmd.focus(); +} +Debug.dirty = 0; +Debug.win.scrollTo(0, 99999); +}, +hires_time_now: function() { +var now = new Date(); +return ( now.getTime() / 1000 ); +}, +trace: function(cat, msg) { +if (arguments.length == 1) { +msg = cat; +cat = 'debug'; +} +if (Debug.categories.all || Debug.categories[cat]) { +Debug.buffer.push({ cat: cat, msg: msg, time: Debug.hires_time_now() }); +if (Debug.buffer.length > Debug.max_rows) Debug.buffer.shift(); +if (!Debug.dirty) { +Debug.dirty = 1; +setTimeout( 'Debug.refresh_console();', 1 ); +} +} +} +} +} ); +var session = { +inited: false, +api_mod_cache: {}, +query: parseQueryString( ''+location.search ), +cookie: new CookieTree({ path: '/effect/' }), +storage: {}, +storage_dirty: false, +hooks: { +keys: {} +}, +username: '', +em_width: 11, +audioResourceMatch: /\.mp3$/i, +imageResourceMatch: /\.(jpe|jpeg|jpg|png|gif)$/i, +textResourceMatch: /\.xml$/i, +movieResourceMatch: /\.(flv|mp4|mp4v|mov|3gp|3g2)$/i, +imageResourceMatchString: '\.(jpe|jpeg|jpg|png|gif)$' +}; +session.debug = session.query.debug ? true : false; +var page_manager = null; +var preload_icons = []; +var preload_images = [ +'loading.gif', +'aquaprogressbar.gif', +'aquaprogressbar_bkgnd.gif' +]; +function get_base_url() { +return protocol + '://' + location.hostname + session.config.BaseURI; +} +function effect_init() { +if (session.inited) return; +session.inited = true; +assert( window.config, "Config not loaded" ); +session.config = window.config; +Debug.trace("Starting up"); +rendering_page = false; +preload(); +window.$R = {}; +for (var key in config.RegExpShortcuts) { +$R[key] = new RegExp( config.RegExpShortcuts[key] ); +} +ww_precalc_font("body", "effect_precalc_font_finish"); +page_manager = new Effect.PageManager( config.Pages.Page ); +var session_id = session.cookie.get('effect_session_id'); +if (session_id && session_id.match(/^login/)) { +do_session_recover(); +} +else { +show_default_login_status(); +Nav.init(); +} +Blog.search({ +stag: 'sidebar_docs', +limit: 20, +title_only: true, +sort_by: 'seq', +sort_dir: -1, +target: 'd_sidebar_documents', +outer_div_class: 'sidebar_blog_row', +title_class: 'sidebar_blog_title', +after: '' +}); +Blog.search({ +stag: 'sidebar_tutorials', +limit: 5, +title_only: true, +sort_by: 'seq', +sort_dir: -1, +target: 'd_sidebar_tutorials', +outer_div_class: 'sidebar_blog_row', +title_class: 'sidebar_blog_title', +after: '' +}); +Blog.search({ +stag: 'sidebar_plugins', +limit: 5, +title_only: true, +sort_by: 'seq', +sort_dir: -1, +target: 'd_sidebar_plugins', +outer_div_class: 'sidebar_blog_row', +title_class: 'sidebar_blog_title', +after: '' +}); +$('fe_search_bar').onkeydown = delay_onChange_input_text; +user_storage_idle(); +} +function effect_precalc_font_finish(width, height) { +session.em_width = width; +} +function preload() { +for (var idx = 0, len = preload_icons.length; idx < len; idx++) { +var url = images_uri + '/icons/' + preload_icons[idx] + '.gif'; +preload_icons[idx] = new Image(); +preload_icons[idx].src = url; +} +for (var idx = 0, len = preload_images.length; idx < len; idx++) { +var url = images_uri + '/' + preload_images[idx]; +preload_images[idx] = new Image(); +preload_images[idx].src = url; +} +} +function $P(id) { +if (!id) id = page_manager.current_page_id; +var page = page_manager.find(id); +assert( !!page, "Failed to locate page: " + id ); +return page; +} +function get_pref(name) { +if (!session.user || !session.user.Preferences) return alert("ASSERT FAILURE! Tried to lookup pref " + name + " and user is not yet loaded!"); +return session.user.Preferences[name]; +} +function get_bool_pref(name) { +return (get_pref(name) == 1); +} +function set_pref(name, value) { +session.user.Preferences[name] = value; +} +function set_bool_pref(name, value) { +set_pref(name, value ? '1' : '0'); +} +function save_prefs() { +var prefs_to_save = {}; +if (arguments.length) { +for (var idx = 0, len = arguments.length; idx < len; idx++) { +var key = arguments[idx]; +prefs_to_save[key] = get_pref(key); +} +} +else prefs_to_save = session.user.Preferences; +effect_api_mod_touch('user_get'); +effect_api_send('user_update', { +Username: session.username, +Preferences: prefs_to_save +}, 'save_prefs_2'); +} +function save_prefs_2(response) { +do_message('success', 'Preferences saved.'); +} + +function get_full_name(username) { +var user = session.users[username]; +if (!user) return username; +return user.FullName; +} +function get_buddy_icon_url(username, size) { +var mod = session.api_mod_cache.get_buddy_icon || 0; +if (!size) size = 32; +var url = '/effect/api/get_buddy_icon?username='+username + '&mod=' + mod + '&size=' + size; +return url; +} +function get_buddy_icon_display(username, show_icon, show_name) { +if ((typeof(show_icon) == 'undefined') && get_bool_pref('show_user_icons')) show_icon = 1; +if ((typeof(show_name) == 'undefined') && get_bool_pref('show_user_names')) show_name = 1; +var html = ''; +if (show_icon) html += ''; +if (show_icon && show_name) html += '
'; +if (show_name) html += username; +return html; +} +function do_session_recover() { +session.hooks.after_error = 'do_logout'; +effect_api_send('session_recover', {}, 'do_login_2', { _from_recover: 1 } ); +} +function require_login() { +if (session.user) return true; +Debug.trace('Page requires login, showing login page'); +session.nav_after_login = Nav.currentAnchor(); +setTimeout( function() { +Nav.go( 'Login' ); +}, 1 ); +return false; +} +function popup_window(url, name) { +if (!url) url = ''; +if (!name) name = ''; +var win = window.open(url, name); +if (!win) return alert('Failed to open popup window. If you have a popup blocker, please disable it for this website and try again.'); +return win; +} +function do_login_prompt() { +hide_popup_dialog(); +delete session.progress; +if (!session.temp_password) session.temp_password = ''; +if (!session.username) session.username = ''; +var temp_username = session.open_id || session.username || ''; +var html = ''; +html += '
'; +html += '
'; +html += '
Effect Developer Login
'; +html += '
'; +html += '
Effect Username  or  '+icon('openid', 'OpenID', 'popup_window(\'http://openid.net/\')', 'What is OpenID?')+'


'; +html += '
'; +html += '
'; +html += '

'; +html += ''; +html += ''; +html += ''; +html += '
' + large_icon_button('x', 'Cancel', "clear_login()") + ' ' + large_icon_button('check', 'Login', 'do_login()') + '
'; +html += '
'; +html += ''; +session.hooks.keys[ENTER_KEY] = 'do_login'; +session.hooks.keys[ESC_KEY] = 'clear_login'; +safe_focus( 'fe_username' ); +show_popup_dialog(450, 225, html); +} +function do_openid_reg(title, auto_login_button) { +hide_popup_dialog(); +delete session.progress; +if (!title) title = 'Register Account Using OpenID'; +if (typeof(auto_login_button) == 'undefined') auto_login_button = 1; +var html = ''; +html += '
'; +html += '
'; +html += '
'+title+'
'; +html += '
'; +html += '
'+icon('openid', 'Enter Your OpenID URL:')+'
'; +if (auto_login_button) html += '


'; +html += '
'; +html += '

'; +html += ''; +html += ''; +html += ''; +html += '
' + large_icon_button('x', 'Cancel', "hide_popup_dialog()") + ' ' + large_icon_button('check', title.match(/login/i) ? 'Login' : 'Register', 'do_openid_login()') + '
'; +html += '
'; +html += ''; +session.hooks.keys[ENTER_KEY] = 'do_openid_login'; +session.hooks.keys[ESC_KEY] = 'hide_popup_dialog'; +safe_focus( 'fe_username' ); +show_popup_dialog(450, 225, html); +} +function do_login_prompt_2() { +hide_popup_dialog(); +delete session.progress; +if (!session.temp_password) session.temp_password = ''; +if (!session.username) session.username = ''; +var html = ''; +html += '
'; +html += '"; + second_cell = ""; + row = $("").attr("id", "s" + index).attr("class", "location_row").html(first_cell + second_cell); + $locationsDiv.append(row); + } + if (index === this.numSearchToDisplay) { + $locationsDiv.append(""); + return $locationsDiv.append(""); + } + }, this); + return this.geocoder.geocode({ + address: address + }, __bind(function(result, status) { + if (status !== "OK") { + $('.error_message').html(t("Search Address Failed")).fadeIn(); + return; + } + _.each(result, showResults); + $("#search_results").html($locationsDiv); + this.locationChange("search"); + this.searchResults = result; + return this.displaySearchLoc(); + }, this)); + }; + ClientsRequestView.prototype.mouseoverLocation = function(e) { + var $el, id, marker; + $el = $(e.currentTarget); + id = $el.attr("id").substring(1); + marker = this.markers[id]; + return marker.setAnimation(google.maps.Animation.BOUNCE); + }; + ClientsRequestView.prototype.mouseoutLocation = function(e) { + var $el, id, marker; + $el = $(e.currentTarget); + id = $el.attr("id").substring(1); + marker = this.markers[id]; + return marker.setAnimation(null); + }; + ClientsRequestView.prototype.searchLocation = function(e) { + e.preventDefault(); + $("#address").val($(e.currentTarget).html()); + return this.searchAddress(); + }; + ClientsRequestView.prototype.favoriteClick = function(e) { + var index, location; + e.preventDefault(); + $(".favorites").attr("href", ""); + index = $(e.currentTarget).removeAttr("href").attr("id"); + location = new google.maps.LatLng(USER.locations[index].latitude, USER.locations[index].longitude); + return this.panToLocation(location); + }; + ClientsRequestView.prototype.clickLocation = function(e) { + var id; + id = $(e.currentTarget).attr("id").substring(1); + return this.panToLocation(this.markers[id].getPosition()); + }; + ClientsRequestView.prototype.panToLocation = function(location) { + this.map.panTo(location); + this.map.setZoom(16); + return this.pickup_icon.setPosition(location); + }; + ClientsRequestView.prototype.locationLinkHandle = function(e) { + var panelName; + e.preventDefault(); + panelName = $(e.currentTarget).attr("id"); + return this.locationChange(panelName); + }; + ClientsRequestView.prototype.locationChange = function(type) { + $(".locations_link").attr("href", "").css("font-weight", "normal"); + switch (type) { + case "favorite": + $(".search_results").attr("href", ""); + $(".locations_link#favorite").removeAttr("href").css("font-weight", "bold"); + $("#search_results").hide(); + $("#favorite_results").fadeIn(); + return this.displayFavLoc(); + case "search": + $(".favorites").attr("href", ""); + $(".locations_link#search").removeAttr("href").css("font-weight", "bold"); + $("#favorite_results").hide(); + $("#search_results").fadeIn(); + return this.displaySearchLoc(); + } + }; + ClientsRequestView.prototype.rateTrip = function(e) { + var rating; + rating = $(e.currentTarget).attr("id"); + $(".stars").attr("src", "/web/img/star_inactive.png"); + return _(rating).times(function(index) { + return $(".stars#" + (index + 1)).attr("src", "/web/img/star_active.png"); + }); + }; + ClientsRequestView.prototype.pickupHandle = function(e) { + var $el, callback, message; + e.preventDefault(); + $el = $(e.currentTarget).find("span"); + switch ($el.html()) { + case t("Request Pickup"): + _.delay(this.requestRide, 3000); + $("#status_message").html(t("Sending pickup request...")); + $el.html(t("Cancel Pickup")).parent().attr("class", "button_red"); + this.pickup_icon.setDraggable(false); + this.map.panTo(this.pickup_icon.getPosition()); + return this.map.setZoom(18); + case t("Cancel Pickup"): + if (this.status === "ready") { + $el.html(t("Request Pickup")).parent().attr("class", "button_green"); + return this.pickup_icon.setDraggable(true); + } else { + callback = __bind(function(v, m, f) { + if (v) { + this.AskDispatch("PickupCanceledClient"); + return this.setStatus("ready"); + } + }, this); + message = t("Cancel Request Prompt"); + if (this.status === "arriving") { + message = 'Cancel Request Arrived Prompt'; + } + return $.prompt(message, { + buttons: { + Ok: true, + Cancel: false + }, + callback: callback + }); + } + } + }; + ClientsRequestView.prototype.requestRide = function() { + if ($("#pickupHandle").find("span").html() === t("Cancel Pickup")) { + this.AskDispatch("Pickup"); + return this.setStatus("searching"); + } + }; + ClientsRequestView.prototype.removeCabs = function() { + _.each(this.cabs, __bind(function(point) { + return point.setMap(null); + }, this)); + return this.cabs = []; + }; + ClientsRequestView.prototype.addToFavLoc = function(e) { + var $el, lat, lng, nickname; + e.preventDefault(); + $el = $(e.currentTarget); + $el.find(".error_message").html(""); + nickname = $el.find("#favLocNickname").val().toString(); + lat = $el.find("#pickupLat").val().toString(); + lng = $el.find("#pickupLng").val().toString(); + if (nickname.length < 3) { + $el.find(".error_message").html(t("Favorite Location Nickname Length Error")); + return; + } + this.ShowSpinner("submit"); + return $.ajax({ + type: 'POST', + url: API + "/locations", + dataType: 'json', + data: { + token: USER.token, + nickname: nickname, + latitude: lat, + longitude: lng + }, + success: __bind(function(data, textStatus, jqXHR) { + return $el.html(t("Favorite Location Save Succeeded")); + }, this), + error: __bind(function(jqXHR, textStatus, errorThrown) { + return $el.find(".error_message").html(t("Favorite Location Save Failed")); + }, this), + complete: __bind(function(data) { + return this.HideSpinner(); + }, this) + }); + }; + ClientsRequestView.prototype.showFavLoc = function(e) { + $(e.currentTarget).fadeOut(); + return $("#favLoc_form").fadeIn(); + }; + ClientsRequestView.prototype.selectInputText = function(e) { + e.currentTarget.focus(); + return e.currentTarget.select(); + }; + ClientsRequestView.prototype.displayFavLoc = function() { + var alphabet, bounds; + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + this.removeMarkers(); + bounds = new google.maps.LatLngBounds(); + _.each(USER.locations, __bind(function(location, index) { + var marker; + marker = new google.maps.Marker({ + position: new google.maps.LatLng(location.latitude, location.longitude), + map: this.map, + title: t("Favorite Location Title", { + id: alphabet != null ? alphabet[index] : void 0 + }), + icon: "https://www.google.com/mapfiles/marker" + alphabet[index] + ".png" + }); + this.markers.push(marker); + bounds.extend(marker.getPosition()); + return google.maps.event.addListener(marker, 'click', __bind(function() { + return this.pickup_icon.setPosition(marker.getPosition()); + }, this)); + }, this)); + this.pickup_icon.setPosition(_.first(this.markers).getPosition()); + return this.map.fitBounds(bounds); + }; + ClientsRequestView.prototype.displaySearchLoc = function() { + var alphabet; + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + this.removeMarkers(); + return _.each(this.searchResults, __bind(function(result, index) { + var marker; + if (index < this.numSearchToDisplay) { + marker = new google.maps.Marker({ + position: result.geometry.location, + map: this.map, + title: t("Search Location Title", { + id: alphabet != null ? alphabet[index] : void 0 + }), + icon: "https://www.google.com/mapfiles/marker" + alphabet[index] + ".png" + }); + this.markers.push(marker); + return this.panToLocation(result.geometry.location); + } + }, this)); + }; + ClientsRequestView.prototype.removeMarkers = function() { + _.each(this.markers, __bind(function(marker) { + return marker.setMap(null); + }, this)); + return this.markers = []; + }; + ClientsRequestView.prototype.AskDispatch = function(ask, options) { + var attrs, lowestETA, processData, showCab; + if (ask == null) { + ask = ""; + } + if (options == null) { + options = {}; + } + switch (ask) { + case "NearestCab": + attrs = { + latitude: this.pickup_icon.getPosition().lat(), + longitude: this.pickup_icon.getPosition().lng() + }; + lowestETA = 99999; + showCab = __bind(function(cab) { + var point; + point = new google.maps.Marker({ + position: new google.maps.LatLng(cab.latitude, cab.longitude), + map: this.map, + icon: this.cabMarker, + title: t("ETA Message", { + minutes: app.helpers.FormatSeconds(cab != null ? cab.eta : void 0, true) + }) + }); + if (cab.eta < lowestETA) { + lowestETA = cab.eta; + } + return this.cabs.push(point); + }, this); + processData = __bind(function(data, textStatus, jqXHR) { + if (this.status === "ready") { + this.removeCabs(); + if (data.sorry) { + $("#status_message").html(data.sorry).fadeIn(); + } else { + _.each(data.driverLocations, showCab); + $("#status_message").html(t("Nearest Cab Message", { + minutes: app.helpers.FormatSeconds(lowestETA, true) + })).fadeIn(); + } + if (Backbone.history.fragment === "!/request") { + return _.delay(this.showCabs, this.pollInterval); + } + } + }, this); + return this.AjaxCall(ask, processData, attrs); + case "StatusClient": + processData = __bind(function(data, textStatus, jqXHR) { + var bounds, cabLocation, locationSaved, point, userLocation; + if (data.messageType === "OK") { + switch (data.status) { + case "completed": + this.removeCabs(); + this.setStatus("rate"); + return this.fetchTripDetails(data.tripID); + case "open": + return this.setStatus("ready"); + case "begintrip": + this.setStatus("riding"); + cabLocation = new google.maps.LatLng(data.latitude, data.longitude); + this.removeCabs(); + this.pickup_icon.setMap(null); + point = new google.maps.Marker({ + position: cabLocation, + map: this.map, + icon: this.cabMarker + }); + this.cabs.push(point); + this.map.panTo(point.getPosition()); + $("#rideName").html(data.driverName); + $("#ridePhone").html(data.driverMobile); + $("#ride_address_wrapper").hide(); + if (Backbone.history.fragment === "!/request") { + return _.delay(this.AskDispatch, this.pollInterval, "StatusClient"); + } + break; + case "pending": + this.setStatus("searching"); + if (Backbone.history.fragment === "!/request") { + return _.delay(this.AskDispatch, this.pollInterval, "StatusClient"); + } + break; + case "accepted": + case "arrived": + if (data.status === "accepted") { + this.setStatus("waiting"); + $("#status_message").html(t("Arrival ETA Message", { + minutes: app.helpers.FormatSeconds(data.eta, true) + })); + } else { + this.setStatus("arriving"); + $("#status_message").html(t("Arriving Now Message")); + } + userLocation = new google.maps.LatLng(data.pickupLocation.latitude, data.pickupLocation.longitude); + cabLocation = new google.maps.LatLng(data.latitude, data.longitude); + this.pickup_icon.setPosition(userLocation); + this.removeCabs(); + $("#rideName").html(data.driverName); + $("#ridePhone").html(data.driverMobile); + if ($("#rideAddress").html() === "") { + locationSaved = false; + _.each(USER.locations, __bind(function(location) { + if (parseFloat(location.latitude) === parseFloat(data.pickupLocation.latitude) && parseFloat(location.longitude) === parseFloat(data.pickupLocation.longitude)) { + return locationSaved = true; + } + }, this)); + if (locationSaved) { + $("#addToFavButton").hide(); + } + $("#pickupLat").val(data.pickupLocation.latitude); + $("#pickupLng").val(data.pickupLocation.longitude); + this.geocoder.geocode({ + location: userLocation + }, __bind(function(result, status) { + $("#rideAddress").html(result[0].formatted_address); + return $("#favLocNickname").val("" + result[0].address_components[0].short_name + " " + result[0].address_components[1].short_name); + }, this)); + } + point = new google.maps.Marker({ + position: cabLocation, + map: this.map, + icon: this.cabMarker + }); + this.cabs.push(point); + bounds = bounds = new google.maps.LatLngBounds(); + bounds.extend(cabLocation); + bounds.extend(userLocation); + this.map.fitBounds(bounds); + if (Backbone.history.fragment === "!/request") { + return _.delay(this.AskDispatch, this.pollInterval, "StatusClient"); + } + } + } + }, this); + return this.AjaxCall(ask, processData); + case "Pickup": + attrs = { + latitude: this.pickup_icon.getPosition().lat(), + longitude: this.pickup_icon.getPosition().lng() + }; + processData = __bind(function(data, textStatus, jqXHR) { + if (data.messageType === "Error") { + return $("#status_message").html(data.description); + } else { + return this.AskDispatch("StatusClient"); + } + }, this); + return this.AjaxCall(ask, processData, attrs); + case "PickupCanceledClient": + processData = __bind(function(data, textStatus, jqXHR) { + if (data.messageType === "OK") { + return this.setStatus("ready"); + } else { + return $("#status_message").html(data.description); + } + }, this); + return this.AjaxCall(ask, processData, attrs); + case "RatingDriver": + attrs = { + rating: options.rating + }; + processData = __bind(function(data, textStatus, jqXHR) { + if (data.messageType === "OK") { + this.setStatus("init"); + } else { + $("status_message").html(t("Rating Driver Failed")); + } + return this.HideSpinner(); + }, this); + return this.AjaxCall(ask, processData, attrs); + case "Feedback": + attrs = { + message: options.message + }; + processData = __bind(function(data, textStatus, jqXHR) { + if (data.messageType === "OK") { + return alert("rated"); + } + }, this); + return this.AjaxCall(ask, processData, attrs); + } + }; + ClientsRequestView.prototype.AjaxCall = function(type, successCallback, attrs) { + if (attrs == null) { + attrs = {}; + } + _.extend(attrs, { + token: USER.token, + messageType: type, + app: "client", + version: "1.0.60", + device: "web" + }); + return $.ajax({ + type: 'POST', + url: DISPATCH + "/", + processData: false, + data: JSON.stringify(attrs), + success: successCallback, + dataType: 'json', + error: __bind(function(jqXHR, textStatus, errorThrown) { + $("#status_message").html(errorThrown); + return this.HideSpinner(); + }, this) + }); + }; + return ClientsRequestView; + })(); +}).call(this); +}, "views/clients/settings": function(exports, require, module) {(function() { + var clientsSettingsTemplate; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsSettingsTemplate = require('templates/clients/settings'); + exports.ClientsSettingsView = (function() { + __extends(ClientsSettingsView, UberView); + function ClientsSettingsView() { + this.render = __bind(this.render, this); + this.initialize = __bind(this.initialize, this); + ClientsSettingsView.__super__.constructor.apply(this, arguments); + } + ClientsSettingsView.prototype.id = 'settings_view'; + ClientsSettingsView.prototype.className = 'view_container'; + ClientsSettingsView.prototype.events = { + 'submit #profile_pic_form': 'processPicUpload', + 'click #submit_pic': 'processPicUpload', + 'click a.setting_change': "changeTab", + 'submit #edit_info_form': "submitInfo", + 'click #change_password': 'changePass' + }; + ClientsSettingsView.prototype.divs = { + 'info_div': "Information", + 'pic_div': "Picture" + }; + ClientsSettingsView.prototype.pageTitle = t("Settings") + " | " + t("Uber"); + ClientsSettingsView.prototype.tabTitle = { + 'info_div': t("Information"), + 'pic_div': t("Picture") + }; + ClientsSettingsView.prototype.initialize = function() { + return this.mixin(require('web-lib/mixins/i18n_phone_form').i18nPhoneForm); + }; + ClientsSettingsView.prototype.render = function(type) { + if (type == null) { + type = "info"; + } + this.RefreshUserInfo(__bind(function() { + var $el, alphabet; + this.delegateEvents(); + this.HideSpinner(); + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + $el = $(this.el); + $(this.el).html(clientsSettingsTemplate({ + type: type + })); + $el.find("#" + type + "_div").show(); + $el.find("a[href='" + type + "_div']").parent().addClass("active"); + return document.title = "" + this.tabTitle[type + '_div'] + " " + this.pageTitle; + }, this)); + this.delegateEvents(); + return this; + }; + ClientsSettingsView.prototype.changeTab = function(e) { + var $eTarget, $el, div, link, pageDiv, _i, _j, _len, _len2, _ref, _ref2; + e.preventDefault(); + $eTarget = $(e.currentTarget); + this.ClearGlobalStatus(); + $el = $(this.el); + _ref = $el.find(".setting_change"); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + link = _ref[_i]; + $(link).parent().removeClass("active"); + } + $eTarget.parent().addClass("active"); + _ref2 = _.keys(this.divs); + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + div = _ref2[_j]; + $el.find("#" + div).hide(); + } + pageDiv = $eTarget.attr('href'); + $el.find("#" + pageDiv).show(); + Backbone.history.navigate("!/settings/" + (this.divs[pageDiv].toLowerCase().replace(" ", "-")), false); + document.title = "" + this.tabTitle[pageDiv] + " " + this.pageTitle; + if (pageDiv === "loc_div") { + try { + google.maps.event.trigger(this.map, 'resize'); + return this.map.fitBounds(this.bounds); + } catch (_e) {} + } + }; + ClientsSettingsView.prototype.submitInfo = function(e) { + var $e, attrs, client, options; + $('#global_status').find('.success_message').text(''); + $('#global_status').find('.error_message').text(''); + $('.error_message').text(''); + e.preventDefault(); + $e = $(e.currentTarget); + attrs = $e.serializeToJson(); + attrs['mobile_country_id'] = this.$('#mobile_country_id').val(); + if (attrs['password'] === '') { + delete attrs['password']; + } + options = { + success: __bind(function(response) { + this.ShowSuccess(t("Information Update Succeeded")); + return this.RefreshUserInfo(); + }, this), + error: __bind(function(model, data) { + var errors; + if (data.status === 406) { + errors = JSON.parse(data.responseText); + return _.each(_.keys(errors), function(field) { + return $("#" + field).parent().find('span.error_message').text(errors[field]); + }); + } else { + return this.ShowError(t("Information Update Failed")); + } + }, this), + type: "PUT" + }; + client = new app.models.client({ + id: USER.id + }); + return client.save(attrs, options); + }; + ClientsSettingsView.prototype.changePass = function(e) { + e.preventDefault(); + $(e.currentTarget).hide(); + return $("#password").show(); + }; + ClientsSettingsView.prototype.processPicUpload = function(e) { + e.preventDefault(); + this.ShowSpinner("submit"); + return $.ajaxFileUpload({ + url: API + '/user_pictures', + secureuri: false, + fileElementId: 'picture', + data: { + token: USER.token + }, + dataType: 'json', + complete: __bind(function(data, status) { + this.HideSpinner(); + if (status === 'success') { + this.ShowSuccess(t("Picture Update Succeeded")); + return this.RefreshUserInfo(__bind(function() { + return $("#settingsProfPic").attr("src", USER.picture_url + ("?" + (Math.floor(Math.random() * 1000)))); + }, this)); + } else { + if (data.error) { + return this.ShowError(data.error); + } else { + return this.ShowError("Picture Update Failed"); + } + } + }, this) + }); + }; + return ClientsSettingsView; + })(); +}).call(this); +}, "views/clients/sign_up": function(exports, require, module) {(function() { + var clientsSignUpTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + clientsSignUpTemplate = require('templates/clients/sign_up'); + exports.ClientsSignUpView = (function() { + __extends(ClientsSignUpView, UberView); + function ClientsSignUpView() { + ClientsSignUpView.__super__.constructor.apply(this, arguments); + } + ClientsSignUpView.prototype.id = 'signup_view'; + ClientsSignUpView.prototype.className = 'view_container'; + ClientsSignUpView.prototype.initialize = function() { + this.mixin(require('web-lib/mixins/i18n_phone_form').i18nPhoneForm); + return $('#location_country').live('change', function() { + if (!$('#mobile').val()) { + return $('#mobile_country').find("option[value=" + ($(this).val()) + "]").attr('selected', 'selected').end().trigger('change'); + } + }); + }; + ClientsSignUpView.prototype.events = { + 'submit form': 'signup', + 'click button': 'signup', + 'change #card_number': 'showCardType', + 'change #location_country': 'countryChange' + }; + ClientsSignUpView.prototype.render = function(invite) { + this.HideSpinner(); + $(this.el).html(clientsSignUpTemplate({ + invite: invite + })); + return this; + }; + ClientsSignUpView.prototype.signup = function(e) { + var $el, attrs, client, error_messages, options; + e.preventDefault(); + $el = $("form"); + $el.find('#terms_error').hide(); + if (!$el.find('#signup_terms input[type=checkbox]').attr('checked')) { + $('#spinner.submit').hide(); + $el.find('#terms_error').show(); + return; + } + error_messages = $el.find('.error_message').html(""); + attrs = { + first_name: $el.find('#first_name').val(), + last_name: $el.find('#last_name').val(), + email: $el.find('#email').val(), + password: $el.find('#password').val(), + location_country: $el.find('#location_country option:selected').attr('data-iso2'), + location: $el.find('#location').val(), + language: $el.find('#language').val(), + mobile_country: $el.find('#mobile_country option:selected').attr('data-iso2'), + mobile: $el.find('#mobile').val(), + card_number: $el.find('#card_number').val(), + card_expiration_month: $el.find('#card_expiration_month').val(), + card_expiration_year: $el.find('#card_expiration_year').val(), + card_code: $el.find('#card_code').val(), + use_case: $el.find('#use_case').val(), + promotion_code: $el.find('#promotion_code').val() + }; + options = { + statusCode: { + 200: function(response) { + $.cookie('token', response.token); + amplify.store('USERjson', response); + app.refreshMenu(); + return app.routers.clients.navigate('!/dashboard', true); + }, + 406: function(e) { + var error, errors, _i, _len, _ref, _results; + errors = JSON.parse(e.responseText); + _ref = _.keys(errors); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + error = _ref[_i]; + _results.push($('#' + error).parent().find('span').html($('#' + error).parent().find('span').html() + " " + errors[error])); + } + return _results; + } + }, + complete: __bind(function(response) { + return this.HideSpinner(); + }, this) + }; + client = new app.models.client; + $('.spinner#submit').show(); + return client.save(attrs, options); + }; + ClientsSignUpView.prototype.countryChange = function(e) { + var $e; + $e = $(e.currentTarget); + return $("#mobile_country").val($e.val()).trigger('change'); + }; + ClientsSignUpView.prototype.showCardType = function(e) { + var $el, reAmerica, reDiscover, reMaster, reVisa, validCard; + reVisa = /^4\d{3}-?\d{4}-?\d{4}-?\d{4}$/; + reMaster = /^5[1-5]\d{2}-?\d{4}-?\d{4}-?\d{4}$/; + reAmerica = /^6011-?\d{4}-?\d{4}-?\d{4}$/; + reDiscover = /^3[4,7]\d{13}$/; + $el = $("#card_logos_signup"); + validCard = false; + if (e.currentTarget.value.match(reVisa)) { + $el.find("#overlay_left").css('width', "0px"); + return $el.find("#overlay_right").css('width', "75%"); + } else if (e.currentTarget.value.match(reMaster)) { + $el.find("#overlay_left").css('width', "25%"); + return $el.find("#overlay_right").css('width', "50%"); + } else if (e.currentTarget.value.match(reAmerica)) { + $el.find("#overlay_left").css('width', "75%"); + $el.find("#overlay_right").css('width', "0px"); + return console.log("amex"); + } else if (e.currentTarget.value.match(reDiscover)) { + $el.find("#overlay_left").css('width', "50%"); + return $el.find("#overlay_right").css('width', "25%"); + } else { + $el.find("#overlay_left").css('width', "0px"); + return $el.find("#overlay_right").css('width', "0px"); + } + }; + return ClientsSignUpView; + })(); +}).call(this); +}, "views/clients/trip_detail": function(exports, require, module) {(function() { + var clientsTripDetailTemplate; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsTripDetailTemplate = require('templates/clients/trip_detail'); + exports.TripDetailView = (function() { + __extends(TripDetailView, UberView); + function TripDetailView() { + this.resendReceipt = __bind(this.resendReceipt, this); + TripDetailView.__super__.constructor.apply(this, arguments); + } + TripDetailView.prototype.id = 'trip_detail_view'; + TripDetailView.prototype.className = 'view_container'; + TripDetailView.prototype.events = { + 'click a#fare_review': 'showFareReview', + 'click #fare_review_hide': 'hideFareReview', + 'submit #form_review_form': 'submitFareReview', + 'click #submit_fare_review': 'submitFareReview', + 'click .resendReceipt': 'resendReceipt' + }; + TripDetailView.prototype.render = function(id) { + if (id == null) { + id = 'invalid'; + } + this.ReadUserInfo(); + this.HideSpinner(); + this.model = new app.models.trip({ + id: id + }); + this.model.fetch({ + data: { + relationships: 'points,driver,city.country' + }, + dataType: 'json', + success: __bind(function() { + var trip; + trip = this.model; + $(this.el).html(clientsTripDetailTemplate({ + trip: trip + })); + this.RequireMaps(__bind(function() { + var bounds, endPos, map, myOptions, path, polyline, startPos; + bounds = new google.maps.LatLngBounds(); + path = []; + _.each(this.model.get('points'), __bind(function(point) { + path.push(new google.maps.LatLng(point.lat, point.lng)); + return bounds.extend(_.last(path)); + }, this)); + myOptions = { + zoom: 12, + center: path[0], + mapTypeId: google.maps.MapTypeId.ROADMAP, + zoomControl: false, + rotateControl: false, + panControl: false, + mapTypeControl: false, + scrollwheel: false + }; + map = new google.maps.Map(document.getElementById("trip_details_map"), myOptions); + map.fitBounds(bounds); + startPos = new google.maps.Marker({ + position: _.first(path), + map: map, + title: t("Trip started here"), + icon: 'https://uber-static.s3.amazonaws.com/marker_start.png' + }); + endPos = new google.maps.Marker({ + position: _.last(path), + map: map, + title: t("Trip ended here"), + icon: 'https://uber-static.s3.amazonaws.com/marker_end.png' + }); + startPos.setMap(map); + endPos.setMap(map); + polyline = new google.maps.Polyline({ + path: path, + strokeColor: '#003F87', + strokeOpacity: 1, + strokeWeight: 5 + }); + return polyline.setMap(map); + }, this)); + return this.HideSpinner(); + }, this) + }); + this.ShowSpinner('load'); + this.delegateEvents(); + return this; + }; + TripDetailView.prototype.showFareReview = function(e) { + e.preventDefault(); + $('#fare_review_box').slideDown(); + return $('#fare_review').hide(); + }; + TripDetailView.prototype.hideFareReview = function(e) { + e.preventDefault(); + $('#fare_review_box').slideUp(); + return $('#fare_review').show(); + }; + TripDetailView.prototype.submitFareReview = function(e) { + var attrs, errorMessage, id, options; + e.preventDefault(); + errorMessage = $(".error_message"); + errorMessage.hide(); + id = $("#tripid").val(); + this.model = new app.models.trip({ + id: id + }); + attrs = { + note: $('#form_review_message').val(), + note_type: 'client_fare_review' + }; + options = { + success: __bind(function(response) { + $(".success_message").fadeIn(); + return $("#fare_review_form_wrapper").slideUp(); + }, this), + error: __bind(function(error) { + return errorMessage.fadeIn(); + }, this) + }; + return this.model.save(attrs, options); + }; + TripDetailView.prototype.resendReceipt = function(e) { + var $e; + e.preventDefault(); + $e = $(e.currentTarget); + this.$(".resendReceiptSuccess").empty().show(); + this.$(".resentReceiptError").empty().show(); + e.preventDefault(); + $('#spinner').show(); + return $.ajax('/api/trips/func/resend_receipt', { + data: { + token: $.cookie('token'), + trip_id: this.model.id + }, + type: 'POST', + complete: __bind(function(xhr) { + var response; + response = JSON.parse(xhr.responseText); + $('#spinner').hide(); + switch (xhr.status) { + case 200: + this.$(".resendReceiptSuccess").html("Receipt has been emailed"); + return this.$(".resendReceiptSuccess").fadeOut(2000); + default: + this.$(".resendReceiptError").html("Receipt has failed to be emailed"); + return this.$(".resendReceiptError").fadeOut(2000); + } + }, this) + }); + }; + return TripDetailView; + })(); +}).call(this); +}, "views/shared/menu": function(exports, require, module) {(function() { + var menuTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + menuTemplate = require('templates/shared/menu'); + exports.SharedMenuView = (function() { + __extends(SharedMenuView, Backbone.View); + function SharedMenuView() { + SharedMenuView.__super__.constructor.apply(this, arguments); + } + SharedMenuView.prototype.id = 'menu_view'; + SharedMenuView.prototype.render = function() { + var type; + if ($.cookie('token') === null) { + type = 'guest'; + } else { + type = 'client'; + } + $(this.el).html(menuTemplate({ + type: type + })); + return this; + }; + return SharedMenuView; + })(); +}).call(this); +}, "web-lib/collections/countries": function(exports, require, module) {(function() { + var UberCollection; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + UberCollection = require('web-lib/uber_collection').UberCollection; + exports.CountriesCollection = (function() { + __extends(CountriesCollection, UberCollection); + function CountriesCollection() { + CountriesCollection.__super__.constructor.apply(this, arguments); + } + CountriesCollection.prototype.model = app.models.country; + CountriesCollection.prototype.url = '/countries'; + return CountriesCollection; + })(); +}).call(this); +}, "web-lib/collections/vehicle_types": function(exports, require, module) {(function() { + var UberCollection, vehicleType, _ref; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + UberCollection = require('web-lib/uber_collection').UberCollection; + vehicleType = (typeof app !== "undefined" && app !== null ? (_ref = app.models) != null ? _ref.vehicleType : void 0 : void 0) || require('models/vehicle_type').VehicleType; + exports.VehicleTypesCollection = (function() { + __extends(VehicleTypesCollection, UberCollection); + function VehicleTypesCollection() { + VehicleTypesCollection.__super__.constructor.apply(this, arguments); + } + VehicleTypesCollection.prototype.model = vehicleType; + VehicleTypesCollection.prototype.url = '/vehicle_types'; + VehicleTypesCollection.prototype.defaultColumns = ['id', 'created_at', 'updated_at', 'deleted_at', 'created_by_user_id', 'updated_by_user_id', 'city_id', 'type', 'make', 'model', 'capacity', 'minimum_year', 'actions']; + VehicleTypesCollection.prototype.tableColumns = function(cols) { + var actions, c, capacity, city_id, columnValues, created_at, created_by_user_id, deleted_at, headerRow, id, make, minimum_year, model, type, updated_at, updated_by_user_id, _i, _len; + id = { + sTitle: 'Id' + }; + created_at = { + sTitle: 'Created At (UTC)', + 'sType': 'string' + }; + updated_at = { + sTitle: 'Updated At (UTC)', + 'sType': 'string' + }; + deleted_at = { + sTitle: 'Deleted At (UTC)', + 'sType': 'string' + }; + created_by_user_id = { + sTitle: 'Created By' + }; + updated_by_user_id = { + sTitle: 'Updated By' + }; + city_id = { + sTitle: 'City' + }; + type = { + sTitle: 'Type' + }; + make = { + sTitle: 'Make' + }; + model = { + sTitle: 'Model' + }; + capacity = { + sTitle: 'Capacity' + }; + minimum_year = { + sTitle: 'Min. Year' + }; + actions = { + sTitle: 'Actions' + }; + columnValues = { + id: id, + created_at: created_at, + updated_at: updated_at, + deleted_at: deleted_at, + created_by_user_id: created_by_user_id, + updated_by_user_id: updated_by_user_id, + city_id: city_id, + type: type, + make: make, + model: model, + capacity: capacity, + minimum_year: minimum_year, + actions: actions + }; + headerRow = []; + for (_i = 0, _len = cols.length; _i < _len; _i++) { + c = cols[_i]; + if (columnValues[c]) { + headerRow.push(columnValues[c]); + } + } + return headerRow; + }; + return VehicleTypesCollection; + })(); +}).call(this); +}, "web-lib/helpers": function(exports, require, module) {(function() { + var __indexOf = Array.prototype.indexOf || function(item) { + for (var i = 0, l = this.length; i < l; i++) { + if (this[i] === item) return i; + } + return -1; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + exports.helpers = { + pin: function(num, color) { + if (color == null) { + color = 'FF0000'; + } + return ""; + }, + reverseGeocode: function(latitude, longitude) { + if (latitude && longitude) { + return "" + latitude + ", " + longitude + ""; + } else { + return ''; + } + }, + linkedName: function(model) { + var first_name, id, last_name, role, url; + role = model.role || model.get('role'); + id = model.id || model.get('id'); + first_name = model.first_name || model.get('first_name'); + last_name = model.last_name || model.get('last_name'); + url = "/" + role + "s/" + id; + return "" + first_name + " " + last_name + ""; + }, + linkedVehicle: function(vehicle, vehicleType) { + return " " + (vehicleType != null ? vehicleType.get('make') : void 0) + " " + (vehicleType != null ? vehicleType.get('model') : void 0) + " " + (vehicle.get('year')) + " "; + }, + linkedUserId: function(userType, userId) { + return "" + userType + " " + userId + ""; + }, + timeDelta: function(start, end) { + var delta; + if (typeof start === 'string') { + start = this.parseDate(start); + } + if (typeof end === 'string') { + end = this.parseDate(end); + } + if (end && start) { + delta = end.getTime() - start.getTime(); + return this.formatSeconds(delta / 1000); + } else { + return '00:00'; + } + }, + formatSeconds: function(s) { + var minutes, seconds; + s = Math.floor(s); + minutes = Math.floor(s / 60); + seconds = s - minutes * 60; + return "" + (this.leadingZero(minutes)) + ":" + (this.leadingZero(seconds)); + }, + formatCurrency: function(strValue, reverseSign, currency) { + var currency_locale, lc, mf; + if (reverseSign == null) { + reverseSign = false; + } + if (currency == null) { + currency = null; + } + strValue = String(strValue); + if (reverseSign) { + strValue = ~strValue.indexOf('-') ? strValue.split('-').join('') : ['-', strValue].join(''); + } + currency_locale = i18n.currencyToLocale[currency]; + try { + if (!(currency_locale != null) || currency_locale === i18n.locale) { + return i18n.jsworld.mf.format(strValue); + } else { + lc = new jsworld.Locale(POSIX_LC[currency_locale]); + mf = new jsworld.MonetaryFormatter(lc); + return mf.format(strValue); + } + } catch (error) { + i18n.log(error); + return strValue; + } + }, + formatTripFare: function(trip, type) { + var _ref, _ref2; + if (type == null) { + type = "fare"; + } + if (!trip.get('fare')) { + return 'n/a'; + } + if (((_ref = trip.get('fare_breakdown_local')) != null ? _ref.currency : void 0) != null) { + return app.helpers.formatCurrency(trip.get("" + type + "_local"), false, (_ref2 = trip.get('fare_breakdown_local')) != null ? _ref2.currency : void 0); + } else if (trip.get("" + type + "_string") != null) { + return trip.get("" + type + "_string"); + } else if (trip.get("" + type + "_local") != null) { + return trip.get("" + type + "_local"); + } else { + return 'n/a'; + } + }, + formatPhoneNumber: function(phoneNumber, countryCode) { + if (countryCode == null) { + countryCode = "+1"; + } + if (phoneNumber != null) { + phoneNumber = String(phoneNumber); + switch (countryCode) { + case '+1': + return countryCode + ' ' + phoneNumber.substring(0, 3) + '-' + phoneNumber.substring(3, 6) + '-' + phoneNumber.substring(6, 10); + case '+33': + return countryCode + ' ' + phoneNumber.substring(0, 1) + ' ' + phoneNumber.substring(1, 3) + ' ' + phoneNumber.substring(3, 5) + ' ' + phoneNumber.substring(5, 7) + ' ' + phoneNumber.substring(7, 9); + default: + countryCode + phoneNumber; + } + } + return "" + countryCode + " " + phoneNumber; + }, + parseDate: function(d, cityTime, tz) { + var city_filter, parsed, _ref; + if (cityTime == null) { + cityTime = true; + } + if (tz == null) { + tz = null; + } + if (((_ref = !d.substr(-6, 1)) === '+' || _ref === '-') || d.length === 19) { + d += '+00:00'; + } + if (/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})/.test(d)) { + parsed = d.match(/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/); + d = new Date(); + d.setUTCFullYear(parsed[1]); + d.setUTCMonth(parsed[2] - 1); + d.setUTCDate(parsed[3]); + d.setUTCHours(parsed[4]); + d.setUTCMinutes(parsed[5]); + d.setUTCSeconds(parsed[6]); + } else { + d = Date.parse(d); + } + if (typeof d === 'number') { + d = new Date(d); + } + d = new timezoneJS.Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), 'Etc/UTC'); + if (tz) { + d.convertToTimezone(tz); + } else if (cityTime) { + city_filter = $.cookie('city_filter'); + if (city_filter) { + tz = $("#city_filter option[value=" + city_filter + "]").attr('data-timezone'); + if (tz) { + d.convertToTimezone(tz); + } + } + } + return d; + }, + dateToTimezone: function(d) { + var city_filter, tz; + d = new timezoneJS.Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), 'Etc/UTC'); + city_filter = $.cookie('city_filter'); + if (city_filter) { + tz = $("#city_filter option[value=" + city_filter + "]").attr('data-timezone'); + d.convertToTimezone(tz); + } + return d; + }, + fixAMPM: function(d, formatted) { + if (d.hours >= 12) { + return formatted.replace(/\b[AP]M\b/, 'PM'); + } else { + return formatted.replace(/\b[AP]M\b/, 'AM'); + } + }, + formatDate: function(d, time, timezone) { + var formatted; + if (time == null) { + time = true; + } + if (timezone == null) { + timezone = null; + } + d = this.parseDate(d, true, timezone); + formatted = time ? ("" + (i18n.jsworld.dtf.formatDate(d)) + " ") + this.formatTime(d, d.getTimezoneInfo()) : i18n.jsworld.dtf.formatDate(d); + return this.fixAMPM(d, formatted); + }, + formatDateLong: function(d, time, timezone) { + if (time == null) { + time = true; + } + if (timezone == null) { + timezone = null; + } + d = this.parseDate(d, true, timezone); + timezone = d.getTimezoneInfo().tzAbbr; + if (time) { + return (i18n.jsworld.dtf.formatDateTime(d)) + (" " + timezone); + } else { + return i18n.jsworld.dtf.formatDate(d); + } + }, + formatTimezoneJSDate: function(d) { + var day, hours, jsDate, minutes, month, year; + year = d.getFullYear(); + month = this.leadingZero(d.getMonth()); + day = this.leadingZero(d.getDate()); + hours = this.leadingZero(d.getHours()); + minutes = this.leadingZero(d.getMinutes()); + jsDate = new Date(year, month, day, hours, minutes, 0); + return jsDate.toDateString(); + }, + formatTime: function(d, timezone) { + var formatted; + if (timezone == null) { + timezone = null; + } + formatted = ("" + (i18n.jsworld.dtf.formatTime(d))) + (timezone != null ? " " + (timezone != null ? timezone.tzAbbr : void 0) : ""); + return this.fixAMPM(d, formatted); + }, + formatISODate: function(d) { + var pad; + pad = function(n) { + if (n < 10) { + return '0' + n; + } + return n; + }; + return d.getUTCFullYear() + '-' + pad(d.getUTCMonth() + 1) + '-' + pad(d.getUTCDate()) + 'T' + pad(d.getUTCHours()) + ':' + pad(d.getUTCMinutes()) + ':' + pad(d.getUTCSeconds()) + 'Z'; + }, + formatExpDate: function(d) { + var month, year; + d = this.parseDate(d); + year = d.getFullYear(); + month = this.leadingZero(d.getMonth() + 1); + return "" + year + "-" + month; + }, + formatLatLng: function(lat, lng, precision) { + if (precision == null) { + precision = 8; + } + return parseFloat(lat).toFixed(precision) + ',' + parseFloat(lng).toFixed(precision); + }, + leadingZero: function(num) { + if (num < 10) { + return "0" + num; + } else { + return num; + } + }, + roundNumber: function(num, dec) { + return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec); + }, + notesToHTML: function(notes) { + var i, note, notesHTML, _i, _len; + notesHTML = ''; + i = 1; + if (notes) { + for (_i = 0, _len = notes.length; _i < _len; _i++) { + note = notes[_i]; + notesHTML += "" + note['userid'] + "     " + (this.formatDate(note['created_at'])) + "

" + note['note'] + "

"; + notesHTML += "
"; + } + } + return notesHTML.replace("'", '"e'); + }, + formatPhone: function(n) { + var parts, phone, regexObj; + n = "" + n; + regexObj = /^(?:\+?1[-. ]?)?(?:\(?([0-9]{3})\)?[-. ]?)?([0-9]{3})[-. ]?([0-9]{4})$/; + if (regexObj.test(n)) { + parts = n.match(regexObj); + phone = ""; + if (parts[1]) { + phone += "(" + parts[1] + ") "; + } + phone += "" + parts[2] + "-" + parts[3]; + } else { + phone = n; + } + return phone; + }, + usStates: ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'District of Columbia', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'], + onboardingPages: ['applied', 'ready_to_interview', 'pending_interview', 'interviewed', 'accepted', 'ready_to_onboard', 'pending_onboarding', 'active', 'waitlisted', 'rejected'], + driverBreadCrumb: function(loc, model) { + var onboardingPage, out, _i, _len, _ref; + out = "Drivers > "; + if (!(model != null)) { + out += ""; + } else { + out += "" + (this.onboardingUrlToName(model.get('driver_status'))) + ""; + out += " > " + (this.linkedName(model)) + " (" + (model.get('role')) + ") #" + (model.get('id')); + } + return out; + }, + onboardingUrlToName: function(url) { + return url != null ? url.replace(/_/g, " ").replace(/(^|\s)([a-z])/g, function(m, p1, p2) { + return p1 + p2.toUpperCase(); + }) : void 0; + }, + formatVehicle: function(vehicle) { + if (vehicle.get('make') && vehicle.get('model') && vehicle.get('license_plate')) { + return "" + (vehicle.get('make')) + " " + (vehicle.get('model')) + " (" + (vehicle.get('license_plate')) + ")"; + } + }, + docArbitraryFields: function(docName, cityDocs) { + var doc, field, out, _i, _j, _len, _len2, _ref; + out = ""; + for (_i = 0, _len = cityDocs.length; _i < _len; _i++) { + doc = cityDocs[_i]; + if (doc.name === docName && __indexOf.call(_.keys(doc), "metaFields") >= 0) { + _ref = doc.metaFields; + for (_j = 0, _len2 = _ref.length; _j < _len2; _j++) { + field = _ref[_j]; + out += "" + field.label + ":
"; + } + } + } + return out; + }, + capitaliseFirstLetter: function(string) { + return string.charAt(0).toUpperCase() + string.slice(1); + }, + createDocUploadForm: function(docName, driverId, vehicleId, cityMeta, vehicleName, expirationRequired) { + var ddocs, expDropdowns, pdocs, vdocs; + if (driverId == null) { + driverId = "None"; + } + if (vehicleId == null) { + vehicleId = "None"; + } + if (cityMeta == null) { + cityMeta = []; + } + if (vehicleName == null) { + vehicleName = false; + } + if (expirationRequired == null) { + expirationRequired = false; + } + ddocs = cityMeta["driverRequiredDocs"] || []; + pdocs = cityMeta["partnerRequiredDocs"] || []; + vdocs = cityMeta["vehicleRequiredDocs"] || []; + expDropdowns = "Expiration Date:\n -\n"; + return " \n
\n \n \n \n\n
\n " + (vehicleName ? vehicleName : "") + " " + docName + "\n
\n\n
\n \n
\n\n
\n " + (expirationRequired ? expDropdowns : "") + "\n
\n\n
\n " + (app.helpers.docArbitraryFields(docName, _.union(ddocs, pdocs, vdocs))) + "\n
\n\n
\n \n
\n\n
\n"; + }, + countrySelector: function(name, options) { + var countries, countryCodePrefix, defaultOptions; + if (options == null) { + options = {}; + } + defaultOptions = { + selectedKey: 'telephone_code', + selectedValue: '+1', + silent: false + }; + _.extend(defaultOptions, options); + options = defaultOptions; + countries = new app.collections.countries(); + countries.fetch({ + data: { + limit: 300 + }, + success: function(countries) { + var $option, $select, country, selected, _i, _len, _ref; + selected = false; + _ref = countries.models || []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + country = _ref[_i]; + $select = $("select[name=" + name + "]"); + $option = $('').val(country.id).attr('data-iso2', country.get('iso2')).attr('data-prefix', country.get('telephone_code')).html(country.get('name')); + if (country.get(options.selectedKey) === options.selectedValue && !selected) { + selected = true; + $option.attr('selected', 'selected'); + } + $select.append($option); + } + if (selected && !options.silent) { + return $select.val(options.selected).trigger('change'); + } + } + }); + countryCodePrefix = options.countryCodePrefix ? "data-country-code-prefix='" + options.countryCodePrefix + "'" : ''; + return ""; + }, + missingDocsOnDriver: function(driver) { + var city, docsReq, documents, partnerDocs; + city = driver.get('city'); + documents = driver.get('documents'); + if ((city != null) && (documents != null)) { + docsReq = _.pluck(city != null ? city.get('meta')["driverRequiredDocs"] : void 0, "name"); + if (driver.get('role') === "partner") { + partnerDocs = _.pluck(city != null ? city.get('meta')["partnerRequiredDocs"] : void 0, "name"); + docsReq = _.union(docsReq, partnerDocs); + } + return _.reject(docsReq, __bind(function(doc) { + return __indexOf.call((documents != null ? documents.pluck("name") : void 0) || [], doc) >= 0; + }, this)); + } else { + return []; + } + } + }; +}).call(this); +}, "web-lib/i18n": function(exports, require, module) {(function() { + exports.i18n = { + defaultLocale: 'en_US', + cookieName: '_LOCALE_', + locales: { + 'en_US': "English (US)", + 'fr_FR': "Français" + }, + currencyToLocale: { + 'USD': 'en_US', + 'EUR': 'fr_FR' + }, + logglyKey: 'd2d5a9bc-7ebe-4538-a180-81e62c705b1b', + logglyHost: 'https://logs.loggly.com', + init: function() { + this.castor = new window.loggly({ + url: this.logglyHost + '/inputs/' + this.logglyKey + '?rt=1', + level: 'error' + }); + this.setLocale($.cookie(this.cookieName) || this.defaultLocale); + window.t = _.bind(this.t, this); + this.loadLocaleTranslations(this.locale); + if (!(this[this.defaultLocale] != null)) { + return this.loadLocaleTranslations(this.defaultLocale); + } + }, + loadLocaleTranslations: function(locale) { + var loadPaths, path, _i, _len, _results; + loadPaths = ['web-lib/translations/' + locale, 'web-lib/translations/' + locale.slice(0, 2), 'translations/' + locale, 'translations/' + locale.slice(0, 2)]; + _results = []; + for (_i = 0, _len = loadPaths.length; _i < _len; _i++) { + path = loadPaths[_i]; + locale = path.substring(path.lastIndexOf('/') + 1); + if (this[locale] == null) { + this[locale] = {}; + } + _results.push((function() { + try { + return _.extend(this[locale], require(path).translations); + } catch (error) { + + } + }).call(this)); + } + return _results; + }, + getLocale: function() { + return this.locale; + }, + setLocale: function(locale) { + var message, parts, _ref; + parts = locale.split('_'); + this.locale = parts[0].toLowerCase(); + if (parts.length > 1) { + this.locale += "_" + (parts[1].toUpperCase()); + } + if (this.locale) { + $.cookie(this.cookieName, this.locale, { + path: '/', + domain: '.uber.com' + }); + } + try { + ((_ref = this.jsworld) != null ? _ref : this.jsworld = {}).lc = new jsworld.Locale(POSIX_LC[this.locale]); + this.jsworld.mf = new jsworld.MonetaryFormatter(this.jsworld.lc); + this.jsworld.nf = new jsworld.NumericFormatter(this.jsworld.lc); + this.jsworld.dtf = new jsworld.DateTimeFormatter(this.jsworld.lc); + this.jsworld.np = new jsworld.NumericParser(this.jsworld.lc); + this.jsworld.mp = new jsworld.MonetaryParser(this.jsworld.lc); + return this.jsworld.dtp = new jsworld.DateTimeParser(this.jsworld.lc); + } catch (error) { + message = 'JsWorld error with locale: ' + this.locale; + return this.log({ + message: message, + error: error + }); + } + }, + getTemplate: function(id) { + var _ref, _ref2; + return ((_ref = this[this.locale]) != null ? _ref[id] : void 0) || ((_ref2 = this[this.locale.slice(0, 2)]) != null ? _ref2[id] : void 0); + }, + getTemplateDefault: function(id) { + var _ref, _ref2; + return ((_ref = this[this.defaultLocale]) != null ? _ref[id] : void 0) || ((_ref2 = this[this.defaultLocale.slice(0, 2)]) != null ? _ref2[id] : void 0); + }, + getTemplateOrDefault: function(id) { + return this.getTemplate(id) || this.getTemplateDefault(id); + }, + t: function(id, vars) { + var errStr, locale, template; + if (vars == null) { + vars = {}; + } + locale = this.getLocale(); + template = this.getTemplate(id); + if (template == null) { + if (/dev|test/.test(window.location.host)) { + template = "(?) " + id; + } else { + template = this.getTemplateDefault(id); + } + errStr = "Missing [" + locale + "] translation for [" + id + "] at [" + window.location.hash + "] - Default template is [" + template + "]"; + this.log({ + error: errStr, + locale: locale, + id: id, + defaultTemplate: template + }); + } + if (template) { + return _.template(template, vars); + } else { + return id; + } + }, + log: function(error) { + if (/dev/.test(window.location.host)) { + if ((typeof console !== "undefined" && console !== null ? console.log : void 0) != null) { + return console.log(error); + } + } else { + _.extend(error, { + host: window.location.host, + hash: window.location.hash + }); + return this.castor.error(JSON.stringify(error)); + } + } + }; +}).call(this); +}, "web-lib/mixins/i18n_phone_form": function(exports, require, module) {(function() { + exports.i18nPhoneForm = { + _events: { + 'change select[data-country-code-prefix]': 'setCountryCodePrefix' + }, + setCountryCodePrefix: function(e) { + var $el, prefix; + $el = $(e.currentTarget); + prefix = $el.find('option:selected').attr('data-prefix'); + return $("#" + ($el.attr('data-country-code-prefix'))).text(prefix); + } + }; +}).call(this); +}, "web-lib/models/country": function(exports, require, module) {(function() { + var UberModel; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + UberModel = require('web-lib/uber_model').UberModel; + exports.Country = (function() { + __extends(Country, UberModel); + function Country() { + Country.__super__.constructor.apply(this, arguments); + } + Country.prototype.url = function() { + if (this.id) { + return "/countries/" + this.id; + } else { + return '/countries'; + } + }; + return Country; + })(); +}).call(this); +}, "web-lib/models/vehicle_type": function(exports, require, module) {(function() { + var UberModel; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + UberModel = require('web-lib/uber_model').UberModel; + exports.VehicleType = (function() { + __extends(VehicleType, UberModel); + function VehicleType() { + this.toString = __bind(this.toString, this); + VehicleType.__super__.constructor.apply(this, arguments); + } + VehicleType.prototype.endpoint = 'vehicle_types'; + VehicleType.prototype.toTableRow = function(cols) { + var actions, c, capacity, city_id, columnValues, created_at, created_by_user_id, deleted_at, id, make, minimum_year, model, rows, type, updated_at, updated_by_user_id, _i, _len, _ref; + id = "" + (this.get('id')) + ""; + if (this.get('created_at')) { + created_at = app.helpers.formatDate(this.get('created_at')); + } + if (this.get('updated_at')) { + updated_at = app.helpers.formatDate(this.get('updated_at')); + } + if (this.get('deleted_at')) { + deleted_at = app.helpers.formatDate(this.get('deleted_at')); + } + created_by_user_id = "" + (this.get('created_by_user_id')) + ""; + updated_by_user_id = "" + (this.get('updated_by_user_id')) + ""; + city_id = (_ref = this.get('city')) != null ? _ref.get('display_name') : void 0; + type = this.get('type'); + make = this.get('make'); + model = this.get('model'); + capacity = this.get('capacity'); + minimum_year = this.get('minimum_year'); + actions = "Show"; + if (!this.get('deleted_at')) { + actions += " Edit"; + actions += " Delete"; + } + columnValues = { + id: id, + created_at: created_at, + updated_at: updated_at, + deleted_at: deleted_at, + created_by_user_id: created_by_user_id, + updated_by_user_id: updated_by_user_id, + city_id: city_id, + type: type, + make: make, + model: model, + capacity: capacity, + minimum_year: minimum_year, + actions: actions + }; + rows = []; + for (_i = 0, _len = cols.length; _i < _len; _i++) { + c = cols[_i]; + rows.push(columnValues[c] ? columnValues[c] : '-'); + } + return rows; + }; + VehicleType.prototype.toString = function() { + return this.get('make') + ' ' + this.get('model') + ' ' + this.get('type') + (" (" + (this.get('capacity')) + ")"); + }; + return VehicleType; + })(); +}).call(this); +}, "web-lib/templates/footer": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + var locale, title, _ref; + __out.push('\n\n\n\n\n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "web-lib/translations/en": function(exports, require, module) {(function() { + exports.translations = { + "Info": "Info", + "Learn More": "Learn More", + "Pricing": "Pricing", + "FAQ": "FAQ", + "Support": "Support", + "Support & FAQ": "Support & FAQ", + "Contact Us": "Contact Us", + "Jobs": "Jobs", + "Phones": "Phones", + "Text Message": "Text Message", + "iPhone": "iPhone", + "Android": "Android", + "Drivers": "Drivers", + "Apply": "Apply", + "Sign In": "Sign In", + "Social": "Social", + "Twitter": "Twitter", + "Facebook": "Facebook", + "Blog": "Blog", + "Legal": "Legal", + "Company_Footer": "Company", + "Privacy Policy": "Privacy Policy", + "Terms": "Terms", + "Copyright © Uber Technologies, Inc.": "Copyright © Uber Technologies, Inc.", + "Language:": "Language:", + "Apply to Drive": "Apply to Drive", + "Expiration": "Expiration", + "Fare": "Fare", + "Driver": "Driver ", + "Dashboard": "Dashboard", + "Forgot Password": "Forgot Password", + "Trip Details": "Trip Details", + "Save": "Save", + "Cancel": "Cancel", + "Edit": "Edit", + "Password": "Password", + "First Name": "First Name", + "Last Name": "Last Name", + "Email Address": "Email Address", + "Submit": "Submit", + "Mobile Number": "Mobile Number", + "Zip Code": "Zip Code", + "Sign Out": "Sign Out", + "Confirm Email Message": "Attempting to confirm email...", + "Upload": "Upload", + "Rating": "Rating", + "Pickup Time": "Pickup Time", + "2011": "2011", + "2012": "2012", + "2013": "2013", + "2014": "2014", + "2015": "2015", + "2016": "2016", + "2017": "2017", + "2018": "2018", + "2019": "2019", + "2020": "2020", + "2021": "2021", + "2022": "2022", + "01": "01", + "02": "02", + "03": "03", + "04": "04", + "05": "05", + "06": "06", + "07": "07", + "08": "08", + "09": "09", + "10": "10", + "11": "11", + "12": "12" + }; +}).call(this); +}, "web-lib/translations/fr": function(exports, require, module) {(function() { + exports.translations = { + "Info": "Info", + "Learn More": "En Savoir Plus", + "Pricing": "Calcul du Prix", + "Support & FAQ": "Aide & FAQ", + "Contact Us": "Contactez Nous", + "Jobs": "Emplois", + "Phones": "Téléphones", + "Text Message": "SMS", + "iPhone": "iPhone", + "Android": "Android", + "Apply to Drive": "Candidature Chauffeur", + "Sign In": "Connexion", + "Social": "Contact", + "Twitter": "Twitter", + "Facebook": "Facebook", + "Blog": "Blog", + "Privacy Policy": "Protection des Données Personelles", + "Terms": "Conditions Générales", + "Copyright © Uber Technologies, Inc.": "© Uber, Inc.", + "Language:": "Langue:", + "Forgot Password": "Mot de passe oublié", + "Company_Footer": "À Propos d'Uber", + "Expiration": "Expiration", + "Fare": "Tarif", + "Driver": "Chauffeur", + "Drivers": "Chauffeurs", + "Dashboard": "Tableau de bord", + "Forgot Password": "Mot de passe oublié", + "Forgot Password?": "Mot de passe oublié?", + "Trip Details": "Détails de la course", + "Save": "Enregistrer", + "Cancel": "Annuler", + "Edit": "Modifier", + "Password": "Mot de passe", + "First Name": "Prénom", + "Last Name": "Nom", + "Email Address": "E-mail", + "Submit": "Soumettre", + "Mobile Number": "Téléphone Portable", + "Zip Code": "Code Postal", + "Sign Out": "Se déconnecter", + "Confirm Email Message": "E-mail de confirmation", + "Upload": "Télécharger", + "Rating": "Notation", + "Pickup Time": "Heure de prise en charge", + "2011": "2011", + "2012": "2012", + "2013": "2013", + "2014": "2014", + "2015": "2015", + "2016": "2016", + "2017": "2017", + "2018": "2018", + "2019": "2019", + "2020": "2020", + "2021": "2021", + "2022": "2022", + "01": "01", + "02": "02", + "03": "03", + "04": "04", + "05": "05", + "06": "06", + "07": "07", + "08": "08", + "09": "09", + "10": "10", + "11": "11", + "12": "12" + }; +}).call(this); +}, "web-lib/uber_collection": function(exports, require, module) {(function() { + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + exports.UberCollection = (function() { + __extends(UberCollection, Backbone.Collection); + function UberCollection() { + UberCollection.__super__.constructor.apply(this, arguments); + } + UberCollection.prototype.parse = function(data) { + var model, tmp, _i, _in, _len, _out; + _in = data.resources || data; + _out = []; + if (data.meta) { + this.meta = data.meta; + } + for (_i = 0, _len = _in.length; _i < _len; _i++) { + model = _in[_i]; + tmp = new this.model; + tmp.set(tmp.parse(model)); + _out.push(tmp); + } + return _out; + }; + UberCollection.prototype.isRenderable = function() { + if (this.models.length) { + return true; + } + }; + UberCollection.prototype.toTableRows = function(cols) { + var tableRows; + tableRows = []; + _.each(this.models, function(model) { + return tableRows.push(model.toTableRow(cols)); + }); + return tableRows; + }; + return UberCollection; + })(); +}).call(this); +}, "web-lib/uber_model": function(exports, require, module) {(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __indexOf = Array.prototype.indexOf || function(item) { + for (var i = 0, l = this.length; i < l; i++) { + if (this[i] === item) return i; + } + return -1; + }; + exports.UberModel = (function() { + __extends(UberModel, Backbone.Model); + function UberModel() { + this.refetch = __bind(this.refetch, this); + this.fetch = __bind(this.fetch, this); + this.save = __bind(this.save, this); + this.parse = __bind(this.parse, this); + UberModel.__super__.constructor.apply(this, arguments); + } + UberModel.prototype.endpoint = 'set_api_endpoint_in_subclass'; + UberModel.prototype.refetchOptions = {}; + UberModel.prototype.url = function(type) { + var endpoint_path; + endpoint_path = "/" + this.endpoint; + if (this.get('id')) { + return endpoint_path + ("/" + (this.get('id'))); + } else { + return endpoint_path; + } + }; + UberModel.prototype.isRenderable = function() { + var i, key, value, _ref; + i = 0; + _ref = this.attributes; + for (key in _ref) { + if (!__hasProp.call(_ref, key)) continue; + value = _ref[key]; + if (this.attributes.hasOwnProperty(key)) { + i += 1; + } + if (i > 1) { + return true; + } + } + return !(i === 1); + }; + UberModel.prototype.parse = function(response) { + var attrs, key, model, models, _i, _j, _k, _len, _len2, _len3, _ref, _ref2; + if (typeof response === 'object') { + _ref = _.intersection(_.keys(app.models), _.keys(response)); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + if (response[key]) { + attrs = this.parse(response[key]); + if (typeof attrs === 'object') { + response[key] = new app.models[key](attrs); + } + } + } + _ref2 = _.intersection(_.keys(app.collections), _.keys(response)); + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + key = _ref2[_j]; + models = response[key]; + if (_.isArray(models)) { + response[key] = new app.collections[key]; + for (_k = 0, _len3 = models.length; _k < _len3; _k++) { + model = models[_k]; + attrs = app.collections[key].prototype.model.prototype.parse(model); + response[key].add(new response[key].model(attrs)); + } + } + } + } + return response; + }; + UberModel.prototype.save = function(attributes, options) { + var attr, _i, _j, _len, _len2, _ref, _ref2; + if (options == null) { + options = {}; + } + _ref = _.intersection(_.keys(app.models), _.keys(this.attributes)); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + attr = _ref[_i]; + if (typeof this.get(attr) === "object") { + this.unset(attr, { + silent: true + }); + } + } + _ref2 = _.intersection(_.keys(app.collections), _.keys(this.attributes)); + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + attr = _ref2[_j]; + if (typeof this.get(attr) === "object") { + this.unset(attr, { + silent: true + }); + } + } + if ((options != null) && options.diff && (attributes != null) && attributes !== {}) { + attributes['id'] = this.get('id'); + attributes['token'] = this.get('token'); + this.clear({ + 'silent': true + }); + this.set(attributes, { + silent: true + }); + } + if (__indexOf.call(_.keys(options), "data") < 0 && __indexOf.call(_.keys(this.refetchOptions || {}), "data") >= 0) { + options.data = this.refetchOptions.data; + } + return Backbone.Model.prototype.save.call(this, attributes, options); + }; + UberModel.prototype.fetch = function(options) { + this.refetchOptions = options; + return Backbone.Model.prototype.fetch.call(this, options); + }; + UberModel.prototype.refetch = function() { + return this.fetch(this.refetchOptions); + }; + return UberModel; + })(); +}).call(this); +}, "web-lib/uber_router": function(exports, require, module) {(function() { + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + exports.UberRouter = (function() { + __extends(UberRouter, Backbone.Router); + function UberRouter() { + UberRouter.__super__.constructor.apply(this, arguments); + } + UberRouter.prototype.datePickers = function(format) { + if (format == null) { + format = "%Z-%m-%dT%H:%i:%s%:"; + } + $('.datepicker').AnyTime_noPicker(); + return $('.datepicker').AnyTime_picker({ + 'format': format, + 'formatUtcOffset': '%@' + }); + }; + UberRouter.prototype.autoGrowInput = function() { + return $('.editable input').autoGrowInput(); + }; + UberRouter.prototype.windowTitle = function(title) { + return $(document).attr('title', title); + }; + return UberRouter; + })(); +}).call(this); +}, "web-lib/uber_show_view": function(exports, require, module) {(function() { + var UberView; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + UberView = require('web-lib/uber_view').UberView; + exports.UberShowView = (function() { + __extends(UberShowView, UberView); + function UberShowView() { + UberShowView.__super__.constructor.apply(this, arguments); + } + UberShowView.prototype.view = 'show'; + UberShowView.prototype.events = { + 'click #edit': 'edit', + 'submit form': 'save', + 'click .cancel': 'cancel' + }; + UberShowView.prototype.errors = null; + UberShowView.prototype.showTemplate = null; + UberShowView.prototype.editTemplate = null; + UberShowView.prototype.initialize = function() { + if (this.init_hook) { + this.init_hook(); + } + _.bindAll(this, 'render'); + return this.model.bind('change', this.render); + }; + UberShowView.prototype.render = function() { + var $el; + $el = $(this.el); + this.selectView(); + if (this.view === 'show') { + $el.html(this.showTemplate({ + model: this.model + })); + } else if (this.view === 'edit') { + $el.html(this.editTemplate({ + model: this.model, + errors: this.errors || {}, + collections: this.collections || {} + })); + } else { + $el.html(this.newTemplate({ + model: this.model, + errors: this.errors || {}, + collections: this.collections || {} + })); + } + if (this.render_hook) { + this.render_hook(); + } + this.errors = null; + this.userIdsToLinkedNames(); + this.datePickers(); + return this.place(); + }; + UberShowView.prototype.selectView = function() { + var url; + if (this.options.urlRendering) { + url = window.location.hash; + if (url.match(/\/new/)) { + return this.view = 'new'; + } else if (url.match(/\/edit/)) { + return this.view = 'edit'; + } else { + return this.view = 'show'; + } + } + }; + UberShowView.prototype.edit = function(e) { + e.preventDefault(); + if (this.options.urlRendering) { + window.location.hash = '#/' + this.model.endpoint + '/' + this.model.get('id') + '/edit'; + } else { + this.view = 'edit'; + } + return this.model.change(); + }; + UberShowView.prototype.save = function(e) { + var attributes, ele, form_attrs, _i, _len, _ref; + e.preventDefault(); + attributes = $(e.currentTarget).serializeToJson(); + form_attrs = {}; + _ref = $('input[type="radio"]'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + ele = _ref[_i]; + if ($(ele).is(':checked')) { + form_attrs[$(ele).attr('name')] = $(ele).attr('value'); + } + } + attributes = _.extend(attributes, form_attrs); + if (this.relationships) { + attributes = _.extend(attributes, { + relationships: this.relationships + }); + } + if (this.filter_attributes != null) { + this.filter_attributes(attributes); + } + return this.model.save(attributes, { + silent: true, + success: __bind(function(model) { + if (this.options.urlRendering) { + window.location.hash = '#/' + this.model.endpoint + '/' + this.model.get('id'); + } else { + this.view = 'show'; + } + return this.flash('success', "Uber save!"); + }, this), + statusCode: { + 406: __bind(function(xhr) { + this.errors = JSON.parse(xhr.responseText); + return this.flash('error', 'That was not Uber.'); + }, this) + }, + error: __bind(function(model, xhr) { + var code, message, responseJSON, responseText; + code = xhr.status; + responseText = xhr.responseText; + if (responseText) { + responseJSON = JSON.parse(responseText); + } + if (responseJSON && (typeof responseJSON === 'object') && (responseJSON.hasOwnProperty('error'))) { + message = responseJSON.error; + } + return this.flash('error', (code || 'Unknown') + ' error' + (': ' + message || '')); + }, this), + complete: __bind(function() { + return this.model.change(); + }, this) + }); + }; + UberShowView.prototype.cancel = function(e) { + e.preventDefault(); + if (this.options.urlRendering) { + window.location.hash = '#/' + this.model.endpoint + '/' + this.model.get('id'); + } else { + this.view = 'show'; + } + return this.model.fetch({ + silent: true, + complete: __bind(function() { + return this.model.change(); + }, this) + }); + }; + return UberShowView; + })(); +}).call(this); +}, "web-lib/uber_sync": function(exports, require, module) {(function() { + var methodType; + var __indexOf = Array.prototype.indexOf || function(item) { + for (var i = 0, l = this.length; i < l; i++) { + if (this[i] === item) return i; + } + return -1; + }; + methodType = { + create: 'POST', + update: 'PUT', + "delete": 'DELETE', + read: 'GET' + }; + exports.UberSync = function(method, model, options) { + var token; + options.type = methodType[method]; + options.url = _.isString(this.url) ? '/api' + this.url : '/api' + this.url(options.type); + options.data = _.extend({}, options.data); + if (__indexOf.call(_.keys(options.data), "city_id") < 0) { + if ($.cookie('city_filter')) { + _.extend(options.data, { + city_id: $.cookie('city_filter') + }); + } + } else { + delete options.data['city_id']; + } + if (options.type === 'POST' || options.type === 'PUT') { + _.extend(options.data, model.toJSON()); + } + token = $.cookie('token') ? $.cookie('token') : typeof USER !== "undefined" && USER !== null ? USER.get('token') : ""; + _.extend(options.data, { + token: token + }); + if (method === "delete") { + options.contentType = 'application/json'; + options.data = JSON.stringify(options.data); + } + return $.ajax(options); + }; +}).call(this); +}, "web-lib/uber_view": function(exports, require, module) {(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + exports.UberView = (function() { + __extends(UberView, Backbone.View); + function UberView() { + this.processDocumentUpload = __bind(this.processDocumentUpload, this); + UberView.__super__.constructor.apply(this, arguments); + } + UberView.prototype.className = 'view_container'; + UberView.prototype.hashId = function() { + return parseInt(location.hash.split('/')[2]); + }; + UberView.prototype.place = function(content) { + var $target; + $target = this.options.scope ? this.options.scope.find(this.options.selector) : $(this.options.selector); + $target[this.options.method || 'html'](content || this.el); + this.delegateEvents(); + $('#spinner').hide(); + return this; + }; + UberView.prototype.mixin = function(m, args) { + var events, self; + if (args == null) { + args = {}; + } + self = this; + events = m._events; + _.extend(this, m); + if (m.initialize) { + m.initialize(self, args); + } + return _.each(_.keys(events), function(key) { + var event, func, selector, split; + split = key.split(' '); + event = split[0]; + selector = split[1]; + func = events[key]; + return $(self.el).find(selector).live(event, function(e) { + return self[func](e); + }); + }); + }; + UberView.prototype.datePickers = function(format) { + if (format == null) { + format = "%Z-%m-%dT%H:%i:%s%:"; + } + $('.datepicker').AnyTime_noPicker(); + return $('.datepicker').AnyTime_picker({ + 'format': format, + 'formatUtcOffset': '%@' + }); + }; + UberView.prototype.dataTable = function(collection, selector, options, params, cols) { + var defaults; + if (selector == null) { + selector = 'table'; + } + if (options == null) { + options = {}; + } + if (params == null) { + params = {}; + } + if (cols == null) { + cols = []; + } + $(selector).empty(); + if (!cols.length) { + cols = collection.defaultColumns; + } + defaults = { + aoColumns: collection.tableColumns(cols), + bDestroy: true, + bSort: false, + bProcessing: true, + bFilter: false, + bServerSide: true, + bPaginate: true, + bScrollInfinite: true, + bScrollCollapse: true, + sScrollY: '600px', + iDisplayLength: 50, + fnServerData: function(source, data, callback) { + var defaultParams; + defaultParams = { + limit: data[4].value, + offset: data[3].value + }; + return collection.fetch({ + data: _.extend(defaultParams, params), + success: function() { + return callback({ + aaData: collection.toTableRows(cols), + iTotalRecords: collection.meta.count, + iTotalDisplayRecords: collection.meta.count + }); + }, + error: function() { + return new Error({ + message: 'Loading error.' + }); + } + }); + }, + fnRowCallback: function(nRow, aData, iDisplayIndex, iDisplayIndexFull) { + $('[data-tooltip]', nRow).qtip({ + content: { + attr: 'data-tooltip' + }, + style: { + classes: "ui-tooltip-light ui-tooltip-rounded ui-tooltip-shadow" + } + }); + return nRow; + } + }; + return $(this.el).find(selector).dataTable(_.extend(defaults, options)); + }; + UberView.prototype.dataTableLocal = function(collection, selector, options, params, cols) { + var $dataTable, defaults; + if (selector == null) { + selector = 'table'; + } + if (options == null) { + options = {}; + } + if (params == null) { + params = {}; + } + if (cols == null) { + cols = []; + } + $(selector).empty(); + if (!cols.length || cols.length === 0) { + cols = collection.defaultColumns; + } + defaults = { + aaData: collection.toTableRows(cols), + aoColumns: collection.tableColumns(cols), + bDestroy: true, + bSort: false, + bProcessing: true, + bFilter: false, + bScrollInfinite: true, + bScrollCollapse: true, + sScrollY: '600px', + iDisplayLength: -1 + }; + $dataTable = $(this.el).find(selector).dataTable(_.extend(defaults, options)); + _.delay(__bind(function() { + if ($dataTable && $dataTable.length > 0) { + return $dataTable.fnAdjustColumnSizing(); + } + }, this), 1); + return $dataTable; + }; + UberView.prototype.reverseGeocode = function() { + var $el; + return ''; + $el = $(this.el); + return this.requireMaps(function() { + var geocoder; + geocoder = new google.maps.Geocoder(); + return $el.find('[data-point]').each(function() { + var $this, latLng, point; + $this = $(this); + point = JSON.parse($this.attr('data-point')); + latLng = new google.maps.LatLng(point.latitude, point.longitude); + return geocoder.geocode({ + latLng: latLng + }, function(data, status) { + if (status === google.maps.GeocoderStatus.OK) { + return $this.text(data[0].formatted_address); + } + }); + }); + }); + }; + UberView.prototype.userIdsToLinkedNames = function() { + var $el; + $el = $(this.el); + return $el.find('a[data-user-id][data-user-type]').each(function() { + var $this, user, userType; + $this = $(this); + userType = $this.attr('data-user-type') === 'user' ? 'client' : $this.attr('data-user-type'); + user = new app.models[userType]({ + id: $this.attr('data-user-id') + }); + return user.fetch({ + success: function(user) { + return $this.html(app.helpers.linkedName(user)).attr('href', "!/" + user.role + "s/" + user.id); + }, + error: function() { + if ($this.attr('data-user-type') === 'user') { + user = new app.models['driver']({ + id: $this.attr('data-user-id') + }); + return user.fetch({ + success: function(user) { + return $this.html(app.helpers.linkedName(user)).attr('href', "!/driver/" + user.id); + } + }); + } + } + }); + }); + }; + UberView.prototype.selectedCity = function() { + var $selected, city, cityFilter; + cityFilter = $.cookie('city_filter'); + $selected = $("#city_filter option[value=" + cityFilter + "]"); + if (city_filter && $selected.length) { + return city = { + lat: parseFloat($selected.attr('data-lat')), + lng: parseFloat($selected.attr('data-lng')), + timezone: $selected.attr('data-timezone') + }; + } else { + return city = { + lat: 37.775, + lng: -122.45, + timezone: 'Etc/UTC' + }; + } + }; + UberView.prototype.updateModel = function(e, success) { + var $el, attrs, model, self; + e.preventDefault(); + $el = $(e.currentTarget); + self = this; + model = new this.model.__proto__.constructor({ + id: this.model.id + }); + attrs = {}; + $el.find('[name]').each(function() { + var $this; + $this = $(this); + return attrs["" + ($this.attr('name'))] = $this.val(); + }); + self.model.set(attrs); + $el.find('span.error').text(''); + return model.save(attrs, { + complete: function(xhr) { + var response; + response = JSON.parse(xhr.responseText); + switch (xhr.status) { + case 200: + self.model = model; + $el.find('[name]').val(''); + if (success) { + return success(); + } + break; + case 406: + return _.each(response, function(error, field) { + return $el.find("[name=" + field + "]").parent().find('span.error').text(error); + }); + default: + return this.unanticipatedError(response); + } + } + }); + }; + UberView.prototype.autoUpdateModel = function(e) { + var $el, arg, model, self, val; + $el = $(e.currentTarget); + val = $el.val(); + self = this; + if (val !== this.model.get($el.attr('id'))) { + arg = {}; + arg[$el.attr('id')] = $el.is(':checkbox') ? $el.is(':checked') ? 1 : 0 : val; + $('.editable span').empty(); + this.model.set(arg); + model = new this.model.__proto__.constructor({ + id: this.model.id + }); + return model.save(arg, { + complete: function(xhr) { + var key, response, _i, _len, _ref, _results; + response = JSON.parse(xhr.responseText); + switch (xhr.status) { + case 200: + self.flash('success', 'Saved!'); + return $el.blur(); + case 406: + self.flash('error', 'That was not Uber.'); + _ref = _.keys(response); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + _results.push($el.parent().find('span').html(response[key])); + } + return _results; + break; + default: + return self.unanticipatedError; + } + } + }); + } + }; + UberView.prototype.unanticipatedError = function(response) { + return self.flash('error', response); + }; + UberView.prototype.flash = function(type, text) { + var $banner; + $banner = $("." + type); + $banner.find('p').text(text).end().css('border', '1px solid #999').animate({ + top: 0 + }, 500); + return setTimeout(function() { + return $banner.animate({ + top: -$banner.outerHeight() + }, 500); + }, 3000); + }; + UberView.prototype.requireMaps = function(callback) { + if (typeof google !== 'undefined' && google.maps) { + return callback(); + } else { + return $.getScript("https://www.google.com/jsapi?key=" + CONFIG.googleJsApiKey, function() { + return google.load('maps', 3, { + callback: callback, + other_params: 'sensor=false&language=en' + }); + }); + } + }; + UberView.prototype.select_drop_down = function(model, key) { + var value; + value = model.get(key); + if (value) { + return $("select[id='" + key + "'] option[value='" + value + "']").attr('selected', 'selected'); + } + }; + UberView.prototype.processDocumentUpload = function(e) { + var $fi, $form, arbData, curDate, data, expDate, expM, expY, expiration, fileElementId, invalid; + e.preventDefault(); + $form = $(e.currentTarget); + $fi = $("input[type=file]", $form); + $(".validationError").removeClass("validationError"); + if (!$fi.val()) { + return $fi.addClass("validationError"); + } else { + fileElementId = $fi.attr('id'); + expY = $("select[name=expiration-year]", $form).val(); + expM = $("select[name=expiration-month]", $form).val(); + invalid = false; + if (expY && expM) { + expDate = new Date(expY, expM, 28); + curDate = new Date(); + if (expDate < curDate) { + invalid = true; + $(".expiration", $form).addClass("validationError"); + } + expiration = "" + expY + "-" + expM + "-28T23:59:59Z"; + } + arbData = {}; + $(".arbitraryField", $form).each(__bind(function(i, e) { + arbData[$(e).attr('name')] = $(e).val(); + if ($(e).val() === "") { + invalid = true; + return $(e).addClass("validationError"); + } + }, this)); + if (!invalid) { + data = { + token: $.cookie('token') || USER.get('token'), + name: $("input[name=fileName]", $form).val(), + meta: escape(JSON.stringify(arbData)), + user_id: $("input[name=driver_id]", $form).val(), + vehicle_id: $("input[name=vehicle_id]", $form).val() + }; + if (expiration) { + data['expiration'] = expiration; + } + $("#spinner").show(); + return $.ajaxFileUpload({ + url: '/api/documents', + secureuri: false, + fileElementId: fileElementId, + data: data, + complete: __bind(function(resp, status) { + var key, _i, _len, _ref, _results; + $("#spinner").hide(); + if (status === "success") { + if (this.model) { + this.model.refetch(); + } else { + USER.refetch(); + } + } + if (status === "error") { + _ref = _.keys(resp); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + _results.push($("*[name=" + key + "]", $form).addClass("validationError")); + } + return _results; + } + }, this) + }); + } + } + }; + return UberView; + })(); +}).call(this); +}, "web-lib/views/footer": function(exports, require, module) {(function() { + var footerTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + footerTemplate = require('web-lib/templates/footer'); + exports.SharedFooterView = (function() { + __extends(SharedFooterView, Backbone.View); + function SharedFooterView() { + SharedFooterView.__super__.constructor.apply(this, arguments); + } + SharedFooterView.prototype.id = 'footer_view'; + SharedFooterView.prototype.events = { + 'click .language': 'intl_set_cookie_locale' + }; + SharedFooterView.prototype.render = function() { + $(this.el).html(footerTemplate()); + this.delegateEvents(); + return this; + }; + SharedFooterView.prototype.intl_set_cookie_locale = function(e) { + var _ref; + i18n.setLocale(e != null ? (_ref = e.srcElement) != null ? _ref.id : void 0 : void 0); + return location.reload(); + }; + return SharedFooterView; + })(); +}).call(this); +}}); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/embed-tokens.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/embed-tokens.js new file mode 100755 index 0000000..61307ee --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/embed-tokens.js @@ -0,0 +1,15 @@ +#! /usr/bin/env node + +global.sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util"); +var fs = require("fs"); +var uglify = require("uglify-js"), // symlink ~/.node_libraries/uglify-js.js to ../uglify-js.js + jsp = uglify.parser, + pro = uglify.uglify; + +var code = fs.readFileSync("embed-tokens.js", "utf8").replace(/^#.*$/mg, ""); +var ast = jsp.parse(code, null, true); + +// trololo +function fooBar() {} + +console.log(sys.inspect(ast, null, null)); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/goto.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/goto.js new file mode 100644 index 0000000..945960c --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/goto.js @@ -0,0 +1,26 @@ +function unique(arqw) { + var a = [], i, j + outer: for (i = 0; i < arqw.length; i++) { + for (j = 0; j < a.length; j++) { + if (a[j] == arqw[i]) { + continue outer + } + } + a[a.length] = arqw[i] + } + return a +} + + +function unique(arqw) { + var crap = [], i, j + outer: for (i = 0; i < arqw.length; i++) { + for (j = 0; j < crap.length; j++) { + if (crap[j] == arqw[i]) { + continue outer + } + } + crap[crap.length] = arqw[i] + } + return crap +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/goto2.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/goto2.js new file mode 100644 index 0000000..d13b2bc --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/goto2.js @@ -0,0 +1,8 @@ +function q(qooo) { + var a; + foo: for(;;) { + a++; + if (something) break foo; + return qooo; + } +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/hoist.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/hoist.js new file mode 100644 index 0000000..4bf2b94 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/hoist.js @@ -0,0 +1,33 @@ +function foo(arg1, arg2, arg3, arg4, arg5, arg6) { + var a = 5; + { + var d = 10, mak = 20, buz = 30; + var q = buz * 2; + } + if (moo) { + var a, b, c; + } + for (var arg1 = 0, d = 20; arg1 < 10; ++arg1) + console.log(arg3); + for (var i in mak) {} + for (j in d) {} + var d; + + function test() { + + }; + + //test(); + + (function moo(first, second){ + console.log(first); + })(1); + + (function moo(first, second){ + console.log(moo()); + })(1); +} + + +var foo; +var bar; diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/instrument.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/instrument.js new file mode 100644 index 0000000..c6a9d79 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/instrument.js @@ -0,0 +1,97 @@ +// sample on how to use the parser and walker API to instrument some code + +var jsp = require("uglify-js").parser; +var pro = require("uglify-js").uglify; + +function instrument(code) { + var ast = jsp.parse(code, false, true); // true for the third arg specifies that we want + // to have start/end tokens embedded in the + // statements + var w = pro.ast_walker(); + + // we're gonna need this to push elements that we're currently looking at, to avoid + // endless recursion. + var analyzing = []; + function do_stat() { + var ret; + if (this[0].start && analyzing.indexOf(this) < 0) { + // without the `analyzing' hack, w.walk(this) would re-enter here leading + // to infinite recursion + analyzing.push(this); + ret = [ "splice", // XXX: "block" is safer + [ [ "stat", + [ "call", [ "name", "trace" ], + [ [ "string", this[0].toString() ], + [ "num", this[0].start.line ], + [ "num", this[0].start.col ], + [ "num", this[0].end.line ], + [ "num", this[0].end.col ]]]], + w.walk(this) ]]; + analyzing.pop(this); + } + return ret; + }; + var new_ast = w.with_walkers({ + "stat" : do_stat, + "label" : do_stat, + "break" : do_stat, + "continue" : do_stat, + "debugger" : do_stat, + "var" : do_stat, + "const" : do_stat, + "return" : do_stat, + "throw" : do_stat, + "try" : do_stat, + "defun" : do_stat, + "if" : do_stat, + "while" : do_stat, + "do" : do_stat, + "for" : do_stat, + "for-in" : do_stat, + "switch" : do_stat, + "with" : do_stat + }, function(){ + return w.walk(ast); + }); + return pro.gen_code(new_ast, { beautify: true }); +} + + + + +////// test code follows. + +var code = instrument(test.toString()); +console.log(code); + +function test() { + // simple stats + a = 5; + c += a + b; + "foo"; + + // var + var foo = 5; + const bar = 6, baz = 7; + + // switch block. note we can't track case lines the same way. + switch ("foo") { + case "foo": + return 1; + case "bar": + return 2; + } + + // for/for in + for (var i = 0; i < 5; ++i) { + console.log("Hello " + i); + } + for (var i in [ 1, 2, 3]) { + console.log(i); + } + + // note however that the following is broken. I guess we + // should add the block brackets in this case... + for (var i = 0; i < 5; ++i) + console.log("foo"); +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/instrument2.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/instrument2.js new file mode 100644 index 0000000..6aee5f3 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/instrument2.js @@ -0,0 +1,138 @@ +// sample on how to use the parser and walker API to instrument some code + +var jsp = require("uglify-js").parser; +var pro = require("uglify-js").uglify; + +function instrument(code) { + var ast = jsp.parse(code, false, true); // true for the third arg specifies that we want + // to have start/end tokens embedded in the + // statements + var w = pro.ast_walker(); + + function trace (line, comment) { + var code = pro.gen_code(line, { beautify: true }); + var data = line[0] + + var args = [] + if (!comment) comment = "" + if (typeof data === "object") { + code = code.split(/\n/).shift() + args = [ [ "string", data.toString() ], + [ "string", code ], + [ "num", data.start.line ], + [ "num", data.start.col ], + [ "num", data.end.line ], + [ "num", data.end.col ]] + } else { + args = [ [ "string", data ], + [ "string", code ]] + + } + return [ "call", [ "name", "trace" ], args ]; + } + + // we're gonna need this to push elements that we're currently looking at, to avoid + // endless recursion. + var analyzing = []; + function do_stat() { + var ret; + if (this[0].start && analyzing.indexOf(this) < 0) { + // without the `analyzing' hack, w.walk(this) would re-enter here leading + // to infinite recursion + analyzing.push(this); + ret = [ "splice", + [ [ "stat", trace(this) ], + w.walk(this) ]]; + analyzing.pop(this); + } + return ret; + } + + function do_cond(c, t, f) { + return [ this[0], w.walk(c), + ["seq", trace(t), w.walk(t) ], + ["seq", trace(f), w.walk(f) ]]; + } + + function do_binary(c, l, r) { + if (c !== "&&" && c !== "||") { + return [this[0], c, w.walk(l), w.walk(r)]; + } + return [ this[0], c, + ["seq", trace(l), w.walk(l) ], + ["seq", trace(r), w.walk(r) ]]; + } + + var new_ast = w.with_walkers({ + "stat" : do_stat, + "label" : do_stat, + "break" : do_stat, + "continue" : do_stat, + "debugger" : do_stat, + "var" : do_stat, + "const" : do_stat, + "return" : do_stat, + "throw" : do_stat, + "try" : do_stat, + "defun" : do_stat, + "if" : do_stat, + "while" : do_stat, + "do" : do_stat, + "for" : do_stat, + "for-in" : do_stat, + "switch" : do_stat, + "with" : do_stat, + "conditional" : do_cond, + "binary" : do_binary + }, function(){ + return w.walk(ast); + }); + return pro.gen_code(new_ast, { beautify: true }); +} + + +////// test code follows. + +var code = instrument(test.toString()); +console.log(code); + +function test() { + // simple stats + a = 5; + c += a + b; + "foo"; + + // var + var foo = 5; + const bar = 6, baz = 7; + + // switch block. note we can't track case lines the same way. + switch ("foo") { + case "foo": + return 1; + case "bar": + return 2; + } + + // for/for in + for (var i = 0; i < 5; ++i) { + console.log("Hello " + i); + } + for (var i in [ 1, 2, 3]) { + console.log(i); + } + + for (var i = 0; i < 5; ++i) + console.log("foo"); + + for (var i = 0; i < 5; ++i) { + console.log("foo"); + } + + var k = plurp() ? 1 : 0; + var x = a ? doX(y) && goZoo("zoo") + : b ? blerg({ x: y }) + : null; + + var x = X || Y; +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/liftvars.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/liftvars.js new file mode 100644 index 0000000..2f4b7fe --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/liftvars.js @@ -0,0 +1,8 @@ +var UNUSED_VAR1 = 19; + +function main() { + var unused_var2 = 20; + alert(100); +} + +main(); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/test.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/test.js new file mode 100755 index 0000000..f295fba --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/test.js @@ -0,0 +1,30 @@ +#! /usr/bin/env node + +global.sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util"); +var fs = require("fs"); +var uglify = require("uglify-js"), // symlink ~/.node_libraries/uglify-js.js to ../uglify-js.js + jsp = uglify.parser, + pro = uglify.uglify; + +var code = fs.readFileSync("hoist.js", "utf8"); +var ast = jsp.parse(code); + +ast = pro.ast_lift_variables(ast); + +var w = pro.ast_walker(); +ast = w.with_walkers({ + "function": function() { + var node = w.dive(this); // walk depth first + console.log(pro.gen_code(node, { beautify: true })); + return node; + }, + "name": function(name) { + return [ this[0], "X" ]; + } +}, function(){ + return w.walk(ast); +}); + +console.log(pro.gen_code(ast, { + beautify: true +})); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/uglify-hangs.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/uglify-hangs.js new file mode 100644 index 0000000..0d5b7e0 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/uglify-hangs.js @@ -0,0 +1,3930 @@ +/** + * @fileoverview + * + * JsWorld + * + *

Javascript library for localised formatting and parsing of: + *

    + *
  • Numbers + *
  • Dates and times + *
  • Currency + *
+ * + *

The library classes are configured with standard POSIX locale definitions + * derived from Unicode's Common Locale Data Repository (CLDR). + * + *

Website: JsWorld + * + * @author Vladimir Dzhuvinov + * @version 2.5 (2011-12-23) + */ + + + +/** + * @namespace Namespace container for the JsWorld library objects. + */ +jsworld = {}; + + +/** + * @function + * + * @description Formats a JavaScript Date object as an ISO-8601 date/time + * string. + * + * @param {Date} [d] A valid JavaScript Date object. If undefined the + * current date/time will be used. + * @param {Boolean} [withTZ] Include timezone offset, default false. + * + * @returns {String} The date/time formatted as YYYY-MM-DD HH:MM:SS. + */ +jsworld.formatIsoDateTime = function(d, withTZ) { + + if (typeof d === "undefined") + d = new Date(); // now + + if (typeof withTZ === "undefined") + withTZ = false; + + var s = jsworld.formatIsoDate(d) + " " + jsworld.formatIsoTime(d); + + if (withTZ) { + + var diff = d.getHours() - d.getUTCHours(); + var hourDiff = Math.abs(diff); + + var minuteUTC = d.getUTCMinutes(); + var minute = d.getMinutes(); + + if (minute != minuteUTC && minuteUTC < 30 && diff < 0) + hourDiff--; + + if (minute != minuteUTC && minuteUTC > 30 && diff > 0) + hourDiff--; + + var minuteDiff; + if (minute != minuteUTC) + minuteDiff = ":30"; + else + minuteDiff = ":00"; + + var timezone; + if (hourDiff < 10) + timezone = "0" + hourDiff + minuteDiff; + + else + timezone = "" + hourDiff + minuteDiff; + + if (diff < 0) + timezone = "-" + timezone; + + else + timezone = "+" + timezone; + + s = s + timezone; + } + + return s; +}; + + +/** + * @function + * + * @description Formats a JavaScript Date object as an ISO-8601 date string. + * + * @param {Date} [d] A valid JavaScript Date object. If undefined the current + * date will be used. + * + * @returns {String} The date formatted as YYYY-MM-DD. + */ +jsworld.formatIsoDate = function(d) { + + if (typeof d === "undefined") + d = new Date(); // now + + var year = d.getFullYear(); + var month = d.getMonth() + 1; + var day = d.getDate(); + + return year + "-" + jsworld._zeroPad(month, 2) + "-" + jsworld._zeroPad(day, 2); +}; + + +/** + * @function + * + * @description Formats a JavaScript Date object as an ISO-8601 time string. + * + * @param {Date} [d] A valid JavaScript Date object. If undefined the current + * time will be used. + * + * @returns {String} The time formatted as HH:MM:SS. + */ +jsworld.formatIsoTime = function(d) { + + if (typeof d === "undefined") + d = new Date(); // now + + var hour = d.getHours(); + var minute = d.getMinutes(); + var second = d.getSeconds(); + + return jsworld._zeroPad(hour, 2) + ":" + jsworld._zeroPad(minute, 2) + ":" + jsworld._zeroPad(second, 2); +}; + + +/** + * @function + * + * @description Parses an ISO-8601 formatted date/time string to a JavaScript + * Date object. + * + * @param {String} isoDateTimeVal An ISO-8601 formatted date/time string. + * + *

Accepted formats: + * + *

    + *
  • YYYY-MM-DD HH:MM:SS + *
  • YYYYMMDD HHMMSS + *
  • YYYY-MM-DD HHMMSS + *
  • YYYYMMDD HH:MM:SS + *
+ * + * @returns {Date} The corresponding Date object. + * + * @throws Error on a badly formatted date/time string or on a invalid date. + */ +jsworld.parseIsoDateTime = function(isoDateTimeVal) { + + if (typeof isoDateTimeVal != "string") + throw "Error: The parameter must be a string"; + + // First, try to match "YYYY-MM-DD HH:MM:SS" format + var matches = isoDateTimeVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)/); + + // If unsuccessful, try to match "YYYYMMDD HHMMSS" format + if (matches === null) + matches = isoDateTimeVal.match(/^(\d\d\d\d)(\d\d)(\d\d)[T ](\d\d)(\d\d)(\d\d)/); + + // ... try to match "YYYY-MM-DD HHMMSS" format + if (matches === null) + matches = isoDateTimeVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d)(\d\d)(\d\d)/); + + // ... try to match "YYYYMMDD HH:MM:SS" format + if (matches === null) + matches = isoDateTimeVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)/); + + // Report bad date/time string + if (matches === null) + throw "Error: Invalid ISO-8601 date/time string"; + + // Force base 10 parse int as some values may have leading zeros! + // (to avoid implicit octal base conversion) + var year = parseInt(matches[1], 10); + var month = parseInt(matches[2], 10); + var day = parseInt(matches[3], 10); + + var hour = parseInt(matches[4], 10); + var mins = parseInt(matches[5], 10); + var secs = parseInt(matches[6], 10); + + // Simple value range check, leap years not checked + // Note: the originial ISO time spec for leap hours (24:00:00) and seconds (00:00:60) is not supported + if (month < 1 || month > 12 || + day < 1 || day > 31 || + hour < 0 || hour > 23 || + mins < 0 || mins > 59 || + secs < 0 || secs > 59 ) + + throw "Error: Invalid ISO-8601 date/time value"; + + var d = new Date(year, month - 1, day, hour, mins, secs); + + // Check if the input date was valid + // (JS Date does automatic forward correction) + if (d.getDate() != day || d.getMonth() +1 != month) + throw "Error: Invalid date"; + + return d; +}; + + +/** + * @function + * + * @description Parses an ISO-8601 formatted date string to a JavaScript + * Date object. + * + * @param {String} isoDateVal An ISO-8601 formatted date string. + * + *

Accepted formats: + * + *

    + *
  • YYYY-MM-DD + *
  • YYYYMMDD + *
+ * + * @returns {Date} The corresponding Date object. + * + * @throws Error on a badly formatted date string or on a invalid date. + */ +jsworld.parseIsoDate = function(isoDateVal) { + + if (typeof isoDateVal != "string") + throw "Error: The parameter must be a string"; + + // First, try to match "YYYY-MM-DD" format + var matches = isoDateVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)/); + + // If unsuccessful, try to match "YYYYMMDD" format + if (matches === null) + matches = isoDateVal.match(/^(\d\d\d\d)(\d\d)(\d\d)/); + + // Report bad date/time string + if (matches === null) + throw "Error: Invalid ISO-8601 date string"; + + // Force base 10 parse int as some values may have leading zeros! + // (to avoid implicit octal base conversion) + var year = parseInt(matches[1], 10); + var month = parseInt(matches[2], 10); + var day = parseInt(matches[3], 10); + + // Simple value range check, leap years not checked + if (month < 1 || month > 12 || + day < 1 || day > 31 ) + + throw "Error: Invalid ISO-8601 date value"; + + var d = new Date(year, month - 1, day); + + // Check if the input date was valid + // (JS Date does automatic forward correction) + if (d.getDate() != day || d.getMonth() +1 != month) + throw "Error: Invalid date"; + + return d; +}; + + +/** + * @function + * + * @description Parses an ISO-8601 formatted time string to a JavaScript + * Date object. + * + * @param {String} isoTimeVal An ISO-8601 formatted time string. + * + *

Accepted formats: + * + *

    + *
  • HH:MM:SS + *
  • HHMMSS + *
+ * + * @returns {Date} The corresponding Date object, with year, month and day set + * to zero. + * + * @throws Error on a badly formatted time string. + */ +jsworld.parseIsoTime = function(isoTimeVal) { + + if (typeof isoTimeVal != "string") + throw "Error: The parameter must be a string"; + + // First, try to match "HH:MM:SS" format + var matches = isoTimeVal.match(/^(\d\d):(\d\d):(\d\d)/); + + // If unsuccessful, try to match "HHMMSS" format + if (matches === null) + matches = isoTimeVal.match(/^(\d\d)(\d\d)(\d\d)/); + + // Report bad date/time string + if (matches === null) + throw "Error: Invalid ISO-8601 date/time string"; + + // Force base 10 parse int as some values may have leading zeros! + // (to avoid implicit octal base conversion) + var hour = parseInt(matches[1], 10); + var mins = parseInt(matches[2], 10); + var secs = parseInt(matches[3], 10); + + // Simple value range check, leap years not checked + if (hour < 0 || hour > 23 || + mins < 0 || mins > 59 || + secs < 0 || secs > 59 ) + + throw "Error: Invalid ISO-8601 time value"; + + return new Date(0, 0, 0, hour, mins, secs); +}; + + +/** + * @private + * + * @description Trims leading and trailing whitespace from a string. + * + *

Used non-regexp the method from http://blog.stevenlevithan.com/archives/faster-trim-javascript + * + * @param {String} str The string to trim. + * + * @returns {String} The trimmed string. + */ +jsworld._trim = function(str) { + + var whitespace = ' \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000'; + + for (var i = 0; i < str.length; i++) { + + if (whitespace.indexOf(str.charAt(i)) === -1) { + str = str.substring(i); + break; + } + } + + for (i = str.length - 1; i >= 0; i--) { + if (whitespace.indexOf(str.charAt(i)) === -1) { + str = str.substring(0, i + 1); + break; + } + } + + return whitespace.indexOf(str.charAt(0)) === -1 ? str : ''; +}; + + + +/** + * @private + * + * @description Returns true if the argument represents a decimal number. + * + * @param {Number|String} arg The argument to test. + * + * @returns {Boolean} true if the argument represents a decimal number, + * otherwise false. + */ +jsworld._isNumber = function(arg) { + + if (typeof arg == "number") + return true; + + if (typeof arg != "string") + return false; + + // ensure string + var s = arg + ""; + + return (/^-?(\d+|\d*\.\d+)$/).test(s); +}; + + +/** + * @private + * + * @description Returns true if the argument represents a decimal integer. + * + * @param {Number|String} arg The argument to test. + * + * @returns {Boolean} true if the argument represents an integer, otherwise + * false. + */ +jsworld._isInteger = function(arg) { + + if (typeof arg != "number" && typeof arg != "string") + return false; + + // convert to string + var s = arg + ""; + + return (/^-?\d+$/).test(s); +}; + + +/** + * @private + * + * @description Returns true if the argument represents a decimal float. + * + * @param {Number|String} arg The argument to test. + * + * @returns {Boolean} true if the argument represents a float, otherwise false. + */ +jsworld._isFloat = function(arg) { + + if (typeof arg != "number" && typeof arg != "string") + return false; + + // convert to string + var s = arg + ""; + + return (/^-?\.\d+?$/).test(s); +}; + + +/** + * @private + * + * @description Checks if the specified formatting option is contained + * within the options string. + * + * @param {String} option The option to search for. + * @param {String} optionsString The options string. + * + * @returns {Boolean} true if the flag is found, else false + */ +jsworld._hasOption = function(option, optionsString) { + + if (typeof option != "string" || typeof optionsString != "string") + return false; + + if (optionsString.indexOf(option) != -1) + return true; + else + return false; +}; + + +/** + * @private + * + * @description String replacement function. + * + * @param {String} s The string to work on. + * @param {String} target The string to search for. + * @param {String} replacement The replacement. + * + * @returns {String} The new string. + */ +jsworld._stringReplaceAll = function(s, target, replacement) { + + var out; + + if (target.length == 1 && replacement.length == 1) { + // simple char/char case somewhat faster + out = ""; + + for (var i = 0; i < s.length; i++) { + + if (s.charAt(i) == target.charAt(0)) + out = out + replacement.charAt(0); + else + out = out + s.charAt(i); + } + + return out; + } + else { + // longer target and replacement strings + out = s; + + var index = out.indexOf(target); + + while (index != -1) { + + out = out.replace(target, replacement); + + index = out.indexOf(target); + } + + return out; + } +}; + + +/** + * @private + * + * @description Tests if a string starts with the specified substring. + * + * @param {String} testedString The string to test. + * @param {String} sub The string to match. + * + * @returns {Boolean} true if the test succeeds. + */ +jsworld._stringStartsWith = function (testedString, sub) { + + if (testedString.length < sub.length) + return false; + + for (var i = 0; i < sub.length; i++) { + if (testedString.charAt(i) != sub.charAt(i)) + return false; + } + + return true; +}; + + +/** + * @private + * + * @description Gets the requested precision from an options string. + * + *

Example: ".3" returns 3 decimal places precision. + * + * @param {String} optionsString The options string. + * + * @returns {integer Number} The requested precision, -1 if not specified. + */ +jsworld._getPrecision = function (optionsString) { + + if (typeof optionsString != "string") + return -1; + + var m = optionsString.match(/\.(\d)/); + if (m) + return parseInt(m[1], 10); + else + return -1; +}; + + +/** + * @private + * + * @description Takes a decimal numeric amount (optionally as string) and + * returns its integer and fractional parts packed into an object. + * + * @param {Number|String} amount The amount, e.g. "123.45" or "-56.78" + * + * @returns {object} Parsed amount object with properties: + * {String} integer : the integer part + * {String} fraction : the fraction part + */ +jsworld._splitNumber = function (amount) { + + if (typeof amount == "number") + amount = amount + ""; + + var obj = {}; + + // remove negative sign + if (amount.charAt(0) == "-") + amount = amount.substring(1); + + // split amount into integer and decimal parts + var amountParts = amount.split("."); + if (!amountParts[1]) + amountParts[1] = ""; // we need "" instead of null + + obj.integer = amountParts[0]; + obj.fraction = amountParts[1]; + + return obj; +}; + + +/** + * @private + * + * @description Formats the integer part using the specified grouping + * and thousands separator. + * + * @param {String} intPart The integer part of the amount, as string. + * @param {String} grouping The grouping definition. + * @param {String} thousandsSep The thousands separator. + * + * @returns {String} The formatted integer part. + */ +jsworld._formatIntegerPart = function (intPart, grouping, thousandsSep) { + + // empty separator string? no grouping? + // -> return immediately with no formatting! + if (thousandsSep == "" || grouping == "-1") + return intPart; + + // turn the semicolon-separated string of integers into an array + var groupSizes = grouping.split(";"); + + // the formatted output string + var out = ""; + + // the intPart string position to process next, + // start at string end, e.g. "10000000 0) { + + // get next group size (if any, otherwise keep last) + if (groupSizes.length > 0) + size = parseInt(groupSizes.shift(), 10); + + // int parse error? + if (isNaN(size)) + throw "Error: Invalid grouping"; + + // size is -1? -> no more grouping, so just copy string remainder + if (size == -1) { + out = intPart.substring(0, pos) + out; + break; + } + + pos -= size; // move to next sep. char. position + + // position underrun? -> just copy string remainder + if (pos < 1) { + out = intPart.substring(0, pos + size) + out; + break; + } + + // extract group and apply sep. char. + out = thousandsSep + intPart.substring(pos, pos + size) + out; + } + + return out; +}; + + +/** + * @private + * + * @description Formats the fractional part to the specified decimal + * precision. + * + * @param {String} fracPart The fractional part of the amount + * @param {integer Number} precision The desired decimal precision + * + * @returns {String} The formatted fractional part. + */ +jsworld._formatFractionPart = function (fracPart, precision) { + + // append zeroes up to precision if necessary + for (var i=0; fracPart.length < precision; i++) + fracPart = fracPart + "0"; + + return fracPart; +}; + + +/** + * @private + * + * @desription Converts a number to string and pad it with leading zeroes if the + * string is shorter than length. + * + * @param {integer Number} number The number value subjected to selective padding. + * @param {integer Number} length If the number has fewer digits than this length + * apply padding. + * + * @returns {String} The formatted string. + */ +jsworld._zeroPad = function(number, length) { + + // ensure string + var s = number + ""; + + while (s.length < length) + s = "0" + s; + + return s; +}; + + +/** + * @private + * @description Converts a number to string and pads it with leading spaces if + * the string is shorter than length. + * + * @param {integer Number} number The number value subjected to selective padding. + * @param {integer Number} length If the number has fewer digits than this length + * apply padding. + * + * @returns {String} The formatted string. + */ +jsworld._spacePad = function(number, length) { + + // ensure string + var s = number + ""; + + while (s.length < length) + s = " " + s; + + return s; +}; + + + +/** + * @class + * Represents a POSIX-style locale with its numeric, monetary and date/time + * properties. Also provides a set of locale helper methods. + * + *

The locale properties follow the POSIX standards: + * + *

+ * + * @public + * @constructor + * @description Creates a new locale object (POSIX-style) with the specified + * properties. + * + * @param {object} properties An object containing the raw locale properties: + * + * @param {String} properties.decimal_point + * + * A string containing the symbol that shall be used as the decimal + * delimiter (radix character) in numeric, non-monetary formatted + * quantities. This property cannot be omitted and cannot be set to the + * empty string. + * + * + * @param {String} properties.thousands_sep + * + * A string containing the symbol that shall be used as a separator for + * groups of digits to the left of the decimal delimiter in numeric, + * non-monetary formatted monetary quantities. + * + * + * @param {String} properties.grouping + * + * Defines the size of each group of digits in formatted non-monetary + * quantities. The operand is a sequence of integers separated by + * semicolons. Each integer specifies the number of digits in each group, + * with the initial integer defining the size of the group immediately + * preceding the decimal delimiter, and the following integers defining + * the preceding groups. If the last integer is not -1, then the size of + * the previous group (if any) shall be repeatedly used for the + * remainder of the digits. If the last integer is -1, then no further + * grouping shall be performed. + * + * + * @param {String} properties.int_curr_symbol + * + * The first three letters signify the ISO-4217 currency code, + * the fourth letter is the international symbol separation character + * (normally a space). + * + * + * @param {String} properties.currency_symbol + * + * The local shorthand currency symbol, e.g. "$" for the en_US locale + * + * + * @param {String} properties.mon_decimal_point + * + * The symbol to be used as the decimal delimiter (radix character) + * + * + * @param {String} properties.mon_thousands_sep + * + * The symbol to be used as a separator for groups of digits to the + * left of the decimal delimiter. + * + * + * @param {String} properties.mon_grouping + * + * A string that defines the size of each group of digits. The + * operand is a sequence of integers separated by semicolons (";"). + * Each integer specifies the number of digits in each group, with the + * initial integer defining the size of the group preceding the + * decimal delimiter, and the following integers defining the + * preceding groups. If the last integer is not -1, then the size of + * the previous group (if any) must be repeatedly used for the + * remainder of the digits. If the last integer is -1, then no + * further grouping is to be performed. + * + * + * @param {String} properties.positive_sign + * + * The string to indicate a non-negative monetary amount. + * + * + * @param {String} properties.negative_sign + * + * The string to indicate a negative monetary amount. + * + * + * @param {integer Number} properties.frac_digits + * + * An integer representing the number of fractional digits (those to + * the right of the decimal delimiter) to be written in a formatted + * monetary quantity using currency_symbol. + * + * + * @param {integer Number} properties.int_frac_digits + * + * An integer representing the number of fractional digits (those to + * the right of the decimal delimiter) to be written in a formatted + * monetary quantity using int_curr_symbol. + * + * + * @param {integer Number} properties.p_cs_precedes + * + * An integer set to 1 if the currency_symbol precedes the value for a + * monetary quantity with a non-negative value, and set to 0 if the + * symbol succeeds the value. + * + * + * @param {integer Number} properties.n_cs_precedes + * + * An integer set to 1 if the currency_symbol precedes the value for a + * monetary quantity with a negative value, and set to 0 if the symbol + * succeeds the value. + * + * + * @param {integer Number} properties.p_sep_by_space + * + * Set to a value indicating the separation of the currency_symbol, + * the sign string, and the value for a non-negative formatted monetary + * quantity: + * + *

0 No space separates the currency symbol and value.

+ * + *

1 If the currency symbol and sign string are adjacent, a space + * separates them from the value; otherwise, a space separates + * the currency symbol from the value.

+ * + *

2 If the currency symbol and sign string are adjacent, a space + * separates them; otherwise, a space separates the sign string + * from the value.

+ * + * + * @param {integer Number} properties.n_sep_by_space + * + * Set to a value indicating the separation of the currency_symbol, + * the sign string, and the value for a negative formatted monetary + * quantity. Rules same as for p_sep_by_space. + * + * + * @param {integer Number} properties.p_sign_posn + * + * An integer set to a value indicating the positioning of the + * positive_sign for a monetary quantity with a non-negative value: + * + *

0 Parentheses enclose the quantity and the currency_symbol.

+ * + *

1 The sign string precedes the quantity and the currency_symbol.

+ * + *

2 The sign string succeeds the quantity and the currency_symbol.

+ * + *

3 The sign string precedes the currency_symbol.

+ * + *

4 The sign string succeeds the currency_symbol.

+ * + * + * @param {integer Number} properties.n_sign_posn + * + * An integer set to a value indicating the positioning of the + * negative_sign for a negative formatted monetary quantity. Rules same + * as for p_sign_posn. + * + * + * @param {integer Number} properties.int_p_cs_precedes + * + * An integer set to 1 if the int_curr_symbol precedes the value for a + * monetary quantity with a non-negative value, and set to 0 if the + * symbol succeeds the value. + * + * + * @param {integer Number} properties.int_n_cs_precedes + * + * An integer set to 1 if the int_curr_symbol precedes the value for a + * monetary quantity with a negative value, and set to 0 if the symbol + * succeeds the value. + * + * + * @param {integer Number} properties.int_p_sep_by_space + * + * Set to a value indicating the separation of the int_curr_symbol, + * the sign string, and the value for a non-negative internationally + * formatted monetary quantity. Rules same as for p_sep_by_space. + * + * + * @param {integer Number} properties.int_n_sep_by_space + * + * Set to a value indicating the separation of the int_curr_symbol, + * the sign string, and the value for a negative internationally + * formatted monetary quantity. Rules same as for p_sep_by_space. + * + * + * @param {integer Number} properties.int_p_sign_posn + * + * An integer set to a value indicating the positioning of the + * positive_sign for a positive monetary quantity formatted with the + * international format. Rules same as for p_sign_posn. + * + * + * @param {integer Number} properties.int_n_sign_posn + * + * An integer set to a value indicating the positioning of the + * negative_sign for a negative monetary quantity formatted with the + * international format. Rules same as for p_sign_posn. + * + * + * @param {String[] | String} properties.abday + * + * The abbreviated weekday names, corresponding to the %a conversion + * specification. The property must be either an array of 7 strings or + * a string consisting of 7 semicolon-separated substrings, each + * surrounded by double-quotes. The first must be the abbreviated name + * of the day corresponding to Sunday, the second the abbreviated name + * of the day corresponding to Monday, and so on. + * + * + * @param {String[] | String} properties.day + * + * The full weekday names, corresponding to the %A conversion + * specification. The property must be either an array of 7 strings or + * a string consisting of 7 semicolon-separated substrings, each + * surrounded by double-quotes. The first must be the full name of the + * day corresponding to Sunday, the second the full name of the day + * corresponding to Monday, and so on. + * + * + * @param {String[] | String} properties.abmon + * + * The abbreviated month names, corresponding to the %b conversion + * specification. The property must be either an array of 12 strings or + * a string consisting of 12 semicolon-separated substrings, each + * surrounded by double-quotes. The first must be the abbreviated name + * of the first month of the year (January), the second the abbreviated + * name of the second month, and so on. + * + * + * @param {String[] | String} properties.mon + * + * The full month names, corresponding to the %B conversion + * specification. The property must be either an array of 12 strings or + * a string consisting of 12 semicolon-separated substrings, each + * surrounded by double-quotes. The first must be the full name of the + * first month of the year (January), the second the full name of the second + * month, and so on. + * + * + * @param {String} properties.d_fmt + * + * The appropriate date representation. The string may contain any + * combination of characters and conversion specifications (%). + * + * + * @param {String} properties.t_fmt + * + * The appropriate time representation. The string may contain any + * combination of characters and conversion specifications (%). + * + * + * @param {String} properties.d_t_fmt + * + * The appropriate date and time representation. The string may contain + * any combination of characters and conversion specifications (%). + * + * + * @param {String[] | String} properties.am_pm + * + * The appropriate representation of the ante-meridiem and post-meridiem + * strings, corresponding to the %p conversion specification. The property + * must be either an array of 2 strings or a string consisting of 2 + * semicolon-separated substrings, each surrounded by double-quotes. + * The first string must represent the ante-meridiem designation, the + * last string the post-meridiem designation. + * + * + * @throws @throws Error on a undefined or invalid locale property. + */ +jsworld.Locale = function(properties) { + + + /** + * @private + * + * @description Identifies the class for internal library purposes. + */ + this._className = "jsworld.Locale"; + + + /** + * @private + * + * @description Parses a day or month name definition list, which + * could be a ready JS array, e.g. ["Mon", "Tue", "Wed"...] or + * it could be a string formatted according to the classic POSIX + * definition e.g. "Mon";"Tue";"Wed";... + * + * @param {String[] | String} namesAn array or string defining + * the week/month names. + * @param {integer Number} expectedItems The number of expected list + * items, e.g. 7 for weekdays, 12 for months. + * + * @returns {String[]} The parsed (and checked) items. + * + * @throws Error on missing definition, unexpected item count or + * missing double-quotes. + */ + this._parseList = function(names, expectedItems) { + + var array = []; + + if (names == null) { + throw "Names not defined"; + } + else if (typeof names == "object") { + // we got a ready array + array = names; + } + else if (typeof names == "string") { + // we got the names in the classic POSIX form, do parse + array = names.split(";", expectedItems); + + for (var i = 0; i < array.length; i++) { + // check for and strip double quotes + if (array[i][0] == "\"" && array[i][array[i].length - 1] == "\"") + array[i] = array[i].slice(1, -1); + else + throw "Missing double quotes"; + } + } + else { + throw "Names must be an array or a string"; + } + + if (array.length != expectedItems) + throw "Expected " + expectedItems + " items, got " + array.length; + + return array; + }; + + + /** + * @private + * + * @description Validates a date/time format string, such as "H:%M:%S". + * Checks that the argument is of type "string" and is not empty. + * + * @param {String} formatString The format string. + * + * @returns {String} The validated string. + * + * @throws Error on null or empty string. + */ + this._validateFormatString = function(formatString) { + + if (typeof formatString == "string" && formatString.length > 0) + return formatString; + else + throw "Empty or no string"; + }; + + + // LC_NUMERIC + + if (properties == null || typeof properties != "object") + throw "Error: Invalid/missing locale properties"; + + + if (typeof properties.decimal_point != "string") + throw "Error: Invalid/missing decimal_point property"; + + this.decimal_point = properties.decimal_point; + + + if (typeof properties.thousands_sep != "string") + throw "Error: Invalid/missing thousands_sep property"; + + this.thousands_sep = properties.thousands_sep; + + + if (typeof properties.grouping != "string") + throw "Error: Invalid/missing grouping property"; + + this.grouping = properties.grouping; + + + // LC_MONETARY + + if (typeof properties.int_curr_symbol != "string") + throw "Error: Invalid/missing int_curr_symbol property"; + + if (! /[A-Za-z]{3}.?/.test(properties.int_curr_symbol)) + throw "Error: Invalid int_curr_symbol property"; + + this.int_curr_symbol = properties.int_curr_symbol; + + + if (typeof properties.currency_symbol != "string") + throw "Error: Invalid/missing currency_symbol property"; + + this.currency_symbol = properties.currency_symbol; + + + if (typeof properties.frac_digits != "number" && properties.frac_digits < 0) + throw "Error: Invalid/missing frac_digits property"; + + this.frac_digits = properties.frac_digits; + + + // may be empty string/null for currencies with no fractional part + if (properties.mon_decimal_point === null || properties.mon_decimal_point == "") { + + if (this.frac_digits > 0) + throw "Error: Undefined mon_decimal_point property"; + else + properties.mon_decimal_point = ""; + } + + if (typeof properties.mon_decimal_point != "string") + throw "Error: Invalid/missing mon_decimal_point property"; + + this.mon_decimal_point = properties.mon_decimal_point; + + + if (typeof properties.mon_thousands_sep != "string") + throw "Error: Invalid/missing mon_thousands_sep property"; + + this.mon_thousands_sep = properties.mon_thousands_sep; + + + if (typeof properties.mon_grouping != "string") + throw "Error: Invalid/missing mon_grouping property"; + + this.mon_grouping = properties.mon_grouping; + + + if (typeof properties.positive_sign != "string") + throw "Error: Invalid/missing positive_sign property"; + + this.positive_sign = properties.positive_sign; + + + if (typeof properties.negative_sign != "string") + throw "Error: Invalid/missing negative_sign property"; + + this.negative_sign = properties.negative_sign; + + + + if (properties.p_cs_precedes !== 0 && properties.p_cs_precedes !== 1) + throw "Error: Invalid/missing p_cs_precedes property, must be 0 or 1"; + + this.p_cs_precedes = properties.p_cs_precedes; + + + if (properties.n_cs_precedes !== 0 && properties.n_cs_precedes !== 1) + throw "Error: Invalid/missing n_cs_precedes, must be 0 or 1"; + + this.n_cs_precedes = properties.n_cs_precedes; + + + if (properties.p_sep_by_space !== 0 && + properties.p_sep_by_space !== 1 && + properties.p_sep_by_space !== 2) + throw "Error: Invalid/missing p_sep_by_space property, must be 0, 1 or 2"; + + this.p_sep_by_space = properties.p_sep_by_space; + + + if (properties.n_sep_by_space !== 0 && + properties.n_sep_by_space !== 1 && + properties.n_sep_by_space !== 2) + throw "Error: Invalid/missing n_sep_by_space property, must be 0, 1, or 2"; + + this.n_sep_by_space = properties.n_sep_by_space; + + + if (properties.p_sign_posn !== 0 && + properties.p_sign_posn !== 1 && + properties.p_sign_posn !== 2 && + properties.p_sign_posn !== 3 && + properties.p_sign_posn !== 4) + throw "Error: Invalid/missing p_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.p_sign_posn = properties.p_sign_posn; + + + if (properties.n_sign_posn !== 0 && + properties.n_sign_posn !== 1 && + properties.n_sign_posn !== 2 && + properties.n_sign_posn !== 3 && + properties.n_sign_posn !== 4) + throw "Error: Invalid/missing n_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.n_sign_posn = properties.n_sign_posn; + + + if (typeof properties.int_frac_digits != "number" && properties.int_frac_digits < 0) + throw "Error: Invalid/missing int_frac_digits property"; + + this.int_frac_digits = properties.int_frac_digits; + + + if (properties.int_p_cs_precedes !== 0 && properties.int_p_cs_precedes !== 1) + throw "Error: Invalid/missing int_p_cs_precedes property, must be 0 or 1"; + + this.int_p_cs_precedes = properties.int_p_cs_precedes; + + + if (properties.int_n_cs_precedes !== 0 && properties.int_n_cs_precedes !== 1) + throw "Error: Invalid/missing int_n_cs_precedes property, must be 0 or 1"; + + this.int_n_cs_precedes = properties.int_n_cs_precedes; + + + if (properties.int_p_sep_by_space !== 0 && + properties.int_p_sep_by_space !== 1 && + properties.int_p_sep_by_space !== 2) + throw "Error: Invalid/missing int_p_sep_by_spacev, must be 0, 1 or 2"; + + this.int_p_sep_by_space = properties.int_p_sep_by_space; + + + if (properties.int_n_sep_by_space !== 0 && + properties.int_n_sep_by_space !== 1 && + properties.int_n_sep_by_space !== 2) + throw "Error: Invalid/missing int_n_sep_by_space property, must be 0, 1, or 2"; + + this.int_n_sep_by_space = properties.int_n_sep_by_space; + + + if (properties.int_p_sign_posn !== 0 && + properties.int_p_sign_posn !== 1 && + properties.int_p_sign_posn !== 2 && + properties.int_p_sign_posn !== 3 && + properties.int_p_sign_posn !== 4) + throw "Error: Invalid/missing int_p_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.int_p_sign_posn = properties.int_p_sign_posn; + + + if (properties.int_n_sign_posn !== 0 && + properties.int_n_sign_posn !== 1 && + properties.int_n_sign_posn !== 2 && + properties.int_n_sign_posn !== 3 && + properties.int_n_sign_posn !== 4) + throw "Error: Invalid/missing int_n_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.int_n_sign_posn = properties.int_n_sign_posn; + + + // LC_TIME + + if (properties == null || typeof properties != "object") + throw "Error: Invalid/missing time locale properties"; + + + // parse the supported POSIX LC_TIME properties + + // abday + try { + this.abday = this._parseList(properties.abday, 7); + } + catch (error) { + throw "Error: Invalid abday property: " + error; + } + + // day + try { + this.day = this._parseList(properties.day, 7); + } + catch (error) { + throw "Error: Invalid day property: " + error; + } + + // abmon + try { + this.abmon = this._parseList(properties.abmon, 12); + } catch (error) { + throw "Error: Invalid abmon property: " + error; + } + + // mon + try { + this.mon = this._parseList(properties.mon, 12); + } catch (error) { + throw "Error: Invalid mon property: " + error; + } + + // d_fmt + try { + this.d_fmt = this._validateFormatString(properties.d_fmt); + } catch (error) { + throw "Error: Invalid d_fmt property: " + error; + } + + // t_fmt + try { + this.t_fmt = this._validateFormatString(properties.t_fmt); + } catch (error) { + throw "Error: Invalid t_fmt property: " + error; + } + + // d_t_fmt + try { + this.d_t_fmt = this._validateFormatString(properties.d_t_fmt); + } catch (error) { + throw "Error: Invalid d_t_fmt property: " + error; + } + + // am_pm + try { + var am_pm_strings = this._parseList(properties.am_pm, 2); + this.am = am_pm_strings[0]; + this.pm = am_pm_strings[1]; + } catch (error) { + // ignore empty/null string errors + this.am = ""; + this.pm = ""; + } + + + /** + * @public + * + * @description Returns the abbreviated name of the specified weekday. + * + * @param {integer Number} [weekdayNum] An integer between 0 and 6. Zero + * corresponds to Sunday, one to Monday, etc. If omitted the + * method will return an array of all abbreviated weekday + * names. + * + * @returns {String | String[]} The abbreviated name of the specified weekday + * or an array of all abbreviated weekday names. + * + * @throws Error on invalid argument. + */ + this.getAbbreviatedWeekdayName = function(weekdayNum) { + + if (typeof weekdayNum == "undefined" || weekdayNum === null) + return this.abday; + + if (! jsworld._isInteger(weekdayNum) || weekdayNum < 0 || weekdayNum > 6) + throw "Error: Invalid weekday argument, must be an integer [0..6]"; + + return this.abday[weekdayNum]; + }; + + + /** + * @public + * + * @description Returns the name of the specified weekday. + * + * @param {integer Number} [weekdayNum] An integer between 0 and 6. Zero + * corresponds to Sunday, one to Monday, etc. If omitted the + * method will return an array of all weekday names. + * + * @returns {String | String[]} The name of the specified weekday or an + * array of all weekday names. + * + * @throws Error on invalid argument. + */ + this.getWeekdayName = function(weekdayNum) { + + if (typeof weekdayNum == "undefined" || weekdayNum === null) + return this.day; + + if (! jsworld._isInteger(weekdayNum) || weekdayNum < 0 || weekdayNum > 6) + throw "Error: Invalid weekday argument, must be an integer [0..6]"; + + return this.day[weekdayNum]; + }; + + + /** + * @public + * + * @description Returns the abbreviated name of the specified month. + * + * @param {integer Number} [monthNum] An integer between 0 and 11. Zero + * corresponds to January, one to February, etc. If omitted the + * method will return an array of all abbreviated month names. + * + * @returns {String | String[]} The abbreviated name of the specified month + * or an array of all abbreviated month names. + * + * @throws Error on invalid argument. + */ + this.getAbbreviatedMonthName = function(monthNum) { + + if (typeof monthNum == "undefined" || monthNum === null) + return this.abmon; + + if (! jsworld._isInteger(monthNum) || monthNum < 0 || monthNum > 11) + throw "Error: Invalid month argument, must be an integer [0..11]"; + + return this.abmon[monthNum]; + }; + + + /** + * @public + * + * @description Returns the name of the specified month. + * + * @param {integer Number} [monthNum] An integer between 0 and 11. Zero + * corresponds to January, one to February, etc. If omitted the + * method will return an array of all month names. + * + * @returns {String | String[]} The name of the specified month or an array + * of all month names. + * + * @throws Error on invalid argument. + */ + this.getMonthName = function(monthNum) { + + if (typeof monthNum == "undefined" || monthNum === null) + return this.mon; + + if (! jsworld._isInteger(monthNum) || monthNum < 0 || monthNum > 11) + throw "Error: Invalid month argument, must be an integer [0..11]"; + + return this.mon[monthNum]; + }; + + + + /** + * @public + * + * @description Gets the decimal delimiter (radix) character for + * numeric quantities. + * + * @returns {String} The radix character. + */ + this.getDecimalPoint = function() { + + return this.decimal_point; + }; + + + /** + * @public + * + * @description Gets the local shorthand currency symbol. + * + * @returns {String} The currency symbol. + */ + this.getCurrencySymbol = function() { + + return this.currency_symbol; + }; + + + /** + * @public + * + * @description Gets the internaltion currency symbol (ISO-4217 code). + * + * @returns {String} The international currency symbol. + */ + this.getIntCurrencySymbol = function() { + + return this.int_curr_symbol.substring(0,3); + }; + + + /** + * @public + * + * @description Gets the position of the local (shorthand) currency + * symbol relative to the amount. Assumes a non-negative amount. + * + * @returns {Boolean} True if the symbol precedes the amount, false if + * the symbol succeeds the amount. + */ + this.currencySymbolPrecedes = function() { + + if (this.p_cs_precedes == 1) + return true; + else + return false; + }; + + + /** + * @public + * + * @description Gets the position of the international (ISO-4217 code) + * currency symbol relative to the amount. Assumes a non-negative + * amount. + * + * @returns {Boolean} True if the symbol precedes the amount, false if + * the symbol succeeds the amount. + */ + this.intCurrencySymbolPrecedes = function() { + + if (this.int_p_cs_precedes == 1) + return true; + else + return false; + + }; + + + /** + * @public + * + * @description Gets the decimal delimiter (radix) for monetary + * quantities. + * + * @returns {String} The radix character. + */ + this.getMonetaryDecimalPoint = function() { + + return this.mon_decimal_point; + }; + + + /** + * @public + * + * @description Gets the number of fractional digits for local + * (shorthand) symbol formatting. + * + * @returns {integer Number} The number of fractional digits. + */ + this.getFractionalDigits = function() { + + return this.frac_digits; + }; + + + /** + * @public + * + * @description Gets the number of fractional digits for + * international (ISO-4217 code) formatting. + * + * @returns {integer Number} The number of fractional digits. + */ + this.getIntFractionalDigits = function() { + + return this.int_frac_digits; + }; +}; + + + +/** + * @class + * Class for localised formatting of numbers. + * + *

See: + * POSIX LC_NUMERIC. + * + * + * @public + * @constructor + * @description Creates a new numeric formatter for the specified locale. + * + * @param {jsworld.Locale} locale A locale object specifying the required + * POSIX LC_NUMERIC formatting properties. + * + * @throws Error on constructor failure. + */ +jsworld.NumericFormatter = function(locale) { + + if (typeof locale != "object" || locale._className != "jsworld.Locale") + throw "Constructor error: You must provide a valid jsworld.Locale instance"; + + this.lc = locale; + + + /** + * @public + * + * @description Formats a decimal numeric value according to the preset + * locale. + * + * @param {Number|String} number The number to format. + * @param {String} [options] Options to modify the formatted output: + *

    + *
  • "^" suppress grouping + *
  • "+" force positive sign for positive amounts + *
  • "~" suppress positive/negative sign + *
  • ".n" specify decimal precision 'n' + *
+ * + * @returns {String} The formatted number. + * + * @throws "Error: Invalid input" on bad input. + */ + this.format = function(number, options) { + + if (typeof number == "string") + number = jsworld._trim(number); + + if (! jsworld._isNumber(number)) + throw "Error: The input is not a number"; + + var floatAmount = parseFloat(number, 10); + + // get the required precision + var reqPrecision = jsworld._getPrecision(options); + + // round to required precision + if (reqPrecision != -1) + floatAmount = Math.round(floatAmount * Math.pow(10, reqPrecision)) / Math.pow(10, reqPrecision); + + + // convert the float number to string and parse into + // object with properties integer and fraction + var parsedAmount = jsworld._splitNumber(String(floatAmount)); + + // format integer part with grouping chars + var formattedIntegerPart; + + if (floatAmount === 0) + formattedIntegerPart = "0"; + else + formattedIntegerPart = jsworld._hasOption("^", options) ? + parsedAmount.integer : + jsworld._formatIntegerPart(parsedAmount.integer, + this.lc.grouping, + this.lc.thousands_sep); + + // format the fractional part + var formattedFractionPart = + reqPrecision != -1 ? + jsworld._formatFractionPart(parsedAmount.fraction, reqPrecision) : + parsedAmount.fraction; + + + // join the integer and fraction parts using the decimal_point property + var formattedAmount = + formattedFractionPart.length ? + formattedIntegerPart + this.lc.decimal_point + formattedFractionPart : + formattedIntegerPart; + + // prepend sign? + if (jsworld._hasOption("~", options) || floatAmount === 0) { + // suppress both '+' and '-' signs, i.e. return abs value + return formattedAmount; + } + else { + if (jsworld._hasOption("+", options) || floatAmount < 0) { + if (floatAmount > 0) + // force '+' sign for positive amounts + return "+" + formattedAmount; + else if (floatAmount < 0) + // prepend '-' sign + return "-" + formattedAmount; + else + // zero case + return formattedAmount; + } + else { + // positive amount with no '+' sign + return formattedAmount; + } + } + }; +}; + + +/** + * @class + * Class for localised formatting of dates and times. + * + *

See: + * POSIX LC_TIME. + * + * @public + * @constructor + * @description Creates a new date/time formatter for the specified locale. + * + * @param {jsworld.Locale} locale A locale object specifying the required + * POSIX LC_TIME formatting properties. + * + * @throws Error on constructor failure. + */ +jsworld.DateTimeFormatter = function(locale) { + + + if (typeof locale != "object" || locale._className != "jsworld.Locale") + throw "Constructor error: You must provide a valid jsworld.Locale instance."; + + this.lc = locale; + + + /** + * @public + * + * @description Formats a date according to the preset locale. + * + * @param {Date|String} date A valid Date object instance or a string + * containing a valid ISO-8601 formatted date, e.g. "2010-31-03" + * or "2010-03-31 23:59:59". + * + * @returns {String} The formatted date + * + * @throws Error on invalid date argument + */ + this.formatDate = function(date) { + + var d = null; + + if (typeof date == "string") { + // assume ISO-8601 date string + try { + d = jsworld.parseIsoDate(date); + } catch (error) { + // try full ISO-8601 date/time string + d = jsworld.parseIsoDateTime(date); + } + } + else if (date !== null && typeof date == "object") { + // assume ready Date object + d = date; + } + else { + throw "Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"; + } + + return this._applyFormatting(d, this.lc.d_fmt); + }; + + + /** + * @public + * + * @description Formats a time according to the preset locale. + * + * @param {Date|String} date A valid Date object instance or a string + * containing a valid ISO-8601 formatted time, e.g. "23:59:59" + * or "2010-03-31 23:59:59". + * + * @returns {String} The formatted time. + * + * @throws Error on invalid date argument. + */ + this.formatTime = function(date) { + + var d = null; + + if (typeof date == "string") { + // assume ISO-8601 time string + try { + d = jsworld.parseIsoTime(date); + } catch (error) { + // try full ISO-8601 date/time string + d = jsworld.parseIsoDateTime(date); + } + } + else if (date !== null && typeof date == "object") { + // assume ready Date object + d = date; + } + else { + throw "Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"; + } + + return this._applyFormatting(d, this.lc.t_fmt); + }; + + + /** + * @public + * + * @description Formats a date/time value according to the preset + * locale. + * + * @param {Date|String} date A valid Date object instance or a string + * containing a valid ISO-8601 formatted date/time, e.g. + * "2010-03-31 23:59:59". + * + * @returns {String} The formatted time. + * + * @throws Error on invalid argument. + */ + this.formatDateTime = function(date) { + + var d = null; + + if (typeof date == "string") { + // assume ISO-8601 format + d = jsworld.parseIsoDateTime(date); + } + else if (date !== null && typeof date == "object") { + // assume ready Date object + d = date; + } + else { + throw "Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"; + } + + return this._applyFormatting(d, this.lc.d_t_fmt); + }; + + + /** + * @private + * + * @description Apples formatting to the Date object according to the + * format string. + * + * @param {Date} d A valid Date instance. + * @param {String} s The formatting string with '%' placeholders. + * + * @returns {String} The formatted string. + */ + this._applyFormatting = function(d, s) { + + s = s.replace(/%%/g, '%'); + s = s.replace(/%a/g, this.lc.abday[d.getDay()]); + s = s.replace(/%A/g, this.lc.day[d.getDay()]); + s = s.replace(/%b/g, this.lc.abmon[d.getMonth()]); + s = s.replace(/%B/g, this.lc.mon[d.getMonth()]); + s = s.replace(/%d/g, jsworld._zeroPad(d.getDate(), 2)); + s = s.replace(/%e/g, jsworld._spacePad(d.getDate(), 2)); + s = s.replace(/%F/g, d.getFullYear() + + "-" + + jsworld._zeroPad(d.getMonth()+1, 2) + + "-" + + jsworld._zeroPad(d.getDate(), 2)); + s = s.replace(/%h/g, this.lc.abmon[d.getMonth()]); // same as %b + s = s.replace(/%H/g, jsworld._zeroPad(d.getHours(), 2)); + s = s.replace(/%I/g, jsworld._zeroPad(this._hours12(d.getHours()), 2)); + s = s.replace(/%k/g, d.getHours()); + s = s.replace(/%l/g, this._hours12(d.getHours())); + s = s.replace(/%m/g, jsworld._zeroPad(d.getMonth()+1, 2)); + s = s.replace(/%n/g, "\n"); + s = s.replace(/%M/g, jsworld._zeroPad(d.getMinutes(), 2)); + s = s.replace(/%p/g, this._getAmPm(d.getHours())); + s = s.replace(/%P/g, this._getAmPm(d.getHours()).toLocaleLowerCase()); // safe? + s = s.replace(/%R/g, jsworld._zeroPad(d.getHours(), 2) + + ":" + + jsworld._zeroPad(d.getMinutes(), 2)); + s = s.replace(/%S/g, jsworld._zeroPad(d.getSeconds(), 2)); + s = s.replace(/%T/g, jsworld._zeroPad(d.getHours(), 2) + + ":" + + jsworld._zeroPad(d.getMinutes(), 2) + + ":" + + jsworld._zeroPad(d.getSeconds(), 2)); + s = s.replace(/%w/g, this.lc.day[d.getDay()]); + s = s.replace(/%y/g, new String(d.getFullYear()).substring(2)); + s = s.replace(/%Y/g, d.getFullYear()); + + s = s.replace(/%Z/g, ""); // to do: ignored until a reliable TMZ method found + + s = s.replace(/%[a-zA-Z]/g, ""); // ignore all other % sequences + + return s; + }; + + + /** + * @private + * + * @description Does 24 to 12 hour conversion. + * + * @param {integer Number} hour24 Hour [0..23]. + * + * @returns {integer Number} Corresponding hour [1..12]. + */ + this._hours12 = function(hour24) { + + if (hour24 === 0) + return 12; // 00h is 12AM + + else if (hour24 > 12) + return hour24 - 12; // 1PM to 11PM + + else + return hour24; // 1AM to 12PM + }; + + + /** + * @private + * + * @description Gets the appropriate localised AM or PM string depending + * on the day hour. Special cases: midnight is 12AM, noon is 12PM. + * + * @param {integer Number} hour24 Hour [0..23]. + * + * @returns {String} The corresponding localised AM or PM string. + */ + this._getAmPm = function(hour24) { + + if (hour24 < 12) + return this.lc.am; + else + return this.lc.pm; + }; +}; + + + +/** + * @class Class for localised formatting of currency amounts. + * + *

See: + * POSIX LC_MONETARY. + * + * @public + * @constructor + * @description Creates a new monetary formatter for the specified locale. + * + * @param {jsworld.Locale} locale A locale object specifying the required + * POSIX LC_MONETARY formatting properties. + * @param {String} [currencyCode] Set the currency explicitly by + * passing its international ISO-4217 code, e.g. "USD", "EUR", "GBP". + * Use this optional parameter to override the default local currency + * @param {String} [altIntSymbol] Non-local currencies are formatted + * with their international ISO-4217 code to prevent ambiguity. + * Use this optional argument to force a different symbol, such as the + * currency's shorthand sign. This is mostly useful when the shorthand + * sign is both internationally recognised and identifies the currency + * uniquely (e.g. the Euro sign). + * + * @throws Error on constructor failure. + */ +jsworld.MonetaryFormatter = function(locale, currencyCode, altIntSymbol) { + + if (typeof locale != "object" || locale._className != "jsworld.Locale") + throw "Constructor error: You must provide a valid jsworld.Locale instance"; + + this.lc = locale; + + /** + * @private + * @description Lookup table to determine the fraction digits for a + * specific currency; most currencies subdivide at 1/100 (2 fractional + * digits), so we store only those that deviate from the default. + * + *

The data is from Unicode's CLDR version 1.7.0. The two currencies + * with non-decimal subunits (MGA and MRO) are marked as having no + * fractional digits as well as all currencies that have no subunits + * in circulation. + * + *

It is "hard-wired" for referential convenience and is only looked + * up when an overriding currencyCode parameter is supplied. + */ + this.currencyFractionDigits = { + "AFN" : 0, "ALL" : 0, "AMD" : 0, "BHD" : 3, "BIF" : 0, + "BYR" : 0, "CLF" : 0, "CLP" : 0, "COP" : 0, "CRC" : 0, + "DJF" : 0, "GNF" : 0, "GYD" : 0, "HUF" : 0, "IDR" : 0, + "IQD" : 0, "IRR" : 0, "ISK" : 0, "JOD" : 3, "JPY" : 0, + "KMF" : 0, "KRW" : 0, "KWD" : 3, "LAK" : 0, "LBP" : 0, + "LYD" : 3, "MGA" : 0, "MMK" : 0, "MNT" : 0, "MRO" : 0, + "MUR" : 0, "OMR" : 3, "PKR" : 0, "PYG" : 0, "RSD" : 0, + "RWF" : 0, "SLL" : 0, "SOS" : 0, "STD" : 0, "SYP" : 0, + "TND" : 3, "TWD" : 0, "TZS" : 0, "UGX" : 0, "UZS" : 0, + "VND" : 0, "VUV" : 0, "XAF" : 0, "XOF" : 0, "XPF" : 0, + "YER" : 0, "ZMK" : 0 + }; + + + // optional currencyCode argument? + if (typeof currencyCode == "string") { + // user wanted to override the local currency + this.currencyCode = currencyCode.toUpperCase(); + + // must override the frac digits too, for some + // currencies have 0, 2 or 3! + var numDigits = this.currencyFractionDigits[this.currencyCode]; + if (typeof numDigits != "number") + numDigits = 2; // default for most currencies + this.lc.frac_digits = numDigits; + this.lc.int_frac_digits = numDigits; + } + else { + // use local currency + this.currencyCode = this.lc.int_curr_symbol.substring(0,3).toUpperCase(); + } + + // extract intl. currency separator + this.intSep = this.lc.int_curr_symbol.charAt(3); + + // flag local or intl. sign formatting? + if (this.currencyCode == this.lc.int_curr_symbol.substring(0,3)) { + // currency matches the local one? -> + // formatting with local symbol and parameters + this.internationalFormatting = false; + this.curSym = this.lc.currency_symbol; + } + else { + // currency doesn't match the local -> + + // do we have an overriding currency symbol? + if (typeof altIntSymbol == "string") { + // -> force formatting with local parameters, using alt symbol + this.curSym = altIntSymbol; + this.internationalFormatting = false; + } + else { + // -> force formatting with intl. sign and parameters + this.internationalFormatting = true; + } + } + + + /** + * @public + * + * @description Gets the currency symbol used in formatting. + * + * @returns {String} The currency symbol. + */ + this.getCurrencySymbol = function() { + + return this.curSym; + }; + + + /** + * @public + * + * @description Gets the position of the currency symbol relative to + * the amount. Assumes a non-negative amount and local formatting. + * + * @param {String} intFlag Optional flag to force international + * formatting by passing the string "i". + * + * @returns {Boolean} True if the symbol precedes the amount, false if + * the symbol succeeds the amount. + */ + this.currencySymbolPrecedes = function(intFlag) { + + if (typeof intFlag == "string" && intFlag == "i") { + // international formatting was forced + if (this.lc.int_p_cs_precedes == 1) + return true; + else + return false; + + } + else { + // check whether local formatting is on or off + if (this.internationalFormatting) { + if (this.lc.int_p_cs_precedes == 1) + return true; + else + return false; + } + else { + if (this.lc.p_cs_precedes == 1) + return true; + else + return false; + } + } + }; + + + /** + * @public + * + * @description Gets the decimal delimiter (radix) used in formatting. + * + * @returns {String} The radix character. + */ + this.getDecimalPoint = function() { + + return this.lc.mon_decimal_point; + }; + + + /** + * @public + * + * @description Gets the number of fractional digits. Assumes local + * formatting. + * + * @param {String} intFlag Optional flag to force international + * formatting by passing the string "i". + * + * @returns {integer Number} The number of fractional digits. + */ + this.getFractionalDigits = function(intFlag) { + + if (typeof intFlag == "string" && intFlag == "i") { + // international formatting was forced + return this.lc.int_frac_digits; + } + else { + // check whether local formatting is on or off + if (this.internationalFormatting) + return this.lc.int_frac_digits; + else + return this.lc.frac_digits; + } + }; + + + /** + * @public + * + * @description Formats a monetary amount according to the preset + * locale. + * + *

+	 * For local currencies the native shorthand symbol will be used for
+	 * formatting.
+	 * Example:
+	 *        locale is en_US
+	 *        currency is USD
+	 *        -> the "$" symbol will be used, e.g. $123.45
+	 *        
+	 * For non-local currencies the international ISO-4217 code will be
+	 * used for formatting.
+	 * Example:
+	 *       locale is en_US (which has USD as currency)
+	 *       currency is EUR
+	 *       -> the ISO three-letter code will be used, e.g. EUR 123.45
+	 *
+	 * If the currency is non-local, but an alternative currency symbol was
+	 * provided, this will be used instead.
+	 * Example
+	 *       locale is en_US (which has USD as currency)
+	 *       currency is EUR
+	 *       an alternative symbol is provided - "€"
+	 *       -> the alternative symbol will be used, e.g. €123.45
+	 * 
+ * + * @param {Number|String} amount The amount to format as currency. + * @param {String} [options] Options to modify the formatted output: + *
    + *
  • "^" suppress grouping + *
  • "!" suppress the currency symbol + *
  • "~" suppress the currency symbol and the sign (positive or negative) + *
  • "i" force international sign (ISO-4217 code) formatting + *
  • ".n" specify decimal precision + * + * @returns The formatted currency amount as string. + * + * @throws "Error: Invalid amount" on bad amount. + */ + this.format = function(amount, options) { + + // if the amount is passed as string, check that it parses to a float + var floatAmount; + + if (typeof amount == "string") { + amount = jsworld._trim(amount); + floatAmount = parseFloat(amount); + + if (typeof floatAmount != "number" || isNaN(floatAmount)) + throw "Error: Amount string not a number"; + } + else if (typeof amount == "number") { + floatAmount = amount; + } + else { + throw "Error: Amount not a number"; + } + + // get the required precision, ".n" option arg overrides default locale config + var reqPrecision = jsworld._getPrecision(options); + + if (reqPrecision == -1) { + if (this.internationalFormatting || jsworld._hasOption("i", options)) + reqPrecision = this.lc.int_frac_digits; + else + reqPrecision = this.lc.frac_digits; + } + + // round + floatAmount = Math.round(floatAmount * Math.pow(10, reqPrecision)) / Math.pow(10, reqPrecision); + + + // convert the float amount to string and parse into + // object with properties integer and fraction + var parsedAmount = jsworld._splitNumber(String(floatAmount)); + + // format integer part with grouping chars + var formattedIntegerPart; + + if (floatAmount === 0) + formattedIntegerPart = "0"; + else + formattedIntegerPart = jsworld._hasOption("^", options) ? + parsedAmount.integer : + jsworld._formatIntegerPart(parsedAmount.integer, + this.lc.mon_grouping, + this.lc.mon_thousands_sep); + + + // format the fractional part + var formattedFractionPart; + + if (reqPrecision == -1) { + // pad fraction with trailing zeros accoring to default locale [int_]frac_digits + if (this.internationalFormatting || jsworld._hasOption("i", options)) + formattedFractionPart = + jsworld._formatFractionPart(parsedAmount.fraction, this.lc.int_frac_digits); + else + formattedFractionPart = + jsworld._formatFractionPart(parsedAmount.fraction, this.lc.frac_digits); + } + else { + // pad fraction with trailing zeros according to optional format parameter + formattedFractionPart = + jsworld._formatFractionPart(parsedAmount.fraction, reqPrecision); + } + + + // join integer and decimal parts using the mon_decimal_point property + var quantity; + + if (this.lc.frac_digits > 0 || formattedFractionPart.length) + quantity = formattedIntegerPart + this.lc.mon_decimal_point + formattedFractionPart; + else + quantity = formattedIntegerPart; + + + // do final formatting with sign and symbol + if (jsworld._hasOption("~", options)) { + return quantity; + } + else { + var suppressSymbol = jsworld._hasOption("!", options) ? true : false; + + var sign = floatAmount < 0 ? "-" : "+"; + + if (this.internationalFormatting || jsworld._hasOption("i", options)) { + + // format with ISO-4217 code (suppressed or not) + if (suppressSymbol) + return this._formatAsInternationalCurrencyWithNoSym(sign, quantity); + else + return this._formatAsInternationalCurrency(sign, quantity); + } + else { + // format with local currency code (suppressed or not) + if (suppressSymbol) + return this._formatAsLocalCurrencyWithNoSym(sign, quantity); + else + return this._formatAsLocalCurrency(sign, quantity); + } + } + }; + + + /** + * @private + * + * @description Assembles the final string with sign, separator and symbol as local + * currency. + * + * @param {String} sign The amount sign: "+" or "-". + * @param {String} q The formatted quantity (unsigned). + * + * @returns {String} The final formatted string. + */ + this._formatAsLocalCurrency = function (sign, q) { + + // assemble final formatted amount by going over all possible value combinations of: + // sign {+,-} , sign position {0,1,2,3,4} , separator {0,1,2} , symbol position {0,1} + if (sign == "+") { + + // parentheses + if (this.lc.p_sign_posn === 0 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { + return "(" + q + this.curSym + ")"; + } + else if (this.lc.p_sign_posn === 0 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { + return "(" + this.curSym + q + ")"; + } + else if (this.lc.p_sign_posn === 0 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { + return "(" + q + " " + this.curSym + ")"; + } + else if (this.lc.p_sign_posn === 0 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { + return "(" + this.curSym + " " + q + ")"; + } + + // sign before q + sym + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { + return this.lc.positive_sign + q + this.curSym; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + this.curSym + q; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { + return this.lc.positive_sign + q + " " + this.curSym; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + this.curSym + " " + q; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { + return this.lc.positive_sign + " " + q + this.curSym; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + " " + this.curSym + q; + } + + // sign after q + sym + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { + return q + this.curSym + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { + return this.curSym + q + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { + return q + " " + this.curSym + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { + return this.curSym + " " + q + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { + return q + this.curSym + " " + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { + return this.curSym + q + " " + this.lc.positive_sign; + } + + // sign before sym + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { + return q + this.lc.positive_sign + this.curSym; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + this.curSym + q; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { + return q + " " + this.lc.positive_sign + this.curSym; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + this.curSym + " " + q; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { + return q + this.lc.positive_sign + " " + this.curSym; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + " " + this.curSym + q; + } + + // sign after symbol + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { + return q + this.curSym + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { + return this.curSym + this.lc.positive_sign + q; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { + return q + " " + this.curSym + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { + return this.curSym + this.lc.positive_sign + " " + q; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { + return q + this.curSym + " " + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { + return this.curSym + " " + this.lc.positive_sign + q; + } + + } + else if (sign == "-") { + + // parentheses enclose q + sym + if (this.lc.n_sign_posn === 0 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { + return "(" + q + this.curSym + ")"; + } + else if (this.lc.n_sign_posn === 0 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { + return "(" + this.curSym + q + ")"; + } + else if (this.lc.n_sign_posn === 0 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { + return "(" + q + " " + this.curSym + ")"; + } + else if (this.lc.n_sign_posn === 0 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { + return "(" + this.curSym + " " + q + ")"; + } + + // sign before q + sym + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { + return this.lc.negative_sign + q + this.curSym; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + this.curSym + q; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { + return this.lc.negative_sign + q + " " + this.curSym; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + this.curSym + " " + q; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { + return this.lc.negative_sign + " " + q + this.curSym; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + " " + this.curSym + q; + } + + // sign after q + sym + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { + return q + this.curSym + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { + return this.curSym + q + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { + return q + " " + this.curSym + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { + return this.curSym + " " + q + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { + return q + this.curSym + " " + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { + return this.curSym + q + " " + this.lc.negative_sign; + } + + // sign before sym + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { + return q + this.lc.negative_sign + this.curSym; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + this.curSym + q; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { + return q + " " + this.lc.negative_sign + this.curSym; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + this.curSym + " " + q; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { + return q + this.lc.negative_sign + " " + this.curSym; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + " " + this.curSym + q; + } + + // sign after symbol + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { + return q + this.curSym + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { + return this.curSym + this.lc.negative_sign + q; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { + return q + " " + this.curSym + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { + return this.curSym + this.lc.negative_sign + " " + q; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { + return q + this.curSym + " " + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { + return this.curSym + " " + this.lc.negative_sign + q; + } + } + + // throw error if we fall through + throw "Error: Invalid POSIX LC MONETARY definition"; + }; + + + /** + * @private + * + * @description Assembles the final string with sign, separator and ISO-4217 + * currency code. + * + * @param {String} sign The amount sign: "+" or "-". + * @param {String} q The formatted quantity (unsigned). + * + * @returns {String} The final formatted string. + */ + this._formatAsInternationalCurrency = function (sign, q) { + + // assemble the final formatted amount by going over all possible value combinations of: + // sign {+,-} , sign position {0,1,2,3,4} , separator {0,1,2} , symbol position {0,1} + + if (sign == "+") { + + // parentheses + if (this.lc.int_p_sign_posn === 0 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { + return "(" + q + this.currencyCode + ")"; + } + else if (this.lc.int_p_sign_posn === 0 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { + return "(" + this.currencyCode + q + ")"; + } + else if (this.lc.int_p_sign_posn === 0 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { + return "(" + q + this.intSep + this.currencyCode + ")"; + } + else if (this.lc.int_p_sign_posn === 0 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { + return "(" + this.currencyCode + this.intSep + q + ")"; + } + + // sign before q + sym + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { + return this.lc.positive_sign + q + this.currencyCode; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.currencyCode + q; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { + return this.lc.positive_sign + q + this.intSep + this.currencyCode; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.currencyCode + this.intSep + q; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { + return this.lc.positive_sign + this.intSep + q + this.currencyCode; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.intSep + this.currencyCode + q; + } + + // sign after q + sym + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { + return q + this.currencyCode + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { + return this.currencyCode + q + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { + return q + this.intSep + this.currencyCode + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { + return this.currencyCode + this.intSep + q + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { + return q + this.currencyCode + this.intSep + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { + return this.currencyCode + q + this.intSep + this.lc.positive_sign; + } + + // sign before sym + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { + return q + this.lc.positive_sign + this.currencyCode; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.currencyCode + q; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { + return q + this.intSep + this.lc.positive_sign + this.currencyCode; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.currencyCode + this.intSep + q; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { + return q + this.lc.positive_sign + this.intSep + this.currencyCode; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.intSep + this.currencyCode + q; + } + + // sign after symbol + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { + return q + this.currencyCode + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { + return this.currencyCode + this.lc.positive_sign + q; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { + return q + this.intSep + this.currencyCode + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { + return this.currencyCode + this.lc.positive_sign + this.intSep + q; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { + return q + this.currencyCode + this.intSep + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { + return this.currencyCode + this.intSep + this.lc.positive_sign + q; + } + + } + else if (sign == "-") { + + // parentheses enclose q + sym + if (this.lc.int_n_sign_posn === 0 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { + return "(" + q + this.currencyCode + ")"; + } + else if (this.lc.int_n_sign_posn === 0 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { + return "(" + this.currencyCode + q + ")"; + } + else if (this.lc.int_n_sign_posn === 0 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { + return "(" + q + this.intSep + this.currencyCode + ")"; + } + else if (this.lc.int_n_sign_posn === 0 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { + return "(" + this.currencyCode + this.intSep + q + ")"; + } + + // sign before q + sym + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { + return this.lc.negative_sign + q + this.currencyCode; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.currencyCode + q; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { + return this.lc.negative_sign + q + this.intSep + this.currencyCode; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.currencyCode + this.intSep + q; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { + return this.lc.negative_sign + this.intSep + q + this.currencyCode; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.intSep + this.currencyCode + q; + } + + // sign after q + sym + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { + return q + this.currencyCode + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { + return this.currencyCode + q + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { + return q + this.intSep + this.currencyCode + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { + return this.currencyCode + this.intSep + q + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { + return q + this.currencyCode + this.intSep + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { + return this.currencyCode + q + this.intSep + this.lc.negative_sign; + } + + // sign before sym + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { + return q + this.lc.negative_sign + this.currencyCode; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.currencyCode + q; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { + return q + this.intSep + this.lc.negative_sign + this.currencyCode; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.currencyCode + this.intSep + q; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { + return q + this.lc.negative_sign + this.intSep + this.currencyCode; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.intSep + this.currencyCode + q; + } + + // sign after symbol + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { + return q + this.currencyCode + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { + return this.currencyCode + this.lc.negative_sign + q; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { + return q + this.intSep + this.currencyCode + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { + return this.currencyCode + this.lc.negative_sign + this.intSep + q; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { + return q + this.currencyCode + this.intSep + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { + return this.currencyCode + this.intSep + this.lc.negative_sign + q; + } + } + + // throw error if we fall through + throw "Error: Invalid POSIX LC MONETARY definition"; + }; + + + /** + * @private + * + * @description Assembles the final string with sign and separator, but suppress the + * local currency symbol. + * + * @param {String} sign The amount sign: "+" or "-". + * @param {String} q The formatted quantity (unsigned). + * + * @returns {String} The final formatted string + */ + this._formatAsLocalCurrencyWithNoSym = function (sign, q) { + + // assemble the final formatted amount by going over all possible value combinations of: + // sign {+,-} , sign position {0,1,2,3,4} , separator {0,1,2} , symbol position {0,1} + + if (sign == "+") { + + // parentheses + if (this.lc.p_sign_posn === 0) { + return "(" + q + ")"; + } + + // sign before q + sym + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { + return this.lc.positive_sign + q; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + q; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { + return this.lc.positive_sign + q; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + q; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { + return this.lc.positive_sign + " " + q; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + " " + q; + } + + // sign after q + sym + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { + return q + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { + return q + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { + return q + " " + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { + return q + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { + return q + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { + return q + " " + this.lc.positive_sign; + } + + // sign before sym + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { + return q + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + q; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { + return q + " " + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + " " + q; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { + return q + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + " " + q; + } + + // sign after symbol + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { + return q + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + q; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { + return q + " " + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + " " + q; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { + return q + " " + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + q; + } + + } + else if (sign == "-") { + + // parentheses enclose q + sym + if (this.lc.n_sign_posn === 0) { + return "(" + q + ")"; + } + + // sign before q + sym + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { + return this.lc.negative_sign + q; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + q; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { + return this.lc.negative_sign + q; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + " " + q; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { + return this.lc.negative_sign + " " + q; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + " " + q; + } + + // sign after q + sym + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { + return q + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { + return q + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { + return q + " " + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { + return q + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { + return q + " " + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { + return q + " " + this.lc.negative_sign; + } + + // sign before sym + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { + return q + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + q; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { + return q + " " + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + " " + q; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { + return q + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + " " + q; + } + + // sign after symbol + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { + return q + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + q; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { + return q + " " + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + " " + q; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { + return q + " " + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + q; + } + } + + // throw error if we fall through + throw "Error: Invalid POSIX LC MONETARY definition"; + }; + + + /** + * @private + * + * @description Assembles the final string with sign and separator, but suppress + * the ISO-4217 currency code. + * + * @param {String} sign The amount sign: "+" or "-". + * @param {String} q The formatted quantity (unsigned). + * + * @returns {String} The final formatted string. + */ + this._formatAsInternationalCurrencyWithNoSym = function (sign, q) { + + // assemble the final formatted amount by going over all possible value combinations of: + // sign {+,-} , sign position {0,1,2,3,4} , separator {0,1,2} , symbol position {0,1} + + if (sign == "+") { + + // parentheses + if (this.lc.int_p_sign_posn === 0) { + return "(" + q + ")"; + } + + // sign before q + sym + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { + return this.lc.positive_sign + q; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + q; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { + return this.lc.positive_sign + q; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.intSep + q; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { + return this.lc.positive_sign + this.intSep + q; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.intSep + q; + } + + // sign after q + sym + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { + return q + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { + return q + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { + return q + this.intSep + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { + return q + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { + return q + this.intSep + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { + return q + this.intSep + this.lc.positive_sign; + } + + // sign before sym + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { + return q + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + q; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { + return q + this.intSep + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.intSep + q; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { + return q + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.intSep + q; + } + + // sign after symbol + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { + return q + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + q; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { + return q + this.intSep + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.intSep + q; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { + return q + this.intSep + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + q; + } + + } + else if (sign == "-") { + + // parentheses enclose q + sym + if (this.lc.int_n_sign_posn === 0) { + return "(" + q + ")"; + } + + // sign before q + sym + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { + return this.lc.negative_sign + q; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + q; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { + return this.lc.negative_sign + q; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.intSep + q; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { + return this.lc.negative_sign + this.intSep + q; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.intSep + q; + } + + // sign after q + sym + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { + return q + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { + return q + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { + return q + this.intSep + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { + return q + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { + return q + this.intSep + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { + return q + this.intSep + this.lc.negative_sign; + } + + // sign before sym + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { + return q + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + q; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { + return q + this.intSep + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.intSep + q; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { + return q + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.intSep + q; + } + + // sign after symbol + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { + return q + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + q; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { + return q + this.intSep + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.intSep + q; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { + return q + this.intSep + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + q; + } + } + + // throw error if we fall through + throw "Error: Invalid POSIX LC_MONETARY definition"; + }; +}; + + +/** + * @class + * Class for parsing localised number strings. + * + * @public + * @constructor + * @description Creates a new numeric parser for the specified locale. + * + * @param {jsworld.Locale} locale A locale object specifying the required + * POSIX LC_NUMERIC formatting properties. + * + * @throws Error on constructor failure. + */ +jsworld.NumericParser = function(locale) { + + if (typeof locale != "object" || locale._className != "jsworld.Locale") + throw "Constructor error: You must provide a valid jsworld.Locale instance"; + + this.lc = locale; + + + /** + * @public + * + * @description Parses a numeric string formatted according to the + * preset locale. Leading and trailing whitespace is ignored; the number + * may also be formatted without thousands separators. + * + * @param {String} formattedNumber The formatted number. + * + * @returns {Number} The parsed number. + * + * @throws Error on a parse exception. + */ + this.parse = function(formattedNumber) { + + if (typeof formattedNumber != "string") + throw "Parse error: Argument must be a string"; + + // trim whitespace + var s = jsworld._trim(formattedNumber); + + // remove any thousand separator symbols + s = jsworld._stringReplaceAll(formattedNumber, this.lc.thousands_sep, ""); + + // replace any local decimal point symbols with the symbol used + // in JavaScript "." + s = jsworld._stringReplaceAll(s, this.lc.decimal_point, "."); + + // test if the string represents a number + if (jsworld._isNumber(s)) + return parseFloat(s, 10); + else + throw "Parse error: Invalid number string"; + }; +}; + + +/** + * @class + * Class for parsing localised date and time strings. + * + * @public + * @constructor + * @description Creates a new date/time parser for the specified locale. + * + * @param {jsworld.Locale} locale A locale object specifying the required + * POSIX LC_TIME formatting properties. + * + * @throws Error on constructor failure. + */ +jsworld.DateTimeParser = function(locale) { + + if (typeof locale != "object" || locale._className != "jsworld.Locale") + throw "Constructor error: You must provide a valid jsworld.Locale instance."; + + this.lc = locale; + + + /** + * @public + * + * @description Parses a time string formatted according to the + * POSIX LC_TIME t_fmt property of the preset locale. + * + * @param {String} formattedTime The formatted time. + * + * @returns {String} The parsed time in ISO-8601 format (HH:MM:SS), e.g. + * "23:59:59". + * + * @throws Error on a parse exception. + */ + this.parseTime = function(formattedTime) { + + if (typeof formattedTime != "string") + throw "Parse error: Argument must be a string"; + + var dt = this._extractTokens(this.lc.t_fmt, formattedTime); + + var timeDefined = false; + + if (dt.hour !== null && dt.minute !== null && dt.second !== null) { + timeDefined = true; + } + else if (dt.hourAmPm !== null && dt.am !== null && dt.minute !== null && dt.second !== null) { + if (dt.am) { + // AM [12(midnight), 1 .. 11] + if (dt.hourAmPm == 12) + dt.hour = 0; + else + dt.hour = parseInt(dt.hourAmPm, 10); + } + else { + // PM [12(noon), 1 .. 11] + if (dt.hourAmPm == 12) + dt.hour = 12; + else + dt.hour = parseInt(dt.hourAmPm, 10) + 12; + } + timeDefined = true; + } + + if (timeDefined) + return jsworld._zeroPad(dt.hour, 2) + + ":" + + jsworld._zeroPad(dt.minute, 2) + + ":" + + jsworld._zeroPad(dt.second, 2); + else + throw "Parse error: Invalid/ambiguous time string"; + }; + + + /** + * @public + * + * @description Parses a date string formatted according to the + * POSIX LC_TIME d_fmt property of the preset locale. + * + * @param {String} formattedDate The formatted date, must be valid. + * + * @returns {String} The parsed date in ISO-8601 format (YYYY-MM-DD), + * e.g. "2010-03-31". + * + * @throws Error on a parse exception. + */ + this.parseDate = function(formattedDate) { + + if (typeof formattedDate != "string") + throw "Parse error: Argument must be a string"; + + var dt = this._extractTokens(this.lc.d_fmt, formattedDate); + + var dateDefined = false; + + if (dt.year !== null && dt.month !== null && dt.day !== null) { + dateDefined = true; + } + + if (dateDefined) + return jsworld._zeroPad(dt.year, 4) + + "-" + + jsworld._zeroPad(dt.month, 2) + + "-" + + jsworld._zeroPad(dt.day, 2); + else + throw "Parse error: Invalid date string"; + }; + + + /** + * @public + * + * @description Parses a date/time string formatted according to the + * POSIX LC_TIME d_t_fmt property of the preset locale. + * + * @param {String} formattedDateTime The formatted date/time, must be + * valid. + * + * @returns {String} The parsed date/time in ISO-8601 format + * (YYYY-MM-DD HH:MM:SS), e.g. "2010-03-31 23:59:59". + * + * @throws Error on a parse exception. + */ + this.parseDateTime = function(formattedDateTime) { + + if (typeof formattedDateTime != "string") + throw "Parse error: Argument must be a string"; + + var dt = this._extractTokens(this.lc.d_t_fmt, formattedDateTime); + + var timeDefined = false; + var dateDefined = false; + + if (dt.hour !== null && dt.minute !== null && dt.second !== null) { + timeDefined = true; + } + else if (dt.hourAmPm !== null && dt.am !== null && dt.minute !== null && dt.second !== null) { + if (dt.am) { + // AM [12(midnight), 1 .. 11] + if (dt.hourAmPm == 12) + dt.hour = 0; + else + dt.hour = parseInt(dt.hourAmPm, 10); + } + else { + // PM [12(noon), 1 .. 11] + if (dt.hourAmPm == 12) + dt.hour = 12; + else + dt.hour = parseInt(dt.hourAmPm, 10) + 12; + } + timeDefined = true; + } + + if (dt.year !== null && dt.month !== null && dt.day !== null) { + dateDefined = true; + } + + if (dateDefined && timeDefined) + return jsworld._zeroPad(dt.year, 4) + + "-" + + jsworld._zeroPad(dt.month, 2) + + "-" + + jsworld._zeroPad(dt.day, 2) + + " " + + jsworld._zeroPad(dt.hour, 2) + + ":" + + jsworld._zeroPad(dt.minute, 2) + + ":" + + jsworld._zeroPad(dt.second, 2); + else + throw "Parse error: Invalid/ambiguous date/time string"; + }; + + + /** + * @private + * + * @description Parses a string according to the specified format + * specification. + * + * @param {String} fmtSpec The format specification, e.g. "%I:%M:%S %p". + * @param {String} s The string to parse. + * + * @returns {object} An object with set properties year, month, day, + * hour, minute and second if the corresponding values are + * found in the parsed string. + * + * @throws Error on a parse exception. + */ + this._extractTokens = function(fmtSpec, s) { + + // the return object containing the parsed date/time properties + var dt = { + // for date and date/time strings + "year" : null, + "month" : null, + "day" : null, + + // for time and date/time strings + "hour" : null, + "hourAmPm" : null, + "am" : null, + "minute" : null, + "second" : null, + + // used internally only + "weekday" : null + }; + + + // extract and process each token in the date/time spec + while (fmtSpec.length > 0) { + + // Do we have a valid "%\w" placeholder in stream? + if (fmtSpec.charAt(0) == "%" && fmtSpec.charAt(1) != "") { + + // get placeholder + var placeholder = fmtSpec.substring(0,2); + + if (placeholder == "%%") { + // escaped '%'' + s = s.substring(1); + } + else if (placeholder == "%a") { + // abbreviated weekday name + for (var i = 0; i < this.lc.abday.length; i++) { + + if (jsworld._stringStartsWith(s, this.lc.abday[i])) { + dt.weekday = i; + s = s.substring(this.lc.abday[i].length); + break; + } + } + + if (dt.weekday === null) + throw "Parse error: Unrecognised abbreviated weekday name (%a)"; + } + else if (placeholder == "%A") { + // weekday name + for (var i = 0; i < this.lc.day.length; i++) { + + if (jsworld._stringStartsWith(s, this.lc.day[i])) { + dt.weekday = i; + s = s.substring(this.lc.day[i].length); + break; + } + } + + if (dt.weekday === null) + throw "Parse error: Unrecognised weekday name (%A)"; + } + else if (placeholder == "%b" || placeholder == "%h") { + // abbreviated month name + for (var i = 0; i < this.lc.abmon.length; i++) { + + if (jsworld._stringStartsWith(s, this.lc.abmon[i])) { + dt.month = i + 1; + s = s.substring(this.lc.abmon[i].length); + break; + } + } + + if (dt.month === null) + throw "Parse error: Unrecognised abbreviated month name (%b)"; + } + else if (placeholder == "%B") { + // month name + for (var i = 0; i < this.lc.mon.length; i++) { + + if (jsworld._stringStartsWith(s, this.lc.mon[i])) { + dt.month = i + 1; + s = s.substring(this.lc.mon[i].length); + break; + } + } + + if (dt.month === null) + throw "Parse error: Unrecognised month name (%B)"; + } + else if (placeholder == "%d") { + // day of the month [01..31] + if (/^0[1-9]|[1-2][0-9]|3[0-1]/.test(s)) { + dt.day = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised day of the month (%d)"; + } + else if (placeholder == "%e") { + // day of the month [1..31] + + // Note: if %e is leading in fmt string -> space padded! + + var day = s.match(/^\s?(\d{1,2})/); + dt.day = parseInt(day, 10); + + if (isNaN(dt.day) || dt.day < 1 || dt.day > 31) + throw "Parse error: Unrecognised day of the month (%e)"; + + s = s.substring(day.length); + } + else if (placeholder == "%F") { + // equivalent to %Y-%m-%d (ISO-8601 date format) + + // year [nnnn] + if (/^\d\d\d\d/.test(s)) { + dt.year = parseInt(s.substring(0,4), 10); + s = s.substring(4); + } + else { + throw "Parse error: Unrecognised date (%F)"; + } + + // - + if (jsworld._stringStartsWith(s, "-")) + s = s.substring(1); + else + throw "Parse error: Unrecognised date (%F)"; + + // month [01..12] + if (/^0[1-9]|1[0-2]/.test(s)) { + dt.month = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised date (%F)"; + + // - + if (jsworld._stringStartsWith(s, "-")) + s = s.substring(1); + else + throw "Parse error: Unrecognised date (%F)"; + + // day of the month [01..31] + if (/^0[1-9]|[1-2][0-9]|3[0-1]/.test(s)) { + dt.day = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised date (%F)"; + } + else if (placeholder == "%H") { + // hour [00..23] + if (/^[0-1][0-9]|2[0-3]/.test(s)) { + dt.hour = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised hour (%H)"; + } + else if (placeholder == "%I") { + // hour [01..12] + if (/^0[1-9]|1[0-2]/.test(s)) { + dt.hourAmPm = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised hour (%I)"; + } + else if (placeholder == "%k") { + // hour [0..23] + var h = s.match(/^(\d{1,2})/); + dt.hour = parseInt(h, 10); + + if (isNaN(dt.hour) || dt.hour < 0 || dt.hour > 23) + throw "Parse error: Unrecognised hour (%k)"; + + s = s.substring(h.length); + } + else if (placeholder == "%l") { + // hour AM/PM [1..12] + var h = s.match(/^(\d{1,2})/); + dt.hourAmPm = parseInt(h, 10); + + if (isNaN(dt.hourAmPm) || dt.hourAmPm < 1 || dt.hourAmPm > 12) + throw "Parse error: Unrecognised hour (%l)"; + + s = s.substring(h.length); + } + else if (placeholder == "%m") { + // month [01..12] + if (/^0[1-9]|1[0-2]/.test(s)) { + dt.month = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised month (%m)"; + } + else if (placeholder == "%M") { + // minute [00..59] + if (/^[0-5][0-9]/.test(s)) { + dt.minute = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised minute (%M)"; + } + else if (placeholder == "%n") { + // new line + + if (s.charAt(0) == "\n") + s = s.substring(1); + else + throw "Parse error: Unrecognised new line (%n)"; + } + else if (placeholder == "%p") { + // locale's equivalent of AM/PM + if (jsworld._stringStartsWith(s, this.lc.am)) { + dt.am = true; + s = s.substring(this.lc.am.length); + } + else if (jsworld._stringStartsWith(s, this.lc.pm)) { + dt.am = false; + s = s.substring(this.lc.pm.length); + } + else + throw "Parse error: Unrecognised AM/PM value (%p)"; + } + else if (placeholder == "%P") { + // same as %p but forced lower case + if (jsworld._stringStartsWith(s, this.lc.am.toLowerCase())) { + dt.am = true; + s = s.substring(this.lc.am.length); + } + else if (jsworld._stringStartsWith(s, this.lc.pm.toLowerCase())) { + dt.am = false; + s = s.substring(this.lc.pm.length); + } + else + throw "Parse error: Unrecognised AM/PM value (%P)"; + } + else if (placeholder == "%R") { + // same as %H:%M + + // hour [00..23] + if (/^[0-1][0-9]|2[0-3]/.test(s)) { + dt.hour = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised time (%R)"; + + // : + if (jsworld._stringStartsWith(s, ":")) + s = s.substring(1); + else + throw "Parse error: Unrecognised time (%R)"; + + // minute [00..59] + if (/^[0-5][0-9]/.test(s)) { + dt.minute = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised time (%R)"; + + } + else if (placeholder == "%S") { + // second [00..59] + if (/^[0-5][0-9]/.test(s)) { + dt.second = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised second (%S)"; + } + else if (placeholder == "%T") { + // same as %H:%M:%S + + // hour [00..23] + if (/^[0-1][0-9]|2[0-3]/.test(s)) { + dt.hour = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised time (%T)"; + + // : + if (jsworld._stringStartsWith(s, ":")) + s = s.substring(1); + else + throw "Parse error: Unrecognised time (%T)"; + + // minute [00..59] + if (/^[0-5][0-9]/.test(s)) { + dt.minute = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised time (%T)"; + + // : + if (jsworld._stringStartsWith(s, ":")) + s = s.substring(1); + else + throw "Parse error: Unrecognised time (%T)"; + + // second [00..59] + if (/^[0-5][0-9]/.test(s)) { + dt.second = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised time (%T)"; + } + else if (placeholder == "%w") { + // weekday [0..6] + if (/^\d/.test(s)) { + dt.weekday = parseInt(s.substring(0,1), 10); + s = s.substring(1); + } + else + throw "Parse error: Unrecognised weekday number (%w)"; + } + else if (placeholder == "%y") { + // year [00..99] + if (/^\d\d/.test(s)) { + var year2digits = parseInt(s.substring(0,2), 10); + + // this conversion to year[nnnn] is arbitrary!!! + if (year2digits > 50) + dt.year = 1900 + year2digits; + else + dt.year = 2000 + year2digits; + + s = s.substring(2); + } + else + throw "Parse error: Unrecognised year (%y)"; + } + else if (placeholder == "%Y") { + // year [nnnn] + if (/^\d\d\d\d/.test(s)) { + dt.year = parseInt(s.substring(0,4), 10); + s = s.substring(4); + } + else + throw "Parse error: Unrecognised year (%Y)"; + } + + else if (placeholder == "%Z") { + // time-zone place holder is not supported + + if (fmtSpec.length === 0) + break; // ignore rest of fmt spec + } + + // remove the spec placeholder that was just parsed + fmtSpec = fmtSpec.substring(2); + } + else { + // If we don't have a placeholder, the chars + // at pos. 0 of format spec and parsed string must match + + // Note: Space chars treated 1:1 ! + + if (fmtSpec.charAt(0) != s.charAt(0)) + throw "Parse error: Unexpected symbol \"" + s.charAt(0) + "\" in date/time string"; + + fmtSpec = fmtSpec.substring(1); + s = s.substring(1); + } + } + + // parsing finished, return composite date/time object + return dt; + }; +}; + + +/** + * @class + * Class for parsing localised currency amount strings. + * + * @public + * @constructor + * @description Creates a new monetary parser for the specified locale. + * + * @param {jsworld.Locale} locale A locale object specifying the required + * POSIX LC_MONETARY formatting properties. + * + * @throws Error on constructor failure. + */ +jsworld.MonetaryParser = function(locale) { + + if (typeof locale != "object" || locale._className != "jsworld.Locale") + throw "Constructor error: You must provide a valid jsworld.Locale instance"; + + + this.lc = locale; + + + /** + * @public + * + * @description Parses a currency amount string formatted according to + * the preset locale. Leading and trailing whitespace is ignored; the + * amount may also be formatted without thousands separators. Both + * the local (shorthand) symbol and the ISO 4217 code are accepted to + * designate the currency in the formatted amount. + * + * @param {String} formattedCurrency The formatted currency amount. + * + * @returns {Number} The parsed amount. + * + * @throws Error on a parse exception. + */ + this.parse = function(formattedCurrency) { + + if (typeof formattedCurrency != "string") + throw "Parse error: Argument must be a string"; + + // Detect the format type and remove the currency symbol + var symbolType = this._detectCurrencySymbolType(formattedCurrency); + + var formatType, s; + + if (symbolType == "local") { + formatType = "local"; + s = formattedCurrency.replace(this.lc.getCurrencySymbol(), ""); + } + else if (symbolType == "int") { + formatType = "int"; + s = formattedCurrency.replace(this.lc.getIntCurrencySymbol(), ""); + } + else if (symbolType == "none") { + formatType = "local"; // assume local + s = formattedCurrency; + } + else + throw "Parse error: Internal assert failure"; + + // Remove any thousands separators + s = jsworld._stringReplaceAll(s, this.lc.mon_thousands_sep, ""); + + // Replace any local radix char with JavaScript's "." + s = s.replace(this.lc.mon_decimal_point, "."); + + // Remove all whitespaces + s = s.replace(/\s*/g, ""); + + // Remove any local non-negative sign + s = this._removeLocalNonNegativeSign(s, formatType); + + // Replace any local minus sign with JavaScript's "-" and put + // it in front of the amount if necessary + // (special parentheses rule checked too) + s = this._normaliseNegativeSign(s, formatType); + + // Finally, we should be left with a bare parsable decimal number + if (jsworld._isNumber(s)) + return parseFloat(s, 10); + else + throw "Parse error: Invalid currency amount string"; + }; + + + /** + * @private + * + * @description Tries to detect the symbol type used in the specified + * formatted currency string: local(shorthand), + * international (ISO-4217 code) or none. + * + * @param {String} formattedCurrency The the formatted currency string. + * + * @return {String} With possible values "local", "int" or "none". + */ + this._detectCurrencySymbolType = function(formattedCurrency) { + + // Check for whichever sign (int/local) is longer first + // to cover cases such as MOP/MOP$ and ZAR/R + + if (this.lc.getCurrencySymbol().length > this.lc.getIntCurrencySymbol().length) { + + if (formattedCurrency.indexOf(this.lc.getCurrencySymbol()) != -1) + return "local"; + else if (formattedCurrency.indexOf(this.lc.getIntCurrencySymbol()) != -1) + return "int"; + else + return "none"; + } + else { + if (formattedCurrency.indexOf(this.lc.getIntCurrencySymbol()) != -1) + return "int"; + else if (formattedCurrency.indexOf(this.lc.getCurrencySymbol()) != -1) + return "local"; + else + return "none"; + } + }; + + + /** + * @private + * + * @description Removes a local non-negative sign in a formatted + * currency string if it is found. This is done according to the + * locale properties p_sign_posn and int_p_sign_posn. + * + * @param {String} s The input string. + * @param {String} formatType With possible values "local" or "int". + * + * @returns {String} The processed string. + */ + this._removeLocalNonNegativeSign = function(s, formatType) { + + s = s.replace(this.lc.positive_sign, ""); + + // check for enclosing parentheses rule + if (((formatType == "local" && this.lc.p_sign_posn === 0) || + (formatType == "int" && this.lc.int_p_sign_posn === 0) ) && + /\(\d+\.?\d*\)/.test(s)) { + s = s.replace("(", ""); + s = s.replace(")", ""); + } + + return s; + }; + + + /** + * @private + * + * @description Replaces a local negative sign with the standard + * JavaScript minus ("-") sign placed in the correct position + * (preceding the amount). This is done according to the locale + * properties for negative sign symbol and relative position. + * + * @param {String} s The input string. + * @param {String} formatType With possible values "local" or "int". + * + * @returns {String} The processed string. + */ + this._normaliseNegativeSign = function(s, formatType) { + + // replace local negative symbol with JavaScript's "-" + s = s.replace(this.lc.negative_sign, "-"); + + // check for enclosing parentheses rule and replace them + // with negative sign before the amount + if ((formatType == "local" && this.lc.n_sign_posn === 0) || + (formatType == "int" && this.lc.int_n_sign_posn === 0) ) { + + if (/^\(\d+\.?\d*\)$/.test(s)) { + + s = s.replace("(", ""); + s = s.replace(")", ""); + return "-" + s; + } + } + + // check for rule negative sign succeeding the amount + if (formatType == "local" && this.lc.n_sign_posn == 2 || + formatType == "int" && this.lc.int_n_sign_posn == 2 ) { + + if (/^\d+\.?\d*-$/.test(s)) { + s = s.replace("-", ""); + return "-" + s; + } + } + + // check for rule cur. sym. succeeds and sign adjacent + if (formatType == "local" && this.lc.n_cs_precedes === 0 && this.lc.n_sign_posn == 3 || + formatType == "local" && this.lc.n_cs_precedes === 0 && this.lc.n_sign_posn == 4 || + formatType == "int" && this.lc.int_n_cs_precedes === 0 && this.lc.int_n_sign_posn == 3 || + formatType == "int" && this.lc.int_n_cs_precedes === 0 && this.lc.int_n_sign_posn == 4 ) { + + if (/^\d+\.?\d*-$/.test(s)) { + s = s.replace("-", ""); + return "-" + s; + } + } + + return s; + }; +}; + +// end-of-file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/uglify-hangs2.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/uglify-hangs2.js new file mode 100644 index 0000000..4e9f967 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/uglify-hangs2.js @@ -0,0 +1,166 @@ +jsworld.Locale = function(properties) { + + // LC_NUMERIC + + + this.frac_digits = properties.frac_digits; + + + // may be empty string/null for currencies with no fractional part + if (properties.mon_decimal_point === null || properties.mon_decimal_point == "") { + + if (this.frac_digits > 0) + throw "Error: Undefined mon_decimal_point property"; + else + properties.mon_decimal_point = ""; + } + + if (typeof properties.mon_decimal_point != "string") + throw "Error: Invalid/missing mon_decimal_point property"; + + this.mon_decimal_point = properties.mon_decimal_point; + + + if (typeof properties.mon_thousands_sep != "string") + throw "Error: Invalid/missing mon_thousands_sep property"; + + this.mon_thousands_sep = properties.mon_thousands_sep; + + + if (typeof properties.mon_grouping != "string") + throw "Error: Invalid/missing mon_grouping property"; + + this.mon_grouping = properties.mon_grouping; + + + if (typeof properties.positive_sign != "string") + throw "Error: Invalid/missing positive_sign property"; + + this.positive_sign = properties.positive_sign; + + + if (typeof properties.negative_sign != "string") + throw "Error: Invalid/missing negative_sign property"; + + this.negative_sign = properties.negative_sign; + + + if (properties.p_cs_precedes !== 0 && properties.p_cs_precedes !== 1) + throw "Error: Invalid/missing p_cs_precedes property, must be 0 or 1"; + + this.p_cs_precedes = properties.p_cs_precedes; + + + if (properties.n_cs_precedes !== 0 && properties.n_cs_precedes !== 1) + throw "Error: Invalid/missing n_cs_precedes, must be 0 or 1"; + + this.n_cs_precedes = properties.n_cs_precedes; + + + if (properties.p_sep_by_space !== 0 && + properties.p_sep_by_space !== 1 && + properties.p_sep_by_space !== 2) + throw "Error: Invalid/missing p_sep_by_space property, must be 0, 1 or 2"; + + this.p_sep_by_space = properties.p_sep_by_space; + + + if (properties.n_sep_by_space !== 0 && + properties.n_sep_by_space !== 1 && + properties.n_sep_by_space !== 2) + throw "Error: Invalid/missing n_sep_by_space property, must be 0, 1, or 2"; + + this.n_sep_by_space = properties.n_sep_by_space; + + + if (properties.p_sign_posn !== 0 && + properties.p_sign_posn !== 1 && + properties.p_sign_posn !== 2 && + properties.p_sign_posn !== 3 && + properties.p_sign_posn !== 4) + throw "Error: Invalid/missing p_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.p_sign_posn = properties.p_sign_posn; + + + if (properties.n_sign_posn !== 0 && + properties.n_sign_posn !== 1 && + properties.n_sign_posn !== 2 && + properties.n_sign_posn !== 3 && + properties.n_sign_posn !== 4) + throw "Error: Invalid/missing n_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.n_sign_posn = properties.n_sign_posn; + + + if (typeof properties.int_frac_digits != "number" && properties.int_frac_digits < 0) + throw "Error: Invalid/missing int_frac_digits property"; + + this.int_frac_digits = properties.int_frac_digits; + + + if (properties.int_p_cs_precedes !== 0 && properties.int_p_cs_precedes !== 1) + throw "Error: Invalid/missing int_p_cs_precedes property, must be 0 or 1"; + + this.int_p_cs_precedes = properties.int_p_cs_precedes; + + + if (properties.int_n_cs_precedes !== 0 && properties.int_n_cs_precedes !== 1) + throw "Error: Invalid/missing int_n_cs_precedes property, must be 0 or 1"; + + this.int_n_cs_precedes = properties.int_n_cs_precedes; + + + if (properties.int_p_sep_by_space !== 0 && + properties.int_p_sep_by_space !== 1 && + properties.int_p_sep_by_space !== 2) + throw "Error: Invalid/missing int_p_sep_by_spacev, must be 0, 1 or 2"; + + this.int_p_sep_by_space = properties.int_p_sep_by_space; + + + if (properties.int_n_sep_by_space !== 0 && + properties.int_n_sep_by_space !== 1 && + properties.int_n_sep_by_space !== 2) + throw "Error: Invalid/missing int_n_sep_by_space property, must be 0, 1, or 2"; + + this.int_n_sep_by_space = properties.int_n_sep_by_space; + + + if (properties.int_p_sign_posn !== 0 && + properties.int_p_sign_posn !== 1 && + properties.int_p_sign_posn !== 2 && + properties.int_p_sign_posn !== 3 && + properties.int_p_sign_posn !== 4) + throw "Error: Invalid/missing int_p_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.int_p_sign_posn = properties.int_p_sign_posn; + + + if (properties.int_n_sign_posn !== 0 && + properties.int_n_sign_posn !== 1 && + properties.int_n_sign_posn !== 2 && + properties.int_n_sign_posn !== 3 && + properties.int_n_sign_posn !== 4) + throw "Error: Invalid/missing int_n_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.int_n_sign_posn = properties.int_n_sign_posn; + + + // LC_TIME + + if (properties == null || typeof properties != "object") + throw "Error: Invalid/missing time locale properties"; + + + // parse the supported POSIX LC_TIME properties + + // abday + try { + this.abday = this._parseList(properties.abday, 7); + } + catch (error) { + throw "Error: Invalid abday property: " + error; + } + +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/uglify-js.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/uglify-js.js new file mode 100644 index 0000000..4305e23 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/uglify-js.js @@ -0,0 +1,17 @@ +//convienence function(src, [options]); +function uglify(orig_code, options){ + options || (options = {}); + var jsp = uglify.parser; + var pro = uglify.uglify; + + var ast = jsp.parse(orig_code, options.strict_semicolons); // parse code and get the initial AST + ast = pro.ast_mangle(ast, options.mangle_options); // get a new AST with mangled names + ast = pro.ast_squeeze(ast, options.squeeze_options); // get an AST with compression optimizations + var final_code = pro.gen_code(ast, options.gen_options); // compressed code here + return final_code; +}; + +uglify.parser = require("./lib/parse-js"); +uglify.uglify = require("./lib/process"); + +module.exports = uglify \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/.npmignore b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/.npmignore new file mode 100644 index 0000000..8233793 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/.npmignore @@ -0,0 +1,6 @@ +npm-debug.log +node_modules +.*.swp +.lock-* +build + diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/.travis.yml b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/.travis.yml new file mode 100644 index 0000000..08e4dad --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/.travis.yml @@ -0,0 +1,6 @@ +language: node_js +npm_args: --ws:native +node_js: + - "0.6" + - "0.8" + - "0.10" diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/History.md b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/History.md new file mode 100644 index 0000000..f723c6d --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/History.md @@ -0,0 +1,299 @@ +v0.4.29 - August 23th, 2013 +===================== +* Small clean up of the Node 0.11 support by using NAN from the NPM registry [kkoopa] +* Support for custom `Agent`'s through the options. [gramakri] & [TooTallNate] +* Support for custom headers through the options [3rd-Eden] +* Added a `gypfile` flag to the package.json for compiled module discovery [wolfeidau] + +v0.4.28 - August 16th, 2013 +===================== +* Node 0.11 support. [kkoopa] +* Authorization headers are sent when basic auth is used in the url [jcrugzz] +* Origin header will now include the port number [Jason Plum] +* Race condition fixed where data was received before the readyState was updated. [saschagehlich] + +v0.4.27 - June 27th, 2013 +===================== +* Frames are no longer masked in `wscat`. [slaskis] +* Don't retrain reference to large slab buffers. [jmatthewsr-msi] +* Don't use Buffer.byteLength for ArrayBuffer's. [Anthony Pesch] +* Fix browser field in package.json. [shtylman] +* Client-side certificate support & documentation improvements. [Lukas Berns] +* WebSocket readyState's is added to the prototype for spec compatiblity. [BallBearing] +* Use Object.defineProperty. [arlolra] +* Autodetect ArrayBuffers as binary when sending. [BallBearing] +* Check instanceof Buffer for binary data. [arlolra] +* Emit the close event before destroying the internal socket. [3rd-Eden] +* Don't setup multiply timeouts for one connection. [AndreasMadsen] +* Allow support for binding to ethereal port. [wpreul] +* Fix broken terminate reference. [3rd-Eden] +* Misc node 0.10 test fixes and documentation improvements. [3rd-Eden] +* Ensure ssl options are propagated to request. [einaros] +* Add 'Host' and 'Origin' to request header. [Lars-Magnus Skog] +* Subprotocol support. [kanaka] +* Honor ArrayBufferView's byteOffset when sending. [Anthony Pesch] +* Added target attribute for events. [arlolra] + +v0.4.26 - Skipped +===================== + +v0.4.25 - December 17th, 2012 +===================== +* Removed install.js. [shtylman] +* Added browser field to package.json. [shtylman] +* Support overwriting host header. [Raynos] +* Emit 'listening' also with custom http server. [sebiq] + +v0.4.24 - December 6th, 2012 +===================== +* Yet another intermediate release, to not delay minor features any longer. +* Native support installation issues further circumvented. [einaros] + +v0.4.23 - November 19th, 2012 +===================== +* Service release - last before major upgrade. +* Changes default host from 127.0.0.1 to 0.0.0.0. [einaros] + +v0.4.22 - October 3rd, 2012 +===================== +* clear failsafe cleanup timeout once cleanup is called [AndreasMadsen] +* added w3c compatible CloseEvent for onclose / addEventListener("close", ...). [einaros] +* fix the sub protocol header handler [sonnyp] +* fix unhandled exception if socket closes and 'error' is emitted [jmatthewsr-ms] + +v0.4.21 - July 14th, 2012 +===================== +* Emit error if server reponds with anything other than status code 101. [einaros] +* Added 'headers' event to server. [rauchg] +* path.exists moved to fs.exists. [blakmatrix] + +v0.4.20 - June 26th, 2012 +===================== +* node v0.8.0 compatibility release. + +v0.4.19 - June 19th, 2012 +===================== +* Change sender to merge buffers for relatively small payloads, may improve perf in some cases [einaros] +* Avoid EventEmitter for Receiver classes. As above this may improve perf. [einaros] +* Renamed fallback files from the somewhat misleading '*Windows'. [einaros] + +v0.4.18 - June 14th 2012 +===================== +* Fixed incorrect md5 digest encoding in Hixie handshake [nicokaiser] +* Added example of use with Express 3 [einaros] +* Change installation procedure to not require --ws:native to build native extensions. They will now build if a compiler is available. [einaros] + +v0.4.17 - June 13th 2012 +===================== +* Improve error handling during connection handshaking [einaros] +* Ensure that errors are caught also after connection teardown [nicokaiser] +* Update 'mocha' version to 1.1.0. [einaros] +* Stop showing 'undefined' for some error logs. [tricknotes] +* Update 'should' version to 0.6.3 [tricknotes] + +v0.4.16 - June 1st 2012 +===================== +* Build fix for Windows. [einaros] + +v0.4.15 - May 20th 2012 +===================== +* Enable fauxe streaming for hixie tansport. [einaros] +* Allow hixie sender to deal with buffers. [einaros/pigne] +* Allow error code 1011. [einaros] +* Fix framing for empty packets (empty pings and pongs might break). [einaros] +* Improve error and close handling, to avoid connections lingering in CLOSING state. [einaros] + +v0.4.14 - Apr 30th 2012 +===================== +* use node-gyp instead of node-waf [TooTallNate] +* remove old windows compatibility makefile, and silently fall back to native modules [einaros] +* ensure connection status [nicokaiser] +* websocket client updated to use port 443 by default for wss:// connections [einaros] +* support unix sockets [kschzt] + +v0.4.13 - Apr 12th 2012 +===================== + +* circumvent node 0.6+ related memory leak caused by Object.defineProperty [nicokaiser] +* improved error handling, improving stability in massive load use cases [nicokaiser] + +v0.4.12 - Mar 30th 2012 +===================== + +* various memory leak / possible memory leak cleanups [einaros] +* api documentation [nicokaiser] +* add option to disable client tracking [nicokaiser] + +v0.4.11 - Mar 24th 2012 +===================== + +* node v0.7 compatibillity release +* gyp support [TooTallNate] +* commander dependency update [jwueller] +* loadbalancer support [nicokaiser] + +v0.4.10 - Mar 22th 2012 +===================== + +* Final hixie close frame fixes. [nicokaiser] + +v0.4.9 - Mar 21st 2012 +===================== + +* Various hixie bugfixes (such as proper close frame handling). [einaros] + +v0.4.8 - Feb 29th 2012 +===================== + +* Allow verifyClient to run asynchronously [karlsequin] +* Various bugfixes and cleanups. [einaros] + +v0.4.7 - Feb 21st 2012 +===================== + +* Exposed bytesReceived from websocket client object, which makes it possible to implement bandwidth sampling. [einaros] +* Updated browser based file upload example to include and output per websocket channel bandwidth sampling. [einaros] +* Changed build scripts to check which architecture is currently in use. Required after the node.js changes to have prebuilt packages target ia32 by default. [einaros] + +v0.4.6 - Feb 9th 2012 +===================== + +* Added browser based file upload example. [einaros] +* Added server-to-browser status push example. [einaros] +* Exposed pause() and resume() on WebSocket object, to enable client stream shaping. [einaros] + +v0.4.5 - Feb 7th 2012 +===================== + +* Corrected regression bug in handling of connections with the initial frame delivered across both http upgrade head and a standalone packet. This would lead to a race condition, which in some cases could cause message corruption. [einaros] + +v0.4.4 - Feb 6th 2012 +===================== + +* Pass original request object to verifyClient, for cookie or authentication verifications. [einaros] +* Implemented addEventListener and slightly improved the emulation API by adding a MessageEvent with a readonly data attribute. [aslakhellesoy] +* Rewrite parts of hybi receiver to avoid stack overflows for large amounts of packets bundled in the same buffer / packet. [einaros] + +v0.4.3 - Feb 4th 2012 +===================== + +* Prioritized update: Corrected issue which would cause sockets to stay open longer than necessary, and resource leakage because of this. [einaros] + +v0.4.2 - Feb 4th 2012 +===================== + +* Breaking change: WebSocketServer's verifyOrigin option has been renamed to verifyClient. [einaros] +* verifyClient now receives { origin: 'origin header', secure: true/false }, where 'secure' will be true for ssl connections. [einaros] +* Split benchmark, in preparation for more thorough case. [einaros] +* Introduced hixie-76 draft support for server, since Safari (iPhone / iPad / OS X) and Opera still aren't updated to use Hybi. [einaros] +* Expose 'supports' object from WebSocket, to indicate e.g. the underlying transport's support for binary data. [einaros] +* Test and code cleanups. [einaros] + +v0.4.1 - Jan 25th 2012 +===================== + +* Use readline in wscat [tricknotes] +* Refactor _state away, in favor of the new _readyState [tricknotes] +* travis-ci integration [einaros] +* Fixed race condition in testsuite, causing a few tests to fail (without actually indicating errors) on travis [einaros] +* Expose pong event [paddybyers] +* Enabled running of WebSocketServer in noServer-mode, meaning that upgrades are passed in manually. [einaros] +* Reworked connection procedure for WebSocketServer, and cleaned up tests. [einaros] + +v0.4.0 - Jan 2nd 2012 +===================== + +* Windows compatibility [einaros] +* Windows compatible test script [einaros] + +v0.3.9 - Jan 1st 2012 +====================== + +* Improved protocol framing performance [einaros] +* WSS support [kazuyukitanimura] +* WSS tests [einaros] +* readyState exposed [justinlatimer, tricknotes] +* url property exposed [justinlatimer] +* Removed old 'state' property [einaros] +* Test cleanups [einaros] + +v0.3.8 - Dec 27th 2011 +====================== + +* Made it possible to listen on specific paths, which is especially good to have for precreated http servers [einaros] +* Extensive WebSocket / WebSocketServer cleanup, including changing all internal properties to unconfigurable, unenumerable properties [einaros] +* Receiver modifications to ensure even better performance with fragmented sends [einaros] +* Fixed issue in sender.js, which would cause SlowBuffer instances (such as returned from the crypto library's randomBytes) to be copied (and thus be dead slow) [einaros] +* Removed redundant buffer copy in sender.js, which should improve server performance [einaros] + +v0.3.7 - Dec 25nd 2011 +====================== + +* Added a browser based API which uses EventEmitters internally [3rd-Eden] +* Expose request information from upgrade event for websocket server clients [mmalecki] + +v0.3.6 - Dec 19th 2011 +====================== + +* Added option to let WebSocket.Server use an already existing http server [mmalecki] +* Migrating various option structures to use options.js module [einaros] +* Added a few more tests, options and handshake verifications to ensure that faulty connections are dealt with [einaros] +* Code cleanups in Sender and Receiver, to ensure even faster parsing [einaros] + +v0.3.5 - Dec 13th 2011 +====================== + +* Optimized Sender.js, Receiver.js and bufferutil.cc: + * Apply loop-unrolling-like small block copies rather than use node.js Buffer#copy() (which is slow). + * Mask blocks of data using combination of 32bit xor and loop-unrolling, instead of single bytes. + * Keep pre-made send buffer for small transfers. +* Leak fixes and code cleanups. + +v0.3.3 - Dec 12th 2011 +====================== + +* Compile fix for Linux. +* Rewrote parts of WebSocket.js, to avoid try/catch and thus avoid optimizer bailouts. + +v0.3.2 - Dec 11th 2011 +====================== + +* Further performance updates, including the additions of a native BufferUtil module, which deals with several of the cpu intensive WebSocket operations. + +v0.3.1 - Dec 8th 2011 +====================== + +* Service release, fixing broken tests. + +v0.3.0 - Dec 8th 2011 +====================== + +* Node.js v0.4.x compatibility. +* Code cleanups and efficiency improvements. +* WebSocket server added, although this will still mainly be a client library. +* WebSocket server certified to pass the Autobahn test suite. +* Protocol improvements and corrections - such as handling (redundant) masks for empty fragments. +* 'wscat' command line utility added, which can act as either client or server. + +v0.2.6 - Dec 3rd 2011 +====================== + +* Renamed to 'ws'. Big woop, right -- but easy-websocket really just doesn't cut it anymore! + +v0.2.5 - Dec 3rd 2011 +====================== + + * Rewrote much of the WebSocket parser, to ensure high speed for highly fragmented messages. + * Added a BufferPool, as a start to more efficiently deal with allocations for WebSocket connections. More work to come, in that area. + * Updated the Autobahn report, at http://einaros.github.com/easy-websocket, with comparisons against WebSocket-Node 1.0.2 and Chrome 16. + +v0.2.0 - Nov 25th 2011 +====================== + + * Major rework to make sure all the Autobahn test cases pass. Also updated the internal tests to cover more corner cases. + +v0.1.2 - Nov 14th 2011 +====================== + + * Back and forth, back and forth: now settled on keeping the api (event names, methods) closer to the websocket browser api. This will stick now. + * Started keeping this history record. Better late than never, right? diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/Makefile b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/Makefile new file mode 100644 index 0000000..151aa2b --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/Makefile @@ -0,0 +1,40 @@ +ALL_TESTS = $(shell find test/ -name '*.test.js') +ALL_INTEGRATION = $(shell find test/ -name '*.integration.js') + +all: + node-gyp configure build + +clean: + node-gyp clean + +run-tests: + @./node_modules/.bin/mocha \ + -t 2000 \ + -s 2400 \ + $(TESTFLAGS) \ + $(TESTS) + +run-integrationtests: + @./node_modules/.bin/mocha \ + -t 5000 \ + -s 6000 \ + $(TESTFLAGS) \ + $(TESTS) + +test: + @$(MAKE) NODE_TLS_REJECT_UNAUTHORIZED=0 NODE_PATH=lib TESTS="$(ALL_TESTS)" run-tests + +integrationtest: + @$(MAKE) NODE_TLS_REJECT_UNAUTHORIZED=0 NODE_PATH=lib TESTS="$(ALL_INTEGRATION)" run-integrationtests + +benchmark: + @node bench/sender.benchmark.js + @node bench/parser.benchmark.js + +autobahn: + @NODE_PATH=lib node test/autobahn.js + +autobahn-server: + @NODE_PATH=lib node test/autobahn-server.js + +.PHONY: test diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/README.md b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/README.md new file mode 100644 index 0000000..dd72ffa --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/README.md @@ -0,0 +1,159 @@ +[![Build Status](https://secure.travis-ci.org/einaros/ws.png)](http://travis-ci.org/einaros/ws) + +# ws: a node.js websocket library # + +`ws` is a simple to use websocket implementation, up-to-date against RFC-6455, and [probably the fastest WebSocket library for node.js](http://web.archive.org/web/20130314230536/http://hobbycoding.posterous.com/the-fastest-websocket-module-for-nodejs). + +Passes the quite extensive Autobahn test suite. See http://einaros.github.com/ws for the full reports. + +Comes with a command line utility, `wscat`, which can either act as a server (--listen), or client (--connect); Use it to debug simple websocket services. + +## Protocol support ## + +* **Hixie draft 76** (Old and deprecated, but still in use by Safari and Opera. Added to ws version 0.4.2, but server only. Can be disabled by setting the `disableHixie` option to true.) +* **HyBi drafts 07-12** (Use the option `protocolVersion: 8`, or argument `-p 8` for wscat) +* **HyBi drafts 13-17** (Current default, alternatively option `protocolVersion: 13`, or argument `-p 13` for wscat) + +_See the echo.websocket.org example below for how to use the `protocolVersion` option._ + +## Usage ## + +### Installing ### + +`npm install ws` + +### Sending and receiving text data ### + +```js +var WebSocket = require('ws'); +var ws = new WebSocket('ws://www.host.com/path'); +ws.on('open', function() { + ws.send('something'); +}); +ws.on('message', function(data, flags) { + // flags.binary will be set if a binary data is received + // flags.masked will be set if the data was masked +}); +``` + +### Sending binary data ### + +```js +var WebSocket = require('ws'); +var ws = new WebSocket('ws://www.host.com/path'); +ws.on('open', function() { + var array = new Float32Array(5); + for (var i = 0; i < array.length; ++i) array[i] = i / 2; + ws.send(array, {binary: true, mask: true}); +}); +``` + +Setting `mask`, as done for the send options above, will cause the data to be masked according to the websocket protocol. The same option applies for text data. + +### Server example ### + +```js +var WebSocketServer = require('ws').Server + , wss = new WebSocketServer({port: 8080}); +wss.on('connection', function(ws) { + ws.on('message', function(message) { + console.log('received: %s', message); + }); + ws.send('something'); +}); +``` + +### Error handling best practices ### + +```js +// If the WebSocket is closed before the following send is attempted +ws.send('something'); + +// Errors (both immediate and async write errors) can be detected in an optional callback. +// The callback is also the only way of being notified that data has actually been sent. +ws.send('something', function(error) { + // if error is null, the send has been completed, + // otherwise the error object will indicate what failed. +}); + +// Immediate errors can also be handled with try/catch-blocks, but **note** +// that since sends are inherently asynchronous, socket write failures will *not* +// be captured when this technique is used. +try { + ws.send('something'); +} +catch (e) { + // handle error +} +``` + +### echo.websocket.org demo ### + +```js +var WebSocket = require('ws'); +var ws = new WebSocket('ws://echo.websocket.org/', {protocolVersion: 8, origin: 'http://websocket.org'}); +ws.on('open', function() { + console.log('connected'); + ws.send(Date.now().toString(), {mask: true}); +}); +ws.on('close', function() { + console.log('disconnected'); +}); +ws.on('message', function(data, flags) { + console.log('Roundtrip time: ' + (Date.now() - parseInt(data)) + 'ms', flags); + setTimeout(function() { + ws.send(Date.now().toString(), {mask: true}); + }, 500); +}); +``` + +### wscat against echo.websocket.org ### + + $ npm install -g ws + $ wscat -c ws://echo.websocket.org -p 8 + connected (press CTRL+C to quit) + > hi there + < hi there + > are you a happy parrot? + < are you a happy parrot? + +### Other examples ### + +For a full example with a browser client communicating with a ws server, see the examples folder. + +Note that the usage together with Express 3.0 is quite different from Express 2.x. The difference is expressed in the two different serverstats-examples. + +Otherwise, see the test cases. + +### Running the tests ### + +`make test` + +## API Docs ## + +See the doc/ directory for Node.js-like docs for the ws classes. + +## License ## + +(The MIT License) + +Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bench/parser.benchmark.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bench/parser.benchmark.js new file mode 100644 index 0000000..ff5f737 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bench/parser.benchmark.js @@ -0,0 +1,115 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +/** + * Benchmark dependencies. + */ + +var benchmark = require('benchmark') + , Receiver = require('../').Receiver + , suite = new benchmark.Suite('Receiver'); +require('tinycolor'); +require('./util'); + +/** + * Setup receiver. + */ + +suite.on('start', function () { + receiver = new Receiver(); +}); + +suite.on('cycle', function () { + receiver = new Receiver(); +}); + +/** + * Benchmarks. + */ + +var pingMessage = 'Hello' + , pingPacket1 = getBufferFromHexString('89 ' + (pack(2, 0x80 | pingMessage.length)) + + ' 34 83 a8 68 '+ getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68'))); +suite.add('ping message', function () { + receiver.add(pingPacket1); +}); + +var pingPacket2 = getBufferFromHexString('89 00') +suite.add('ping with no data', function () { + receiver.add(pingPacket2); +}); + +var closePacket = getBufferFromHexString('88 00'); +suite.add('close message', function () { + receiver.add(closePacket); + receiver.endPacket(); +}); + +var maskedTextPacket = getBufferFromHexString('81 93 34 83 a8 68 01 b9 92 52 4f a1 c6 09 59 e6 8a 52 16 e6 cb 00 5b a1 d5'); +suite.add('masked text message', function () { + receiver.add(maskedTextPacket); +}); + +binaryDataPacket = (function() { + var length = 125 + , message = new Buffer(length) + for (var i = 0; i < length; ++i) message[i] = i % 10; + return getBufferFromHexString('82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + + getHexStringFromBuffer(mask(message), '34 83 a8 68')); +})(); +suite.add('binary data (125 bytes)', function () { + try { + receiver.add(binaryDataPacket); + + } + catch(e) {console.log(e)} +}); + +binaryDataPacket2 = (function() { + var length = 65535 + , message = new Buffer(length) + for (var i = 0; i < length; ++i) message[i] = i % 10; + return getBufferFromHexString('82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + + getHexStringFromBuffer(mask(message), '34 83 a8 68')); +})(); +suite.add('binary data (65535 bytes)', function () { + receiver.add(binaryDataPacket2); +}); + +binaryDataPacket3 = (function() { + var length = 200*1024 + , message = new Buffer(length) + for (var i = 0; i < length; ++i) message[i] = i % 10; + return getBufferFromHexString('82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + + getHexStringFromBuffer(mask(message), '34 83 a8 68')); +})(); +suite.add('binary data (200 kB)', function () { + receiver.add(binaryDataPacket3); +}); + +/** + * Output progress. + */ + +suite.on('cycle', function (bench, details) { + console.log('\n ' + suite.name.grey, details.name.white.bold); + console.log(' ' + [ + details.hz.toFixed(2).cyan + ' ops/sec'.grey + , details.count.toString().white + ' times executed'.grey + , 'benchmark took '.grey + details.times.elapsed.toString().white + ' sec.'.grey + , + ].join(', '.grey)); +}); + +/** + * Run/export benchmarks. + */ + +if (!module.parent) { + suite.run(); +} else { + module.exports = suite; +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bench/sender.benchmark.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bench/sender.benchmark.js new file mode 100644 index 0000000..20c171a --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bench/sender.benchmark.js @@ -0,0 +1,66 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +/** + * Benchmark dependencies. + */ + +var benchmark = require('benchmark') + , Sender = require('../').Sender + , suite = new benchmark.Suite('Sender'); +require('tinycolor'); +require('./util'); + +/** + * Setup sender. + */ + +suite.on('start', function () { + sender = new Sender(); + sender._socket = { write: function() {} }; +}); + +suite.on('cycle', function () { + sender = new Sender(); + sender._socket = { write: function() {} }; +}); + +/** + * Benchmarks + */ + +framePacket = new Buffer(200*1024); +framePacket.fill(99); +suite.add('frameAndSend, unmasked (200 kB)', function () { + sender.frameAndSend(0x2, framePacket, true, false); +}); +suite.add('frameAndSend, masked (200 kB)', function () { + sender.frameAndSend(0x2, framePacket, true, true); +}); + +/** + * Output progress. + */ + +suite.on('cycle', function (bench, details) { + console.log('\n ' + suite.name.grey, details.name.white.bold); + console.log(' ' + [ + details.hz.toFixed(2).cyan + ' ops/sec'.grey + , details.count.toString().white + ' times executed'.grey + , 'benchmark took '.grey + details.times.elapsed.toString().white + ' sec.'.grey + , + ].join(', '.grey)); +}); + +/** + * Run/export benchmarks. + */ + +if (!module.parent) { + suite.run(); +} else { + module.exports = suite; +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bench/speed.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bench/speed.js new file mode 100644 index 0000000..3ce6414 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bench/speed.js @@ -0,0 +1,105 @@ +var cluster = require('cluster') + , WebSocket = require('../') + , WebSocketServer = WebSocket.Server + , crypto = require('crypto') + , util = require('util') + , ansi = require('ansi'); +require('tinycolor'); + +function roundPrec(num, prec) { + var mul = Math.pow(10, prec); + return Math.round(num * mul) / mul; +} + +function humanSize(bytes) { + if (bytes >= 1048576) return roundPrec(bytes / 1048576, 2) + ' MB'; + if (bytes >= 1024) return roundPrec(bytes / 1024, 2) + ' kB'; + return roundPrec(bytes, 2) + ' B'; +} + +function generateRandomData(size) { + var buffer = new Buffer(size); + for (var i = 0; i < size; ++i) { + buffer[i] = ~~(Math.random() * 127); + } + return buffer; +} + +if (cluster.isMaster) { + var wss = new WebSocketServer({port: 8181}, function() { + cluster.fork(); + }); + wss.on('connection', function(ws) { + ws.on('message', function(data, flags) { + ws.send(data, {binary: flags&&flags.binary}); + }); + ws.on('close', function() {}); + }); + cluster.on('death', function(worker) { + wss.close(); + }); +} +else { + var cursor = ansi(process.stdout); + + var configs = [ + [true, 10000, 64], + [true, 5000, 16*1024], + [true, 1000, 128*1024], + [true, 100, 1024*1024], + [true, 1, 500*1024*1024], + [false, 10000, 64], + [false, 5000, 16*1024], + [false, 1000, 128*1024], + [false, 100, 1024*1024], + ]; + + var largest = configs[0][1]; + for (var i = 0, l = configs.length; i < l; ++i) { + if (configs[i][2] > largest) largest = configs[i][2]; + } + + console.log('Generating %s of test data ...', humanSize(largest)); + var randomBytes = generateRandomData(largest); + + function roundtrip(useBinary, roundtrips, size, cb) { + var data = randomBytes.slice(0, size); + var prefix = util.format('Running %d roundtrips of %s %s data', roundtrips, humanSize(size), useBinary ? 'binary' : 'text'); + console.log(prefix); + var client = new WebSocket('ws://localhost:' + '8181'); + var dt; + var roundtrip = 0; + function send() { + client.send(data, {binary: useBinary}); + } + client.on('error', function(e) { + console.error(e); + process.exit(); + }); + client.on('open', function() { + dt = Date.now(); + send(); + }); + client.on('message', function(data, flags) { + if (++roundtrip == roundtrips) { + var elapsed = Date.now() - dt; + cursor.up(); + console.log('%s:\t%ss\t%s' + , useBinary ? prefix.green : prefix.cyan + , roundPrec(elapsed / 1000, 1).toString().green.bold + , (humanSize((size * roundtrips) / elapsed * 1000) + '/s').blue.bold); + client.close(); + cb(); + return; + } + process.nextTick(send); + }); + } + + (function run() { + if (configs.length == 0) process.exit(); + var config = configs.shift(); + config.push(run); + roundtrip.apply(null, config); + })(); +} \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bench/util.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bench/util.js new file mode 100644 index 0000000..5f01281 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bench/util.js @@ -0,0 +1,105 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +/** + * Returns a Buffer from a "ff 00 ff"-type hex string. + */ + +getBufferFromHexString = function(byteStr) { + var bytes = byteStr.split(' '); + var buf = new Buffer(bytes.length); + for (var i = 0; i < bytes.length; ++i) { + buf[i] = parseInt(bytes[i], 16); + } + return buf; +} + +/** + * Returns a hex string from a Buffer. + */ + +getHexStringFromBuffer = function(data) { + var s = ''; + for (var i = 0; i < data.length; ++i) { + s += padl(data[i].toString(16), 2, '0') + ' '; + } + return s.trim(); +} + +/** + * Splits a buffer in two parts. + */ + +splitBuffer = function(buffer) { + var b1 = new Buffer(Math.ceil(buffer.length / 2)); + buffer.copy(b1, 0, 0, b1.length); + var b2 = new Buffer(Math.floor(buffer.length / 2)); + buffer.copy(b2, 0, b1.length, b1.length + b2.length); + return [b1, b2]; +} + +/** + * Performs hybi07+ type masking on a hex string or buffer. + */ + +mask = function(buf, maskString) { + if (typeof buf == 'string') buf = new Buffer(buf); + var mask = getBufferFromHexString(maskString || '34 83 a8 68'); + for (var i = 0; i < buf.length; ++i) { + buf[i] ^= mask[i % 4]; + } + return buf; +} + +/** + * Returns a hex string representing the length of a message + */ + +getHybiLengthAsHexString = function(len, masked) { + if (len < 126) { + var buf = new Buffer(1); + buf[0] = (masked ? 0x80 : 0) | len; + } + else if (len < 65536) { + var buf = new Buffer(3); + buf[0] = (masked ? 0x80 : 0) | 126; + getBufferFromHexString(pack(4, len)).copy(buf, 1); + } + else { + var buf = new Buffer(9); + buf[0] = (masked ? 0x80 : 0) | 127; + getBufferFromHexString(pack(16, len)).copy(buf, 1); + } + return getHexStringFromBuffer(buf); +} + +/** + * Unpacks a Buffer into a number. + */ + +unpack = function(buffer) { + var n = 0; + for (var i = 0; i < buffer.length; ++i) { + n = (i == 0) ? buffer[i] : (n * 256) + buffer[i]; + } + return n; +} + +/** + * Returns a hex string, representing a specific byte count 'length', from a number. + */ + +pack = function(length, number) { + return padl(number.toString(16), length, '0').replace(/([0-9a-f][0-9a-f])/gi, '$1 ').trim(); +} + +/** + * Left pads the string 's' to a total length of 'n' with char 'c'. + */ + +padl = function(s, n, c) { + return new Array(1 + n - s.length).join(c) + s; +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bin/wscat b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bin/wscat new file mode 100755 index 0000000..0ec894d --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/bin/wscat @@ -0,0 +1,190 @@ +#!/usr/bin/env node + +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var WebSocket = require('../') + , fs = require('fs') + , program = require('commander') + , util = require('util') + , events = require('events') + , readline = require('readline'); + +/** + * InputReader - processes console input + */ + +function Console() { + this.stdin = process.stdin; + this.stdout = process.stdout; + + this.readlineInterface = readline.createInterface(this.stdin, this.stdout); + + var self = this; + this.readlineInterface.on('line', function(data) { + self.emit('line', data); + }); + this.readlineInterface.on('close', function() { + self.emit('close'); + }); + + this._resetInput = function() { + self.clear(); + } +} +util.inherits(Console, events.EventEmitter); + +Console.Colors = { + Red: '\033[31m', + Green: '\033[32m', + Yellow: '\033[33m', + Blue: '\033[34m', + Default: '\033[39m' +}; + +Console.prototype.prompt = function() { + this.readlineInterface.prompt(); +} + +Console.prototype.print = function(msg, color) { + this.clear(); + color = color || Console.Colors.Default; + this.stdout.write(color + msg + Console.Colors.Default + '\n'); + this.prompt(); +} + +Console.prototype.clear = function() { + this.stdout.write('\033[2K\033[E'); +} + +Console.prototype.pause = function() { + this.stdin.on('keypress', this._resetInput); +} + +Console.prototype.resume = function() { + this.stdin.removeListener('keypress', this._resetInput); +} + +/** + * The actual application + */ + +var version = JSON.parse(fs.readFileSync(__dirname + '/../package.json', 'utf8')).version; +program + .version(version) + .usage('[options] ') + .option('-l, --listen ', 'listen on port') + .option('-c, --connect ', 'connect to a websocket server') + .option('-p, --protocol ', 'optional protocol version') + .option('-o, --origin ', 'optional origin') + .option('--host ', 'optional host') + .option('-s, --subprotocol ', 'optional subprotocol') + .parse(process.argv); + +if (program.listen && program.connect) { + console.error('\033[33merror: use either --listen or --connect\033[39m'); + process.exit(-1); +} +else if (program.listen) { + var wsConsole = new Console(); + wsConsole.pause(); + var options = {}; + if (program.protocol) options.protocolVersion = program.protocol; + if (program.origin) options.origin = program.origin; + if (program.subprotocol) options.protocol = program.subprotocol; + var ws = null; + var wss = new WebSocket.Server({port: program.listen}, function() { + wsConsole.print('listening on port ' + program.listen + ' (press CTRL+C to quit)', Console.Colors.Green); + wsConsole.clear(); + }); + wsConsole.on('close', function() { + if (ws) { + try { + ws.close(); + } + catch (e) {} + } + process.exit(0); + }); + wsConsole.on('line', function(data) { + if (ws) { + ws.send(data, {mask: false}); + wsConsole.prompt(); + } + }); + wss.on('connection', function(newClient) { + if (ws) { + // limit to one client + newClient.terminate(); + return; + }; + ws = newClient; + wsConsole.resume(); + wsConsole.prompt(); + wsConsole.print('client connected', Console.Colors.Green); + ws.on('close', function() { + wsConsole.print('disconnected', Console.Colors.Green); + wsConsole.clear(); + wsConsole.pause(); + ws = null; + }); + ws.on('error', function(code, description) { + wsConsole.print('error: ' + code + (description ? ' ' + description : ''), Console.Colors.Yellow); + }); + ws.on('message', function(data, flags) { + wsConsole.print('< ' + data, Console.Colors.Blue); + }); + }); + wss.on('error', function(error) { + wsConsole.print('error: ' + error.toString(), Console.Colors.Yellow); + process.exit(-1); + }); +} +else if (program.connect) { + var wsConsole = new Console(); + var options = {}; + if (program.protocol) options.protocolVersion = program.protocol; + if (program.origin) options.origin = program.origin; + if (program.subprotocol) options.protocol = program.subprotocol; + if (program.host) options.host = program.host; + var ws = new WebSocket(program.connect, options); + ws.on('open', function() { + wsConsole.print('connected (press CTRL+C to quit)', Console.Colors.Green); + wsConsole.on('line', function(data) { + ws.send(data, {mask: true}); + wsConsole.prompt(); + }); + }); + ws.on('close', function() { + wsConsole.print('disconnected', Console.Colors.Green); + wsConsole.clear(); + process.exit(); + }); + ws.on('error', function(code, description) { + wsConsole.print('error: ' + code + (description ? ' ' + description : ''), Console.Colors.Yellow); + process.exit(-1); + }); + ws.on('message', function(data, flags) { + wsConsole.print('< ' + data, Console.Colors.Blue); + }); + wsConsole.on('close', function() { + if (ws) { + try { + ws.close(); + } + catch(e) {} + process.exit(); + } + }); +} +else { + console.error('\033[33merror: use either --listen or --connect\033[39m'); + process.exit(-1); +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/binding.gyp b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/binding.gyp new file mode 100644 index 0000000..c9b4d1a --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/binding.gyp @@ -0,0 +1,16 @@ +{ + 'targets': [ + { + 'target_name': 'validation', + 'include_dirs': ["> $(depfile) +# Add extra rules as in (2). +# We remove slashes and replace spaces with new lines; +# remove blank lines; +# delete the first line and append a colon to the remaining lines. +sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\ + grep -v '^$$' |\ + sed -e 1d -e 's|$$|:|' \ + >> $(depfile) +rm $(depfile).raw +endef + +# Command definitions: +# - cmd_foo is the actual command to run; +# - quiet_cmd_foo is the brief-output summary of the command. + +quiet_cmd_cc = CC($(TOOLSET)) $@ +cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_cxx = CXX($(TOOLSET)) $@ +cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_objc = CXX($(TOOLSET)) $@ +cmd_objc = $(CC.$(TOOLSET)) $(GYP_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< + +quiet_cmd_objcxx = CXX($(TOOLSET)) $@ +cmd_objcxx = $(CXX.$(TOOLSET)) $(GYP_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< + +# Commands for precompiled header files. +quiet_cmd_pch_c = CXX($(TOOLSET)) $@ +cmd_pch_c = $(CC.$(TOOLSET)) $(GYP_PCH_CFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< +quiet_cmd_pch_cc = CXX($(TOOLSET)) $@ +cmd_pch_cc = $(CC.$(TOOLSET)) $(GYP_PCH_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< +quiet_cmd_pch_m = CXX($(TOOLSET)) $@ +cmd_pch_m = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< +quiet_cmd_pch_mm = CXX($(TOOLSET)) $@ +cmd_pch_mm = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< + +# gyp-mac-tool is written next to the root Makefile by gyp. +# Use $(4) for the command, since $(2) and $(3) are used as flag by do_cmd +# already. +quiet_cmd_mac_tool = MACTOOL $(4) $< +cmd_mac_tool = ./gyp-mac-tool $(4) $< "$@" + +quiet_cmd_mac_package_framework = PACKAGE FRAMEWORK $@ +cmd_mac_package_framework = ./gyp-mac-tool package-framework "$@" $(4) + +quiet_cmd_infoplist = INFOPLIST $@ +cmd_infoplist = $(CC.$(TOOLSET)) -E -P -Wno-trigraphs -x c $(INFOPLIST_DEFINES) "$<" -o "$@" + +quiet_cmd_touch = TOUCH $@ +cmd_touch = touch $@ + +quiet_cmd_copy = COPY $@ +# send stderr to /dev/null to ignore messages when linking directories. +cmd_copy = rm -rf "$@" && cp -af "$<" "$@" + +quiet_cmd_alink = LIBTOOL-STATIC $@ +cmd_alink = rm -f $@ && ./gyp-mac-tool filter-libtool libtool $(GYP_LIBTOOLFLAGS) -static -o $@ $(filter %.o,$^) + +quiet_cmd_link = LINK($(TOOLSET)) $@ +cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) + +# TODO(thakis): Find out and document the difference between shared_library and +# loadable_module on mac. +quiet_cmd_solink = SOLINK($(TOOLSET)) $@ +cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) + +# TODO(thakis): The solink_module rule is likely wrong. Xcode seems to pass +# -bundle -single_module here (for osmesa.so). +quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ +cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS) + + +# Define an escape_quotes function to escape single quotes. +# This allows us to handle quotes properly as long as we always use +# use single quotes and escape_quotes. +escape_quotes = $(subst ','\'',$(1)) +# This comment is here just to include a ' to unconfuse syntax highlighting. +# Define an escape_vars function to escape '$' variable syntax. +# This allows us to read/write command lines with shell variables (e.g. +# $LD_LIBRARY_PATH), without triggering make substitution. +escape_vars = $(subst $$,$$$$,$(1)) +# Helper that expands to a shell command to echo a string exactly as it is in +# make. This uses printf instead of echo because printf's behaviour with respect +# to escape sequences is more portable than echo's across different shells +# (e.g., dash, bash). +exact_echo = printf '%s\n' '$(call escape_quotes,$(1))' + +# Helper to compare the command we're about to run against the command +# we logged the last time we ran the command. Produces an empty +# string (false) when the commands match. +# Tricky point: Make has no string-equality test function. +# The kernel uses the following, but it seems like it would have false +# positives, where one string reordered its arguments. +# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ +# $(filter-out $(cmd_$@), $(cmd_$(1)))) +# We instead substitute each for the empty string into the other, and +# say they're equal if both substitutions produce the empty string. +# .d files contain ? instead of spaces, take that into account. +command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\ + $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1)))) + +# Helper that is non-empty when a prerequisite changes. +# Normally make does this implicitly, but we force rules to always run +# so we can check their command lines. +# $? -- new prerequisites +# $| -- order-only dependencies +prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?)) + +# Helper that executes all postbuilds until one fails. +define do_postbuilds + @E=0;\ + for p in $(POSTBUILDS); do\ + eval $$p;\ + E=$$?;\ + if [ $$E -ne 0 ]; then\ + break;\ + fi;\ + done;\ + if [ $$E -ne 0 ]; then\ + rm -rf "$@";\ + exit $$E;\ + fi +endef + +# do_cmd: run a command via the above cmd_foo names, if necessary. +# Should always run for a given target to handle command-line changes. +# Second argument, if non-zero, makes it do asm/C/C++ dependency munging. +# Third argument, if non-zero, makes it do POSTBUILDS processing. +# Note: We intentionally do NOT call dirx for depfile, since it contains ? for +# spaces already and dirx strips the ? characters. +define do_cmd +$(if $(or $(command_changed),$(prereq_changed)), + @$(call exact_echo, $($(quiet)cmd_$(1))) + @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))" + $(if $(findstring flock,$(word 2,$(cmd_$1))), + @$(cmd_$(1)) + @echo " $(quiet_cmd_$(1)): Finished", + @$(cmd_$(1)) + ) + @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile) + @$(if $(2),$(fixup_dep)) + $(if $(and $(3), $(POSTBUILDS)), + $(call do_postbuilds) + ) +) +endef + +# Declare the "all" target first so it is the default, +# even though we don't have the deps yet. +.PHONY: all +all: + +# make looks for ways to re-generate included makefiles, but in our case, we +# don't have a direct way. Explicitly telling make that it has nothing to do +# for them makes it go faster. +%.d: ; + +# Use FORCE_DO_CMD to force a target to run. Should be coupled with +# do_cmd. +.PHONY: FORCE_DO_CMD +FORCE_DO_CMD: + +TOOLSET := target +# Suffix rules, putting all outputs into $(obj). +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + + +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,bufferutil.target.mk)))),) + include bufferutil.target.mk +endif +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,validation.target.mk)))),) + include validation.target.mk +endif + +quiet_cmd_regen_makefile = ACTION Regenerating $@ +cmd_regen_makefile = /usr/local/lib/node_modules/npm/node_modules/node-gyp/gyp/gyp -fmake --ignore-environment "--toplevel-dir=." -I/Users/joshteng/Desktop/chat_with_node/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/config.gypi -I/usr/local/lib/node_modules/npm/node_modules/node-gyp/addon.gypi -I/Users/joshteng/.node-gyp/0.10.15/common.gypi "--depth=." "-Goutput_dir=." "--generator-output=build" "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/Users/joshteng/.node-gyp/0.10.15" "-Dmodule_root_dir=/Users/joshteng/Desktop/chat_with_node/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws" binding.gyp +Makefile: $(srcdir)/../../../../../../../../.node-gyp/0.10.15/common.gypi $(srcdir)/build/config.gypi $(srcdir)/binding.gyp $(srcdir)/../../../../../../../../../../usr/local/lib/node_modules/npm/node_modules/node-gyp/addon.gypi + $(call do_cmd,regen_makefile) + +# "all" is a concatenation of the "all" targets from all the included +# sub-makefiles. This is just here to clarify. +all: + +# Add in dependency-tracking rules. $(all_deps) is the list of every single +# target in our tree. Only consider the ones with .d (dependency) info: +d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d)) +ifneq ($(d_files),) + include $(d_files) +endif diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/.deps/Release/bufferutil.node.d b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/.deps/Release/bufferutil.node.d new file mode 100644 index 0000000..8b19131 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/.deps/Release/bufferutil.node.d @@ -0,0 +1 @@ +cmd_Release/bufferutil.node := ./gyp-mac-tool flock ./Release/linker.lock c++ -shared -Wl,-search_paths_first -mmacosx-version-min=10.5 -arch x86_64 -L./Release -install_name @rpath/bufferutil.node -o Release/bufferutil.node Release/obj.target/bufferutil/src/bufferutil.o -undefined dynamic_lookup diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/.deps/Release/obj.target/bufferutil/src/bufferutil.o.d b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/.deps/Release/obj.target/bufferutil/src/bufferutil.o.d new file mode 100644 index 0000000..800f484 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/.deps/Release/obj.target/bufferutil/src/bufferutil.o.d @@ -0,0 +1,23 @@ +cmd_Release/obj.target/bufferutil/src/bufferutil.o := c++ '-D_DARWIN_USE_64_BIT_INODE=1' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-DBUILDING_NODE_EXTENSION' -I/Users/joshteng/.node-gyp/0.10.15/src -I/Users/joshteng/.node-gyp/0.10.15/deps/uv/include -I/Users/joshteng/.node-gyp/0.10.15/deps/v8/include -I/Users/joshteng/Desktop/chat_with_node/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan -Os -gdwarf-2 -mmacosx-version-min=10.5 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -fno-rtti -fno-exceptions -fno-threadsafe-statics -fno-strict-aliasing -MMD -MF ./Release/.deps/Release/obj.target/bufferutil/src/bufferutil.o.d.raw -c -o Release/obj.target/bufferutil/src/bufferutil.o ../src/bufferutil.cc +Release/obj.target/bufferutil/src/bufferutil.o: ../src/bufferutil.cc \ + /Users/joshteng/.node-gyp/0.10.15/deps/v8/include/v8.h \ + /Users/joshteng/.node-gyp/0.10.15/deps/v8/include/v8stdint.h \ + /Users/joshteng/.node-gyp/0.10.15/src/node.h \ + /Users/joshteng/.node-gyp/0.10.15/deps/uv/include/uv.h \ + /Users/joshteng/.node-gyp/0.10.15/deps/uv/include/uv-private/uv-unix.h \ + /Users/joshteng/.node-gyp/0.10.15/deps/uv/include/uv-private/ngx-queue.h \ + /Users/joshteng/.node-gyp/0.10.15/deps/uv/include/uv-private/uv-darwin.h \ + /Users/joshteng/.node-gyp/0.10.15/src/node_object_wrap.h \ + /Users/joshteng/.node-gyp/0.10.15/src/node_buffer.h \ + /Users/joshteng/Desktop/chat_with_node/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/nan.h +../src/bufferutil.cc: +/Users/joshteng/.node-gyp/0.10.15/deps/v8/include/v8.h: +/Users/joshteng/.node-gyp/0.10.15/deps/v8/include/v8stdint.h: +/Users/joshteng/.node-gyp/0.10.15/src/node.h: +/Users/joshteng/.node-gyp/0.10.15/deps/uv/include/uv.h: +/Users/joshteng/.node-gyp/0.10.15/deps/uv/include/uv-private/uv-unix.h: +/Users/joshteng/.node-gyp/0.10.15/deps/uv/include/uv-private/ngx-queue.h: +/Users/joshteng/.node-gyp/0.10.15/deps/uv/include/uv-private/uv-darwin.h: +/Users/joshteng/.node-gyp/0.10.15/src/node_object_wrap.h: +/Users/joshteng/.node-gyp/0.10.15/src/node_buffer.h: +/Users/joshteng/Desktop/chat_with_node/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/nan.h: diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/.deps/Release/obj.target/validation/src/validation.o.d b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/.deps/Release/obj.target/validation/src/validation.o.d new file mode 100644 index 0000000..072715d --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/.deps/Release/obj.target/validation/src/validation.o.d @@ -0,0 +1,23 @@ +cmd_Release/obj.target/validation/src/validation.o := c++ '-D_DARWIN_USE_64_BIT_INODE=1' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-DBUILDING_NODE_EXTENSION' -I/Users/joshteng/.node-gyp/0.10.15/src -I/Users/joshteng/.node-gyp/0.10.15/deps/uv/include -I/Users/joshteng/.node-gyp/0.10.15/deps/v8/include -I/Users/joshteng/Desktop/chat_with_node/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan -Os -gdwarf-2 -mmacosx-version-min=10.5 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -fno-rtti -fno-exceptions -fno-threadsafe-statics -fno-strict-aliasing -MMD -MF ./Release/.deps/Release/obj.target/validation/src/validation.o.d.raw -c -o Release/obj.target/validation/src/validation.o ../src/validation.cc +Release/obj.target/validation/src/validation.o: ../src/validation.cc \ + /Users/joshteng/.node-gyp/0.10.15/deps/v8/include/v8.h \ + /Users/joshteng/.node-gyp/0.10.15/deps/v8/include/v8stdint.h \ + /Users/joshteng/.node-gyp/0.10.15/src/node.h \ + /Users/joshteng/.node-gyp/0.10.15/deps/uv/include/uv.h \ + /Users/joshteng/.node-gyp/0.10.15/deps/uv/include/uv-private/uv-unix.h \ + /Users/joshteng/.node-gyp/0.10.15/deps/uv/include/uv-private/ngx-queue.h \ + /Users/joshteng/.node-gyp/0.10.15/deps/uv/include/uv-private/uv-darwin.h \ + /Users/joshteng/.node-gyp/0.10.15/src/node_object_wrap.h \ + /Users/joshteng/.node-gyp/0.10.15/src/node_buffer.h \ + /Users/joshteng/Desktop/chat_with_node/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/nan.h +../src/validation.cc: +/Users/joshteng/.node-gyp/0.10.15/deps/v8/include/v8.h: +/Users/joshteng/.node-gyp/0.10.15/deps/v8/include/v8stdint.h: +/Users/joshteng/.node-gyp/0.10.15/src/node.h: +/Users/joshteng/.node-gyp/0.10.15/deps/uv/include/uv.h: +/Users/joshteng/.node-gyp/0.10.15/deps/uv/include/uv-private/uv-unix.h: +/Users/joshteng/.node-gyp/0.10.15/deps/uv/include/uv-private/ngx-queue.h: +/Users/joshteng/.node-gyp/0.10.15/deps/uv/include/uv-private/uv-darwin.h: +/Users/joshteng/.node-gyp/0.10.15/src/node_object_wrap.h: +/Users/joshteng/.node-gyp/0.10.15/src/node_buffer.h: +/Users/joshteng/Desktop/chat_with_node/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/nan.h: diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/.deps/Release/validation.node.d b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/.deps/Release/validation.node.d new file mode 100644 index 0000000..869c91e --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/.deps/Release/validation.node.d @@ -0,0 +1 @@ +cmd_Release/validation.node := ./gyp-mac-tool flock ./Release/linker.lock c++ -shared -Wl,-search_paths_first -mmacosx-version-min=10.5 -arch x86_64 -L./Release -install_name @rpath/validation.node -o Release/validation.node Release/obj.target/validation/src/validation.o -undefined dynamic_lookup diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/bufferutil.node b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/bufferutil.node new file mode 100755 index 0000000000000000000000000000000000000000..5cb239c8e32b272f546009ecfbd9480f23cf3828 GIT binary patch literal 18340 zcmeHPeRNbsmVcdu5RhP0bQ~8oI^g1}Nje|c8O2GPGZ3$a-9lpUmvc*covSIxb|@vG;eY zZY3`-1ax+eXaDFrRNecly7$(tTlGFtubNjr{qT#)n&!yYG_4T17`bbbruB5415VSv zi99Dy({z2YXW3#=HMS@n3pK{Dslkk}NfNql8tYBsWU||vaki}AIz>{#=yo&}XkDX# z>?O=(TVkZjZtuYhWz!ohLD(@Ot!YJUkH$<%+SA*ONT(4Oc#t79VRHmAamct_w#)dN{f=Ch%DQ`e#bb9c(~hhU18?8}0ha zc%Z|OP4@WtFP8OvJV?T{MkY!Wo~;;*t~aaT^g`j|B9@hK{yaMDH}?nQT+@N?65eRBM!@dGI3ZdZVv-fv3TzuGtEfDP_v| z@oz{>ezD7gR=7OP+f6SKrRu_i@O( zbp7@}&>WQ@Ym=rzVXB6%#hj9?T*5AJleO|>B3>Q|w^2@*q2N_lmCY@~k_)$?8EKqe z{*$P6jL~b^kT8vovKvpUr}bdzjMZBe4C;w8r&Ue60@lfI7vfe5dL?6uV=m<)kc&Vr z0=Wp}B9MzfE&{m-d_56xw=||N{;%WNth*&U`z^ekoqeg6o&6@5CEnC;-K{O&?qj9i z)Mt(9Hyh!2CvtRa4PjXZ)%_OrOdFvCo|hX)XP8}`V?5^t)=+nXrzb{{PArUo)EQa6k#D3b}N_7htq z*f&N(sUq3p_on{Cm-@n+Iw?o95Q4tc07fI?n86=2WcoEw*l8q5j0|>)FeMnu&=pV^ zv4+{4gW2?^Hy_H}50&nXCEAukZ*P6k2a_^+!iRy(xuT1M)!d*ZdN>>RmKj>CJmG<><-Xy@dko>zz*lepK{5kojkBacr6H zO?mpLRm5=$q0B~>>J?PP@-DCx%Q{>m6w7L0ECRBgw`D5pA4d=0VclDVzyT7-9}&nv z;1U+N95m(GO#%fY0!Jb6C+uX=f^tB>oqDk`^?v4aR8jBy#B{x%c@~&1sNNNQCtdSTX?b@yIcHFHmD=3Z}hp!Svcac_2C zlj9R_dfEt}3jGDaUIjC_|E_7`5JKWp8 zQF+eRlFS9@3RAs@r}`(@G2p5GsbFcUUAMcJyI06d8}s>ynAV!bf)w8kBImOnrY4?G z9p=W)=c}TFfy}RDpEU46uoSBg5e&lHM3bkF6miKk^{y=Pbsrf1peXYIS`bAF@cm#h z89n`G*yGH4oFAgHA@zq$i%=fO+%2eB37uf?={MtKyAHfNb&%LwW%XT6!4m{$kV0=$ z-_7akYq9CV2`a9zjl_(Pd@u~lP<+eSc++a|%DB5pHj@uX`cDfR)$ z%j7KTEql(^*xRstC#*T+K^={^TuA;}z&hwuC1Q8%t_v!{^T@o&s)ixh`aFv zcypHbx}u~@EQzEaP3CHfJ!xTv?x68Z@%0vBNLhao?g)dKZ;_+wVF{Pg(xr=}OWVn% zwdB&j$pJ-W^JgNu#og4rj+!5o&9}(rw`FrDHLs`U&1e>@c*W3dU}PyVYvakI-dryf6%)6PT)B{Z@J*8F*&6OSQ6Ft>TlN~+>^4u!w z@M+onm~8$~%x3X3)Z9+Z_sQmaWb@0i`B7>PQ*#W>A{rtF-rh-b$%*cMJe%{C_cfWH zK@^i#MKzhd0K|v~?0yCmpGlb>LId?MK}N`XdVIKL--~45-=V%~ODpq76g(4yU`Ja2k-J*O(lzpQ77g7FHl+TKCk0_rL<@2K4E6RRR?i1w! zQPT4`JNu9*4~y~_qI_ADgQ7elN_v;b&VF5#Z;0|uQNAt8cSU(rl)o0`Z$$Y!QPTTJ zcJ}W@c?>1qE`Ny|!O#Q$oyEK0#V--RRq`(qze(~h5Z@{Je3nc#o;)g#GzI~tgW0LZz6t%!1dc?SW`0 zV(4YQgr}p^+yJAp2K_`$BOVPz=7)_)s6LjAqQ}x3N-MObMqo{SAQEW{1lN#DNuzY$ zyi%CG<_5N=T_#Z6SeekJ1nf5(fp~)vFx%0`k|^4=%f@$ZsiJNmDvXGJ7eQr<9J?6v zsJ7KK?qnK$`oe8Sn}bY=C~nA<91i@uOv`i7_ry9>P=1L1T0yEqCcSC+>RBhhsuW%X zReYzIbfH7b+v(5>Zp_#6+Y7Zky5@0ZJLqF6?v{==yzt_$D#xqKCOHC*e8)Lw6_6|1 z3@VTucP-K!>q{IZ(JP<3(~NRRraPHF&a{u|^Gpvh zeU0f+rpK5LGcCY{NvVYCOr~>~Rx_<*+Q@Vn(-6~6rkj{ZqT}*c~-NW<%(?OEDSgf${9UH)OgovbXWGSdE7KiJcQW0@^a-YY zO!qMDXL^X~Ak#OP9%Y(gdYtJnQyi@oeikt;VTye_QD4e*4$}&zE~fO=n8xX4>Swx) z>35mZ-dzFY=xjUw6?QRx;F1}I)rTEjj^?-X2J>Ifd)RTrF?FV^&#_7F$YAS53n8mS z->|Is9^jSv4sA-s%ta&vvsxJnwGS{G{45K;3Ownk1St0Y)IK8NW)xOT_S$$~p$KJ1E3EX@zNFH(V$wgCf1mIL>np7EUDEv#|E-wxZ)W|A3gq|| zR{C^bqC~hEg%y+jr>yT{eT9|&CZRp5Z^fj)_ChJJi1ig#`qp?+|5i-;U92BreT9|2 zHD08z@V$^PW#5SWz=of)VX_yZg3|%CUjnRtm-Plmy3?;Qebn)ab{FBYm*-?ZUw8-BuuU$^1u z*ci9k`<4y2+whNU_<#+6bJ|#Y*VwSfhTCj-lMVlaz+G@rriX3#hf*GQeVKk@!+W6F zqG{9nK=G5T>HVOspvRdW0$m0?%rpafEAUa!+du;_+lCxO4j~)JE0I?rwAMr%!{|A?6!~`K<;a_nw;zeBo#|nJS zEfxA)`N`hn@!&UzWA&<%vh6vR+LyR@-8jO@`SX3;D=&O=Bz=h`<-8?wmV684a4@Sah+`(HRMt2Hf&^)-Kll z3xfXnBr@u{Q?F^I(+v=Sk7%;^_+^}=BO>GuyJij8+$G|K$e%TKWp?yfh&`;;O*nmW z3fp$MnkG7%GJ&9oq_bl5yhSw~y0FbI!ZkX(U*SXxmLN{Bs52-d6KCztS!q3IBKmb! z_;F+=jNe*DO);G#^W$GTZf!L(3g6i7yv>*D|oS+sw#P7&i}p&LSIU$ zvD)oQ_3BczH;jag8mi^ZOg$Saop_LpKL+Osk90BaapH8|WSHt1!}kTUJYI0FFPbm| z(V(GvJDs$6P}1|s@^~8ibQ<$;YN^ucd4+qVUfU906%CllI6cOc-<7Q{XQeN3QzX_F zh|q2J451|Xs)lf)6H%8+UqYd#DG*=di{epjpop4ZLgiZJ)5w9oFQMd;9^uskw{RnF zAf#v3ay13k7^fMzf31K1T7=K`$Qg}!OO0*~XKg*+t>ULKs6t=8IF2_8`DDVv;6>|n zono2Ug(_>X9Q`q|-hI(A3Z5`O0Y^<%U5k}S;a_ir{Bc}2aWiZrJnQA5PIo*W*g(fe z$GK7kHP340F8f!7o;?$+KC#$z=e69)2Dgd-=AzOGdJ4kQfGFp=7gwgag1M}gia_T=P|mj1Hsb+37io&^|%?;^bVsV K*hznsA^k5Q@x^HX literal 0 HcmV?d00001 diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/linker.lock b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/linker.lock new file mode 100644 index 0000000..e69de29 diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/obj.target/bufferutil/src/bufferutil.o b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/obj.target/bufferutil/src/bufferutil.o new file mode 100644 index 0000000000000000000000000000000000000000..e56c8c01712eb2a75e6325b5e2afd0758cf354c1 GIT binary patch literal 81768 zcmd4433yaR);C`Fb~@>Fhmf!+29N|nq6nlroe%_pkaS2OKp!Z_o;;;xMPx{u?6D>CjnD(<*~;x40(j`ICZEqCkg1mExbKmRvR>h`U()u~gb z_PY1J`16;)r7)(s7%m9(`%N+~*$DVM3Skq)6a4K-Auh%;AcX!@oT3!^5D4 zl}PATZoxHsv6Y+O$CuYy+q5Lo79;Q3dOQEy z%k6Q*;>uf5TgS8P)bD>Pud$&iLMbPb_id8UOX;`2x26<}t6yVt9cOULd#FLksqu>6 z=UT+?Pmp)3xQ#2XrF~I!bxWkpDKA6%(o(5k_L)M6iE zuLA;c1qs35;P1q*y4s8fAnF_PYt_$@-!BB`X;Q$>?-`l^#)`5v=E1K|!qrbWT-(yp z7zsDku0U;z_2-N)1?4xyjKA!I;&&C~T_kR8Dk^!+i zmB;mKsjYL$8zSYck^Dg^FL$WMk_9ZTJYG3~cgnj)%DY3>w>zc0d64&DTzRTJr52$+ zM&1lf6zr?iLEta!5%JvN8XG4-4*eoa!;4!{8UpF`zg)NAzf*D*rP=)$<-1gHn!@|x z)-yt53-P-rps}rK!Vjt>VHSQV9D{HYLN>zh{hM9h`APAd>hcX=WS4jTQn{u5rOM79 zQ8ySX?|h&C8u1NJW}i}g>cS_|(KU!%2%99a364~$pVRagaCIlShT$jaTU_*6NhDB@ zx<9~==04z|>pXN+n)~m_BMQDAM$&Yqr_hbA>zd~Bl$R2UC6}W*X!_T#)W=5vcMOqt za?lU1)DDshkard!%MkRRT%$`tuW6nI^vk?0fNmtrzg;g<@|xx&3dYhd1JtA6-Xn}u zY5_vBkmX9>M$~tch{b?vtS1LN0Dy$}h?=Dnzy2hk8J&n;f|y|NQ93O1b^u-@l9!c6 zEu|!`*Si2}=w9{L4T&r>1%O-v?jhlX5Y(CZ0M-!VEjyz3sRXIgN=Us!@v**H zpv)ucX-QAu2l?_AD$sNV^8Gg$nD;aw-bVG(tDX7z129)3=<}SII{^KXFt;b|fe^!P zzIRFF5qF{un}^Eg(N9h~(ubQT0j6T_Hx3bfL6W9t5EIdPc9Q^>LiBn#Nz-%*IjRki zn+Upu=utKG82aU)cL{pmtCV!s9bou?m>Qj^{*!@c0|ui%PSW+Hfz(lH12yWN2lgf) z^A5+)Tm=2zqz_3e!yVkG1FCs9;rchi|2?T!$cpDVUXPx@qhFeoN^0`l?jhQ1RBpE? z0l$yu_UL~BlM8x%d(yXn=z4|=sd0)Q1jPto7ZJtvNjlO31u|r%NTKxf8@a`VROY=3 zpoa|O&)mSx^806s&A1BP$qkj6fNeogTmH297MntA)6OX7UY zt!kg(6X+VC+YxlP9nHKO0lkGV9=EI#K1(kCrm$%S*0ey@yTDFBv6;x~wkVnR_^#B~=BWjjT{Gtb# zjGD`P0DP1K1JP_Fg1AfwZ!A z3_^4UX(xJs%OH#Q0HmR_2Poy*r4Wtm0gP;Ml#fb`3H(v01sWI3vn}x4D&8I7=T1Ic zQ$@_}Ve278T77u`BRdTtV>;!*)Of%}J*lDC>K+)y4I8)`S8d>WT=jvQ3B{5Z^rAd{ zo6=(rZUS%z{g)JE&6Pq3{reOs>RCov^AVDH8vx!ysDGvWHvoig<~jFX0w^DW{E(sr zZJ2w3d4+g>Oi3DH!@L3L4}|&2fnkIG2&umk>Sp(#>v5mf8}~ch18VS_q(9;w*dI4+ z;4!#r1M>(ua6F+{vab(7`b){F`-cEZ1DwAA!Mn&;lEb7dl)haky#ttMiFr@5QtfWS z_=t{q-vWx4#(T+MqXhw@Y)`%ff;UoolOGTgMYTww#Ptu8sfuwyEGrWbUVAwB#W_{#h%zF&5vbIPHAEn1^wMC?6t1Tjxx9vKo-G78xCZ7No zTPB}##jRsn4)GB+ODBF&CMP4x%S52BLr=gmp8?ulCI`8=US9xVmWfCsv&WIDOh`Dn ztf0;u4umKZJHlEfBH4eFR@N3xh|VDGcqye_30cYue*<-Fj$LI=FXRv(QL}X77dc*s zs>*XL(0@U1!!jQL+MeScTwJexK$tleX=L`s)GgNyfqO_ejsrsE*p9H~SR~sE z;%zw=qBBT4;g?rJ7WYfi(CL>)a_zhk&`7U351ka%dY&JfR|#5Evimozf@;bAF}Kw8 z*HTl-$h`4+2yia#jnogMwEJlCnL?!cTdCAk(n$zF+L1JS(<5YMkO1L+FAk*qH@Z`ksS~EY=_)0s$*tr> zc`kngA{*p-vw;wuQXYUj7>P0-i-aCNd zMs0InBQVmaq_W=5je0pJ&3Xe6ZqyEoQPchdsPbKs5bL`)aCWQja)^(pSvv6x-!<$l z(A=z<8Am&P_YN+u*JvP2-xXB8~)o`p7;yHOxjwsPM4(eS;Cpcka7 z(a-?oMPpf?0Xl_7V>M{6G!KXJ>5(rFUA}J|T~Y(Ml+#^1rlmgK& zS7?oVKhx#aOkCcl%K^HO2$uCOY9fzh1!Zo`A~c> z^^C&sG^pT~W?2i#! zSlA#M*%*?dyfNe`-=%OT=3NTBYzzrNjUmsLt;ZCyY~wH?;}-JM3=+&6N%vkz(~|#1 zSN&;APr6;D@=WS*4E$6oVnOP55|Tnp`t#i4cXQQQ+97~cyYv8HvF*~soYLAZafpwo zSvv8HcFB)KdDaDbHKHuDFD4oGK6Mut*K0fwX1gTP$gBrYv`ZwMyhu=Io(F_zm+S~@ zyCjl*jAX5_+m?%U~<_&;g9uZaOB$o)kCPLC4&A{vYK%2C2rsM!;uB( zzcqSRmyp#LGv&rVDl>>5PSSqddWA|+2b zpsj>)J29I8y_qn{DUNjR0dx;xQc~vAJiwH;57K#$>`6ID`9h`Q3t=f_NIgwdXF_0( z2?+0zdk|F3F-HTcJf9@Qdj7vTyVdhK#7ERDo%n_4>u5l@=L>WjdIOeO3AEkwUFD=r zuP6|v=ZiElF9lF|zRZkAP-p%f2;up5gw^v!vL1-H^~geW_Pdb4*Gq5j0Seej^H>-% zNO@6J^y6exM;UN7b-jp}r4Ws5>Wo}-l(08AXxW`Q$Yyo%%_16g=|My3t1#PfwZy?yM*Wr z(oS>_-{(VgvJVRY9(|&g%9}hI!|y{HdKoYNDO^YI-~7_jE43<&L4cB@g)f2N%kt|i z6uiDy}70df~XFSJ0tj|1{OK`+uqmkLwV z2&FWrpNF7d>cor#G(wmw9GI*|K(8Upb~6p%R>)=Ey8!-}P&aD?0Nx`K;@x}yDabc> zKyJI72z1DS<@EEDzk|6vWI!rtUKuO~EakeU5RG0$8>@8BrkHnPZIj;|wp3;}l^ zuh%RA;sBkGBeEviOF*QTwH#1O35cXDB`_MQTS`E3)dOlN0Ri=10;r<|nnejb3yh-# zB<4TBI7&cZvW6AdNT`7O3`BVnOY~=D64b@|wt(69C%T9p_YpN%)CDXTLqDN zryc8*G7mJI?s7XFNs$R%4w5@a)Q!3t9$d_=<-HrwrwMbL1CzBI&|gOba~B^Lae5(+ zB_9G#(?_R!XO3d50YR@wSF@yJx!|l8K>v#{Gt#NZCG-kF{z=eNOei0tFz-hIbE7q< ztLm|uOhwC0qN(b84~p((D&F>VS#*5|6*4vsL0@HnCL>x;pmhqA)r{!P1lnjxmRmXN zZvcNps7)q{d4B=qXv+NNbRx6lU=*Nbgt^c{pH&ISB7$COfqIt!vW=iuq^pKc*rD$& zfIdl>s}&~e1w^^MZ!|@Se0$wR&;~)jDP0wxwCAya&Lzx!7A8uIc{>2UjZhD%Bvc{1 z0O&DrF8v7$6Zgpe)Q$J2y@LL4z;XN25;IGF0Aol^f6imfm%U%8soRSlHODdLmp(oh z@ZJg`uOR6E^vIswlOsIiZ1nkiXk_uWhwRFsK7*Zk_W{@Ec;Mgn$jP|k917@M!hC4q zfRMrT4TL$m5@}>^08ossNI3bCpw7G<2r;^{BdntaUDXyM@f50`B zC4YbfG<~<*IC11Xp{HD(c-Gj@t`31n%r|+9O$$cMd$S^Z(Lu-;FuA*VKLbr513JCs zpy6^X!p4FqLWJ+RRr_J4ihWFl-s?b9TFm3=*@)bWz4|2m6-QlKtChQQOT;&u-fkjpvsN_dXO=_ z7iiJ2y$OH*4M884EH}ZGF1^N~_!0CG&J>FQjSwc_;s}2>%eMQ4@fk>d3i3*8sJR(A9C# zHv;uCq376zGw%UF`%MI9z5|mL1avN8PBX26Y6!^r1Z{AZ&1HaoNSM>@l0;7;YRb33 z=N5yeHLjYYfjXVgZE-{}?*^bQC-fRSnt87S^lyY&Z+A}|cms4VVa`jIqcQ23S>FJ9 z%p_p8n)&nXLO}~}@XiPBe8O*Yrgkf!ZxZHD$wY6s5%c~C=n2#SU+Az-Ru!Pz33G)d zH9k?gd5DqZb!d^_LC~*pCYgjrZY+X+t=)5(w-(SV2(vwzBw3Vs8c^C!)vt3X;~fR) zRKncqOlJn}d*7H@=M5_aN|mu>6oJ6|ZNqTq1io`8i=a zxkUB>gRGsfjZ2l!Fp%;kvd0K2$h=blj3Vfd%VZ_FEVBzpJfWYEX0oH-1&Y?B^xg5e zS=wOW)cPB#7dv=(nUuDj@!$|2QL}X77lQ{Ho$|@IK$G=YW&zOl!Nc2JT(48;#|>V7 zMpmSeNmDs7c#xLz26^*0Be8_oMY_~ler&d$VK%QmkS0uO@J zzPcuKFg12Bt$NqQj2A=PV-WP`U21(n@(hL_jziF2aLKa*DxESw z!-RR!bswq;RYA#f2B3c;%uB9^?3mjC-A$O+ojk7tdXO-CoHo#=U{;Qxzv-$$b8JcH zctFbu^Oj4UERY(^2DFthZ@Xx0V&Pc}=w*a?#}&0py8+N=3G=>P13B_~1*k)W-WwO4 zHkGlF2>M47?NZj50O$h3eC)(51@t_^eB#9X1vO zEUh2Fs#l=>oY-FBH$d&Z0*8n`E1md7ub>XYc?6n|iQ;t{0?j*(c&Ba!nh*Tq9l{W3 z+UAS9BP7s#10&u;9RmF#m|5oW(A?f(dEjK&Fq#2`*7-{h>KBgp&(Bze<7_A8kLf=x1+^aNm|oPw-E6vkOzL1FD1 zMGku)C$_#(5SmNx$r~V$cYah(&d#qFH$)22$j;9wT#gdw9aH{Hc+vTFAU&Qnfj^Sc z7yJV6;3rdI$^m|!^vWt~dYf&w2U93O1+Gh98A&?{0w z>z^Us$K@SBaq2%`F?L@|fJSdT9?%-XylNax<`_B5n}z(Wpe+A0MJ?M&#weitr67vHv{8aUu#;Iz7Ng?a# zAM(B4W6AnHvXHotEyU>*zRPpWPQ*u&ecnmIl^v4}Rvv#h zf?b!)^vSt8k*)bAHS$2#r1m3b^B)J|1)UsnFpx*BQ zb?g{@B|Uy-%#M-7)BxkyF%p=pwZK^R?1W}w$LMaL96LsWX~?I*@S*fX?R!cZe;&d4 z6QazvePo4OcbY_QEIUeMXFdQQdeJFZ6+rXD_m8RdICftI<@qE{u(V+pEv9?kln;IO zuqkQ({-~prsJEUz6{6TswgBBJrKDQUI4SWiXK4OmQ_`~>E8*Kh)LYN;lycXiWjpHx z3=XlnuL;ziyErO&8$@U8k7zv>_ro?$5Bctz>u)XA*vVoVFFpy@3EiF^)7I z^^Wnv8ESGGNijB1qSnU(fOp?8h=U%x#V+pX@@GrMb%HgrDNKBkJ$3El)%ju-gQEr(4>Bbf8C=Wlf0BFSHQ zHD;q8gCKqcAM&9FE5msSWogtsvZ3^l%?qhYnQw(M zBgZ2x{^SUi^D>~xsH&VlCc#3=Q5t_zX5^yZM?dsj&=(o1KJb|(vs~cNn?SIXN>ue< z!YsB%l=o#r-#T2%+^f)tT?z}+g0F1E-V%-2P#OpOje^=E3M!X|G;0bNTSyEd_YY+( z;Lg&I~%kC7(u#(=s-km;KI^6K%k~M zG&qf&=DfsPQtQqchxqtVHl6szG{+d}3iLO~G0VIF=+{v2=FZt$TwJevfiUM1B8|+q z0Tgoy5{_2NBdGJV-+&R534xIa>lT@iVIA@dMc${TG=sDgohymDXo51yTi=s`+0dLt z*nOY^P}zuE06fn_s}7&mpHqh}3SBR3eNs`#d`8qjmP_NyEu*o( zW8yBiWyFuG74>4$xV|;b?U}D#IRJEvf}d+w=fRw zTi|?XOji?5dM59)ST3!Z&jRYtChBF;&gRlc{taaH4pZV4+GC>3Xcn;&xMxk=`AQR7 zZC?(N`%ICq%hYJfQVce|)X2a(#Wn=VJq+B@Chk(191Fs2SdfNx`jHwgn(HVyT5vo7 z!IObyB`H=s97@#Ophz?rz!k8u$d$GIr&;E|vTat%3x-_hj2MVDX!umyAp(h@3PEiI^^ zDD}Vkt34q+DQ6x4dvU7=f)|sjQN&$zS3q|~H0I$?VEa}h%7SZg9U}lV){xK(iHU|3 z!CP<_i`AXpTcAzCUH0%@!S;z+ykSAmTjs~!RDuR$+i6{%sBubjE@jW8G zR_2=ki;GpWV67d^rCHEt0Ue|xT`sy5Tu)IthY+M^EX0M5ce&^id>q&Uy0~anEch}J zQ$B+{N+`y0Jw-MxQv+S63jmu7uu|L(WPJm!MKVq6M~Ms ziKHpZE}rijiI*;V3Azf8mUA!eXW~}R4L(HdRPln;6S(LSe4Xg|g-2=j%Xcmcz9wSc zR*{KZ+)geGzzXORr1B4Tp!{5PS1^EJ)Xt6J6J@ca7#t30(qCqPXl~AE+@DRikEU(M zPp<3nv2*b|j6^&^E2?9|h|;Xp^(3bC+Sp}?(qmArr!cG6#@CTg4TQ=x~7-J`ez&0IHG%cFyy7L2qfrr|kAT3zA z=o0)41O;?gKzG5^DKZ!CW5?5dpIKP*bmtzq6SCBMWyZdY2(h>)apluNLs@e-L3ur$ zSlq|c(DheJoJ|-+fKl=-G$d3adJPBSG(v{xMKqjREIss!07{Ex?&$`GP233JHAFsF zzM;kAgEN?^Mc13UYb#Vv3XVlpJz!l%89HtsV(Xl49Itq5I&DePlF8fy>LD2e=R-Cli~( zA0_d6TLudqU5Rm#1k6&5Q_4>OSX%?m(0Yha1R+H*qhc$-^MS9p0ykGv5=W0W2@6#c zmm!HSiy_9DL=~r3;0i^5BU)FuGL_6ZFc?>Z4_VyPg?*-wm0tx)*|7FAWy`7b_8oUC zIsD^LXc#S&xLc_lN^X$y6>;VJglmgpNUd+H3JfHJw^IV8W09RFCG~Ui*WFQ?3h*8o z{aArlVK_q`aUay#Db;BHT&c4UCKN@xn5+Jc2a4==kn!?Vz?US_y`=P7l-DaV`lbRe zE%`>?_L8aa;&TGs%S5umVq;QeAu2MLbS_Q_ausDh$o{98Yq#_68uQumwBfU{Pz3r(;?>q+?Pfr22N~Ak)dX)|@$v`Wr zU>7T#KSa7*Dx=#KIKa)Qz*kDJer*OToe%$0bhj${{Ln#&xkE-DR^TA7!wUR>60;Rz z-UPj(drHxx=U~NKqz!(MQ1!-1ZO%rtuEOTR2EV`tGad)P`o#-%FUP z8>Q?oB!fyroAV(UG^=oWX%=RIV3;@ss44_esgLLOr#|i`8m>^)Wx<}n2Mg7ufcik* zJBtM<+@hpOsnqoHUhY3&qLtUAi)e9rY(F%?BrWM;^kc4^{t%Q4P*UC4qw&*# zXwoJ6F}bd>g@{s$Ntf!!#^r``Et?alc+a8)HX)H4fh>b}=E~c)41o0h$UKN0! zMNCTxMmUzaS;{-q74u27_Lj>P!ho{ZQa19~p z0y}&vU~34z#ST9oum=eKM?3rp!2U(>|FXlAP%(!iByF|B^8lMm@NG7DI#uK(JSFH7 z4ljne+%tRC0kxUXe>Tzad~@c#XlmZC0AUDQwi8%BRlc{V2Jo$jq}i0>34vkg-;?GJ zgVVxesXpIol1vpOGD~pb^uW_}HwM1Iq^$T8kW1 z(5M#`hc{U@r(k@m^)Z^PYAp;1)nTolQH3u-npEAjp~6hMtY(h@{we7*!Vdoeu)YwZ zO|ZiU09Hcq8XLT(oM3%qJEM_qo{vcb)|=D=27eO5rjUNW0jpkleC>xKv&sVfxCOrY zKtL5%SRhMSU}E4CvQ>YYI-tT*HjYP)zT#8CqsGG8oT-P1ZUi+d9ycZv+2~<~q){Zv zwe5j*l}k~Anx9<2we8MzvzsZJPm_#uz%iSa__YFxK19(oDH97!U5bmhA%+4*uqy0lQ?wH#C2x={2O55c-`I z=G!NPYXO@Kaw=OYwMd0c{|4~sJ8_x42-;~yqwr_k+uA_^>|+mi{(o#ZpA{V(!Yn`|gLQAH46C(X%_2cy+-KOsTzMQSZ4!53RT;&Jxu$KqG{;so-=Vid{r(|{1ZSWLdC zZz~tRS3*^CFDQiXO{VAqm|OT>xz+c|$dA?mC!B4P>1?H@vxSI=7ip)|=`#7NO)BU6 zm@ARkf@R~;F>zga{ii#vq-+Ijdl@+p7qC+@zNQ3}3N|7Tq8_GOZt6S|5GvaW0Y{Lt zgs7N)k&L~SRmwQQ7*0UVM}tJHR0J~@@-qssPT6GOw}Y~*8aGc73+zq3Sufao`-eQv z1}oKMb3c^qO@Af#NQxsQg|PiFVf(5;64l1sg=DK^aSr2YNqs>6YPq4}c;G&v?=dt` z)bA?lKB4@QSg`x}Q)k2h>lbBAULGILRS&&f%$QQ-dzHj-<$MdQkVrViFA|e3GpHTE zkniJRia}&iOqsMkWD4w?4UwoBF{t7qpjyGz z_?Zx+X58AGGeOKJW>U;}%(Q3h1+ZO`o*{p=J~ui@F9vBSA8E&uS|vt7hQF#xsly^E zQ0b!0VWhv6<);)Wd9kEjVkl8wOvL@U%-qwYNDkxkM`aAY_{x_X`O+dEc!}*H@}lte z3hdxYLB3K;>&$#f zlP_WO4H>?x6nqhke5Z-;i|{RxAngK(wO#5H`ECf`?+DV0a*!{gQ=7oFOl@d-Z?`Kw zQ_D#1pZ>u*Y>enb@Cy0yKA#V&9Lp+tqa_%lxzbPcjRnN5133;<$=DD}_xS*IWAA6A zAaIXMn~)X&8HQ99y@MdbCTm6SA}_(bBenA;rgK5=ir$LSN0gYF7~(+LqX!bH=uNT( zYr2&0Q(QgmfI6gk}?8&Xy2Wn3IG>C!~>)bkxuB77s#-6O=RBOLL+{kR4Mj)y>p;X%oHm&}Wiq^kABf zVk_~Xg7<=wr30D2H{H(j%|}|Hftod176g{)CtJu+t;^kj3rQ7pOC(clsiFll( zr&Rf7TNH!1Ii_MFgL6&504P3>RGghG%r)P{ff(46r7-v+W$P4EHt{YXThYxzX+PYa zYRNJz8y=}m=H)RFIQC@{T3z-Kwa3~o={;yA4P}P@mT4nLj%sVIW20ubMOxcNEo*LD z8jUn988x!0xjr&t$?BF-c_aP!FBnxHX=xkPQ8cQdsjjiTK5`fwB$q8}U%WWd+8%9a z99dUKDRvZ%Tnc3vC4f7ZxC;w93jm+niPS~ID_d(@1e7JSRPc#E*H<|I)g#&K$#J_j zxU~%)iFO!dr1BoiUv8RvF++TD0p-9k>2;~u#?Np}x) zZN{ugbEmjISnF8~fXj7n&g%di;o5uZdLF+fXTAGqSI&9>U9QJY-LXE&1u?E&>_K;W zviEy;>i56^P^pg;$)Cj8{*yg#i90_6T()atcwSPH+wJPJ!`<_HxBDDU@NxDy5L^j@ zqh03-f_vP(fW8e^4n1SNJI9mf_Re#=w@n4-8%x~oY2ZEDb!pC4P`F(CPkntoNq)=K z;UTS|Mylsyp22pW$iD1(?MjQwkO*b))9m$sb?3jvlek=J7TrOK684+y9pE6fvRzk7 zj*kr3AlFqA_Mrjm@48w_X(1_Y_eWsPb!C1B&3d_7rXV)N)oZ=md(fRT&+Xmn&b`d- zo>WnZ9C>oCcDq;dES)8@baq%|$yMvg1}Ac~$d;o&=BOMEa(#cQu!#DlQOGeXWgBX>~&qBJp;?TUH@7;Zxn%C=WDOKN4TyZG>?P-8GevMw`tF~3tT$~ zO##Ku(>`{Ob3Hd`3a7Xxyw&pt$sF!^-JJ!$oeBoG>mWlCZr9dzL0=n z!h>_%?pHi-c-|0}xndnmlalP6k_|EQ=x!?|4v|kVrRL8p%K0~~+X{Jwu4l5x@%a5= zoV!bQr5pJ)JpQu|kWY$=UO3<6(dMZ zD9$oT)a^?KD8_&>Q~p@%JS@4NHl)ZIvzZY!@9!cZMUtZ??kpUXx8qEZDN@|2^#GGA zCI@P%z$A%LFH0t1lHib^{jW%pOfqzdkrI)pXkrqzDsi4c5~HqKgWPM7%vQITeIj(y zXl82vi&^`NC5c(&izS&X6EK$S5|d0fbirv{`(MoRU91$fPS-__)PmfAPla^xWjI?( zPe~o#G;~>D!z1#EM158tTiv?Ul6kDbV>S?CNz`^g7gBo51o{~XRjFIGOX6Z3JJvbvr-=R_`E-`S z)cpoS7S?g>e~Rr43!Az>+h99Y-I|iZ>Q-q*2|E9#NeK%~Nlp@lQsUxNyn4xPlEf71 zCP{L!kx&eY+U&U1Ac-k=l@)cS3jLqDRmY*?^f~GXl{#t_CkG1Eg_M)_3(HKAzi|>$ zB&L*@CR81Sy2_AJqi(-5Krt;_%(@(l>DknBoUwElOQKL}O%T>ox9im+UTn{%NQ`yK z{}GAeR%e2cwz^g4fHW5466(~EI&%dsb^DpAu{!jm&Njs;i&^V>ckJZz7%Mj6N)VvMUr+PVW#SnXcA3-^d;#| z{7!0aZj976F*?*+*4|VXZD?+)iL7X8ti>rUI@McV+f?5e8P`#C;)x~AP4wC8@r;gO zSKxk0q?MmArFn38MRR>SK3R+J>bBQK868QkZdlS(8*OimFgnCsTvr!qYpcQMj~Sht zuNL3mr4za{_%xZ(#`h#X)t-&1HlqXQmCbdvjXZ5Zqcj`O=wNVlU28*2bVh5#l7=Qm ztKcGu=GF@Oj5cj~T9mJ9ZCKF|ZRm)MXE!6w(5h&pwW+qTxV5!*HCKvGv{qpOp`i_{ z2T@C+EsGMBCcj$mm$O@B`L?d>f_A=rIT7E{vXy;3duIpq53n${#%iVF8o*#xpueVF4-& z(IEv>X)bm=qi0u4py3p>$6yg5iD~acK&$IedW=rOI$Td0lOD5(@eW33@y*0c^U%RN zq2*jGIWan4?+|O4n4a^nI|Tpaf<;%u=V&Ly0=GCRorx1QmeJvPt}_Y&D%01ivnwm- zOv~<|D%ISS9UK|R&aG?2_v1$d0|g`VMh1$G%g$X?+ZL(M2Ab36j|{YRG#!VPYg*77 zWnXu5F7K5_+LlL~TSnC_t;G^sLv(4FnxRpawy14XTXWs=NOWXFa{};)y2b|7Bs+Fx z8(Ut{)=Vv6MN@rb6>GpZ#~aWjHqI0*1FDSP^6$R4IUJ#M&d2VGV8J zy5=U)9ER&^8ymxH5gJ<-K6(1oKu6ISqZ$`ha*j|36F0FG4XZ*8jH-EUw7K<^g_KlS zRBcUAx<+bG&2?+=01me~%VN6AvtcaXM z-rH#)MKs!+M7H0ybVyy*TW}&3u?vE_s4@8gPQvf(cqB9%?ji}qt(^j;f_6t3mTv4= ze}R!3OSWa6;^p2SR9Umoiwq_r7o}w(w*nmM zG$|Cgis{v1B`Fj_v4&Q(L|1do`5loXvAEO)B^XSRG5Bwnf?J{>Q+0Yo*GklrQz+Ww z*;<*K8qo<@x&mvKpko2=B8XlP*kp0zi%VTg(jsBLL;Ym~L4e)7mGZ(xp{Ba#`i7<@A=V`|3wN|cTZbeT$HL)`=%U6j zsKQa29IWkvwhNTauz|wFl%q=<+FF`<2PW_Ah0+yoZFOkYRMZ%wGM>cBBN%DTo<4Q@ zjJeapq3IXM2IGpfwzw0%KBYr@A9;*9Bxy|^60^YbhM zxG&hr?J2||Fv}($((ZSG(W(gI&<5)+uD&DC&x#yE5*I*KUt7B}*f9%VX<{f~qwg z%NR>o!x$u}K?nJ~K-orEvJE%KVFr;guu?gXL&HZ@UEujB%A7V^RI?40j5Onqlw<;; zvG50LnoA=Xi8RXGbTB(Wm6S0$G6vT*&E_bayh+6JtI_pZS_HFMLc@?BM$|t#X2mDO zz{M~Yim+Lc+WHwyjjLHH21uc%_7(i5G|~vakF!)H4Q@$$S%SpPu@W`1G*ZeBh>?=1 zd{J35#t8qj94J=haZ)Afuo7tmTpa3Uc38jG+AcD`yw=HY_)G{Bj5a%Y zp|HLqMGJ>49a~xW{oEjNS!F+kQM-l4o+ek(VF{&&1MK&sz|;~`LM5xpreoMcGYKPg zx)<{c^jIN`ivaKklq6b$4TY-8I;uy*YYW2_)j{gpio>Dm0`A87f|3UESjVG;6M420 z$;8r&it{-wOG9PFvny*@ab;z=qOk4Ml8Js=ZV*8h(x;XGZal1U4|k_&|@gHqr)(@qO2HG zYemhoZmS`v7u2y=)X)r@x=p^Rz*j7I=HiM;*z15=b`pM?pv_p>q#7zqi94YVKM4q@ zh&Izq%kU8qb&|qgk$@yc^|bWeMjJpKk3nP?5GX2ZXsWNk0%&bh9TrjzvXZ5kxH}pe zf1z3fR7>_DYTmjKAQLN6^l>^)vx!x0m#FEXgpG~G1*uJl=Ydj$Wm!nJ+vb2ol62vJ zk|KfoNi6q1A>5uz3|#HSAsIsEeurT52SqPx*obBYXr{~^Jv%;Lew{M9u6wc6U05NO zIDtW82`#oUD`G5J{JLZg0pnyJwah7pPrQ^P!+x-$ib*ew<@n#+r>ffEAFaAlV@ro> zo%Eepq|=(zWyh_zrh#J;7;kom#^$iaALGzN9X{1D>sMU8yQ+|J8M<=J*t~~AiF`92 zt;Ic^t`VSyiW=;C)q0DJwPLJfuhm8)3$W?rW}}v1z=c@?hFjAy+^XQ?vg$EmE?SJ< zMpuY!dlWRN95}TC9|xy8xe&1+32OM`<$~FO=xu1oV}pT_V5(TfDzeNg7KEDHF*lb! zSSXO(@9OQa*l~uSwNoV+X$TKjrr_cmEDPJxM6q48;s}MDRY5GG+b2be+D|JFWv$IC zX3N!P45}OuskPz|8rfHV?K;Pi8ADFBMoBd6%h${J1%|AwtJ|UbUECX=F(#SRazmYVi&-( z`F`>5sM^aCS7MroTjy7(uJP;fG6%-2#SV;FhapPV;rI&Ua1Zvq&3wpu?10Le?0^b` zI)KWg4siEI4I+^e?41`IJI38Mm)xZ0?mAyLq2?;C8054AXN>676X6%t)-6v^zJ6>w zwJtB_8|=m!B5gZ4feH9|!!PG>V!=!#*M?GEWR^Ac0b*E!CR+8>)~>*xCxpdZTN0|S4o?nYE-G%T&D-+gY6UF~RfaGrl{8aB^MNss za`DPk9jR-_=Gtn*gtDAt;Gz|CRvmv4H57CjtE?f?SYOiIj=Nu`vhl;16Gb5s}5?Y%n zIjIt5x1ow!4pS#X)$x|&ewHr4f=7~$k(`-)*WG}Xu8WSqN|vB2u##9uX)&eer*%VR za4}O>G4evnNRa|KCkS2R8>pvNbriQo<(A%Uh($k)Gdgx+vuz+TjuknqF^ID~R^>*c z4p1d^3R27OeDRnFVzM{a5Rk{GuM z3p=4PynHoRP?a<{wy$W4<+j3O_<5-c{t7xF1?^QVmX`7+u;PfRm0lh?ZI%1t;U*X` z%#3IwG8S%LytpkAwLt5*$_CsjTcz#~SfT!a+IW|?wMG-L6e$`q?hLL16psbQXAk5l zN@2$s0ah@Hu)138z){r@CuR3TN5vU$>}b@Gu3HTYSr?6lg(^XtVWF@z8I~GsArJ=y zu!C#1khYw_T;^QP!pBXQc=1peIxeTP;kGvKvE6}R4XFOFhEbAPmTH_-3e^IMT6%&D zIzb7!F!!sts1ge+ zg0S+2`ud2u_K8zzvB1O#GKR{;^>XsT*)~J&xeko1m_fxPj>U|8_d>?3YqbEqXA`9YGy$YnXh*j)lxwpbpS8>w9$s_MXYJl~Ln21fgj>0Ah_ z7=lKYs^5{YBK|$fDxPjtRfG;jv3Si@i=RKmG=?9v=Gl{nuIav;Z1@Xk)rK0amIy9< zvB3c$2{gw`WC=fxzuN`a;@z_2NsZ|^DPD>?wUow2W~@6_JS8BrD{i{WE6xAtX&^j3 z!pAI__e5zBVuy8?sy}F_ht75YtH1*?{L}!i?(_s7p0YUHr zWyDEW2=`V?k2TH9BhJo8H6DEF!$#rXl?^J zcnY|sLC1d`$Y5v{W=6o_$AJpasjnIB(HV=UMOHMo;zVj)q=omrwC5ief;w#) zVIT$L-a$k@$xlq#M+!Eh65h#%a+L?I#?QOiUlz zFn8*jz$p*UKP42#CCP{6x+8MP4f3NV^w85ZJkMOaB!W|T2}L-DJ?3binqdCq)L{sb z3KWW^A!jE{#bC~`5VNl#@uThjJi*Hgz_EbKAEL(UE&sI?+~FjrCRS$Qi>fVoQNvfX zG~leY-Bo_Sx>Ana5Zy|T(T;L%Q!sy4dlOt8S35kwR|hMMO=n@81fFkZM;g(1{8>z% z_^B_o3Z&K`;*Dfur`cVPY2(p%F}jUMo?x6CkCcyirS-(Dz&}R(@|6hnv=_`72dc~~ z8H3cwtA}Ix(}Q+^gS^O&m5ZUVKqp(AJt2O~6P=YhN=GLrZDiOra)KOUse5s-*!|nA z4NgkCqyVk3S2Z_KFG~%bLnuGNnE>k$M0?LtP(_nNOjQT9HSw`_G5#QR6XvXJ zCnlX;N+1tXFj;Kmt0QV1p-WjY)DDRODN(L77&4cUBE;-KIM|D<3aP=SA;eyGmde}} zmpz4i@}ihbbaUp>lG5RD+fxkC5$Bi`6B{`mwL@Y7^2>{?vblMAdkgm>8bI*snGoj? zG8%-kFcOGKgtZIukFIHNZJNfyU3N3p-=E2ai?Q5d7RoBJ;S zjd01#0I36QaZuItKy?E0csR5+38-T+WsS|XPU=#AZqOlu+giX>E_jSg;54b(Zpn@T z1zXW%W8)@G9 zlI9p|#%_<>+Svqq;x?Kx+1|HkZ(9R1 zL^|i|#s)pDFN>)H-hM*EExb+*4S;b^hBj&NHjdfo1o``8)U9PLPT&k93b~`XEXjft z@>Q9(OSK12xUOiR7&bSH_Im_3;PfK~phu>QJv*F~&ot>Np?8-;1UwEitD|X$D1U zjXFsEBFBo-5_1sd@r)`uGh!zz44vMN*BZsNB22*T#8_d$%hu|miFmA72WR2T_J%sV zUdsBOCMU?j;+J^&$|g(9L6|R5ay6)f+2Ri3f-=1L$*zY3;$X-VXsLx6agYTB#?ZkU zwbpuGAcp%{PSdE_b60g<(R5Me6-j~`uh6Q(leflEB67N|{)ccd5|SgsSZVeLk&QRLvE{_U+gOJ8DNsISL_9()BIfJwPTJ0(|AmcCX z5nUVY^D&WH*twf$@BEybrQLUd@V$gg-EJHk;vGB3@wd2z*CtHuFt!$r^nSa*GmNmcSxd^`ZO!Y8^({u$s_>RHnW78Q`8rQh&7|cRh5b)o)B>n*HT~no{hef!O zjw1A%hbyr5YNh7yI|{Qp$sA^jTO$$vZb<&%i2+~Q(1dftDu0FI6`OwhX+B$jWl7S> zCLRuU+ETUIq4?xg=vJH<^~Qk17Nr=tu(k3%p^i+6#@>+^%gU&O9CO1a94n}8#7myK z@(+{BnG2rainvfbSDgS+w}M_|;SX1J@V7HLl;R@sJO^@N_AxPr_4_9^G}X4QuEuLb zBlNZm2e}37)DDmQ&`a(Tahk{&MnAoOs4$p_(}H!fj-j{JCE_!1v0Qa+frMNN=j19X zkE}|>q)<*CWlr%@zeMH~ulaM77g)Jf60(}um{dhcB;w>H5jRb4&O~fv&IDXASwb#_ zb1F&ZOvI#6P9DD>n+f%NWv!CG%f;cH7Hk)+le%P`6k8|od_!%$Si4J@tU|}6Yhg~9 zuwagtGPh9T3|rB$DVx~1DO=Go%=sLvauuf~u;II8Pn>wdeCN?(tz4vwQ)>)lOu9ij zBUpx)n23fvj^1_xr+{Et14h%aNfJ1QwxqdbbsSm(s~1i?Dm9MEnsk;`13>t=!9D0mfK2IFfG!$Bwmxv;iS8jx^Q;WirTj2akLi*Cq3*Mmzuy#_L}^C z_@>%U5nc3(dDc-AG`01##yF+KPczfF6E{jL0aM_A!o*WDJ9uOM+Fd&FSQ^CJP{XXH zHPR7oimbw*iQhKTtNz6sxEt}N&-$=jco1(}m#@zi5s^I_ThV6@IE*&K+9J-mx;nE@ zFY~c%8S{s1Y1^z#d?UdzBeKfHL;HLgY{p`d7`>#)0g+a)spAju-L{Be;>UT4Fh1nZ z%c{{ivy%K?A^tpihpA8;?=Lg%4JyX;KSolo)$W+ zC)F$p8jL0W1X(aC0#*vsTI$6GU0Kwi>B^oaku@QUvUY14iXFZ&6SG*dBi5orU1mqo zbeSE4NMuK}5PT)kzQpHP+#F+IyFOoLrR!p))k>CNwbe?ZR$S41#I*_Ppp`rnul^Y2 z#638MJc>63;>lZnK$b57(2n`ENOWm)eFgPqM!Np^&-8~v9W`N02gUPhrhizb*%-*6 zK3e8`{4lXdgO`B0+^Uv5jiZ0rYVp&Q9G36!64m%Hdt$}&dpct41MsMk#ulaU9qROK z6oGgYhV|p}8(5Rqu(vrH>B{+yN)+2TI+j|xl9!k?rcNa$HFKDl#>}UsJS}+;CbW(b zmEmnLDaHyb&5PwNl`7fjByY@eS7}0olpM<;#m68@NR+5ptc`!QCd#7pQp^te?g?8I zS=@{dJ=HG8z=Xf3Q+_p>4_*0+vs&F^&5M??24bU^cvHZ(a(0%Dxp;8r{$fG z`*(Acq1}cjvrj2rP<(1}c;ORUc~0i=T$FeIf?T|$=pMyKz@(nLCgOv0&~Vp8kNBl4 z^v1`r@U!8ujPg!T`Q|oH`G%)G<(<1k=44!nEAM>5yxmjIl{j_b6Uvg+Rpp(pR&@SW z-ua_7H@lD7t75|;Et-X^tKD<{R6Va; zKah-=-tIkrs+L!-o$t%@@T{QFo`KHgolnc0ISidE4D>|#7Va$N(!aLw(tB_YbQ64W z(Ul4~#4Wrxl++R}ak8H&vmG0nytt0nq|8 zwM|u8@%cvvGB;&#?NHPYbXC+vRIwL|svW<$|F6r_;q_m_7|IY-s-n2P(>5>mRBU+i zrvt)8VMS$N6voD#(T0%!P{{u!fTf*ZCH919tCC&Z`Lwz{XJ_<`X>~uSX!+)8cTU+d zZVddtvQvE5b(;2L@u8HWMt`3 zl?N??5NNdU@l~RaMT*?TZFW|6jb5eDyj2pGp}bKq@{17L`9AL(Ok8>APvx6edCE5) zil!c#n9-Fv=PB`q%yKFga8BgH$7#_yn@ciKo=u0I=`6{Jriiqj;AF9-XVE0SHI}FZ z!OC39E^=3iM+iY5yD&lb z;kTu2T02om#Qn6Sm`1eIS7+P#>OG%-Fud?{NyPd|S~L64= zkd9m)HeaWhdtCPT>^%En1U_-gKIx=vh#fIrN@F=ZCXQ@y3dlE(p&$)$G9o87aSMpf zJb;u7;px(O3+tf|liMnq3j6=tzw+whavJ^)MVN|!59IPcf@v(7-zVWp&YOZj<60V% z(@=%(@!4MfVRd|4md6Q3Z6&@B%xJiZ@3=C2kd|d5pmJWcGI;JCog^S@|PoT+z~x{ zcc9PXNfS?g^Ra&(mv+X3H@&`T=eJ9azU9joMjkuw+C9gQJod}yf4O4$C1W;TvgVCj z-ah;2JN{L_a{GlNN2P7@z1HI&fi;h8Y&&-8M>lNiam%VB@^}6Hua8aq@1ys<$VT6@ z^qTdbQ``x>7ci`amyXs!ZzwL*bt0tbexpDmccdj|Z zGiu7UcRe3IZBNBTuhfiub>y*+&72=OZ_`cgQ-8Vtt-%8~`R<-{Y1w;u*Y%qDX;-`bXfS z_Nvw2zHrZ+*Y3WhNBF)!u5mA!kp9CtUtRL&)PDpfxX)R$yXDEtU-)Uuh==!Gu%>b0 zPibi`*dAdbLKVVk2xlQ|MYsmxPJ}%O`w@OZNJC){KsW(mB0?2H3&L3le@3_-;U0u% z5Z*!f0^vUh8E}vwLOH@bgcgLe5Vj&*kMK`~j}Z_ym@C%y%~dI9uCpuelih3~(D%AsSZcEdErUPJg|y1<8KP(5L6H#sN% z`pgpXMUuY;`Ncm=#J?pX=odo{{cTz#;*F<@c!tCWrMxxxdOBtA?->5fn9Eq!e8xUP z@ZuZS?WZtyJ_7yuKu3Cwq2VR|BB*E-9|Ya$Z!{wK<~R#NMo{wH0a~KDMDpBHFXChm z`g;@~Hzu9~u*D-%&KF3Rcv|Qi+xRPoEXwbKg^X=RxDR13LWa!Gg_8cN8cxn;NSU`l zW|NdzgYU{)bv+BT6u)}5;MoX&BRyt-XRp+~5_TdOrAg`0-%Th};u|1sdx=b2@kCHo z#Ip%ixf(P&b%6dCOzLb*(TM{4H>k;V(A3JRK2J(yQ#2R7Wp-A^O zsb?6!*1TLV{pA^H^BZM8{(`g~m+6fMLn>sQjWT~7;YN&x_97IZ+?x?@LGVCsg-qjL zi$ogAhlWTWnkvh&BOvszfbOqJ{U4FG`BvI^5cGnX*&^V{H?8{Ii6I8z$Zk|_WII)M zuL04W{kA5sO%!R7U7v!U`=qWlzccU8*NeQX`g@`DFKb?h0%rA#Zza#oGRLKgKfw+$y74>%5qlq;!#-!s$KX7HYIxUVfrhP^dG}t=&uHGvIW(9 zRc@!iH;JcG`o&rqcgy%wsPml1Ql|$vsITW9(Ch zubk$Dm(^ z6QMW4kqDHhEcrW7{u1nHd7p#dAqYbeh9Tr45dJv)(pW7IA%GA>C_osEFa}{PLMg&z z1h^s}KUU(Giifg`V&`(uWHEAUxDET}xL%_c1;4sxR==k^pSpY3?H7LcQvXx57utV5 z`{y-Dx4)5g{!r#)DheMZW+ zFYMU-HaO|CoKsN&o(5@XRyQW`26dUExK+ z{ohnvJz>|`savj^diBEn)tTSla5T%mHt<^Wjk|uXO1*hFjki8}WcPcY_t^3NsG7fg z{`R9=p1c0Ki?1I1r%x8VH+A~vyo}(bG|n76c}n`#duL^S^x(s9-&inX^8?#+-_6PD zY|WZJvw7ARDRsNw@{g>`{>z$G1;eW^`{luxPXF0^rpc2te)$y&pT_AFQs}}d&Q`JPyN?db$2}j=Xo39D+CSgR3C(42qz*;LpTkg4dGmb zOA&S;Jc{rN!Y2qnA^1>_jz;h!lp@SUScdQi1RCRAhj1UlZiIIbzDCgDoP7~;5ym1+ zN2o=h@!oj|mm&NWVJE_?2%jSS8zBvi!61YHLI{Dze9IBmB5Xsr9^rn3e;~YzK;u1^ zhp{6NjzbuSFau!`LOa4Hgv$|bM|ce3p9uR9en#kl%9D+dk5Gm%AE6Op9m1awZa{bd z;aPAnKy9Wr+1*rZ~ybrAhQ7wbg64YO& zxUYu+IuayAHM&$Z1GpUw1_D*0igja=rSqmMN?M4Y(X^)ZMe+#*=RzEv82KPHNmS0^cXHO#Wy9@b%h4#GFX$So`7FS zs?oUu)P^~!1g%8VNK`{%BnkQeQ3CBBVt^`-5ui&=sxi{gHD!jqwo4_ZLscm&C~e;p zg(pEb!M90#XQ{#XgOnA8ESc>qp(0V8XO_@Svb4`NsrL31RCraTVti3jC7V8xixC0I zYCv-=#iyZHAka7zu>@(^0@UBMJqa!X{`(J@2P@c)y53N0cQUeB63OWS>-3PnL zY#$+wa}@dx30fd!U5qLrK|_ZL#`#c6f-aJIq&h6UaJ{Sw+u=cyO3k=Zrx>7R(!U-b zVSw(Fw%-cd%WQuj9W%+KN|(j)GdxIAT{T$fwGJxF6u*FTQyzN)B&mMxFF;?JjQVi` zbk0!*hI7Vg++hg zm{Ky{nk_*2W{SVTxry;*WM5L1$QJWn6w45jgf>t*z&tp)1eGTV#txt*O7Ucqu}zkCGLn(9c1l@4nvDOax3dqjsyNg504+@9YgUj@%0)1g5Q{+u z5eB0O%BKqvn+so~QMS-yl(Ku5)ZiV0Cnq?`~GLWojmlrm_c!3~Q@p{T*23u`oj zAd3j)STTyhl6{^&zcX{WGXwHZZaMz$bKbZ6bocq_)9*cWAL^VW{84T850)mID)baY z!XZ?9jP9>&`W4sXow{l%{S;2$V}oioy{oIja26ro9 zZ$lpbq#GR$i5opGN;i7k7@z$;aQv9PdjNTi?;b$Mfuu*t_w7rH=h1TdYKuqIAMH=J zdt}|@!{ob1*hBg5k#;%XJ>tHP?;e#ue<1Pq=sbNL#G|noW$CRQrSH->+>*Wr;!sW; z`Z#WrzC7djP5J_jV>{_fFOKo7Wsi>ayux?KfzlUI91lug67d*2#vmMcm_pSYh$x|` z4oocmKdPxqE)Lus^*GK(M?cygOdK2mdEleuyCWj6@ZC|7dk-bs9UXa^?;hPcO4G>0 z+cXo0(qcHwv9Mm`?3mhMzB{&7P0o(7H6b_0+J41%$BP_)Xzaj4nu&uCX+j67;sD*F z^E78itJ2&Y^@%w+e39nh5JsAVhxsv_<2cwB%H|k=<35dS-OWZ1o*nRN?2+@0l-wif zoB8gjlpPv5ER}9_NFs({9FzEUTHmZXI{p>obq?131qTmyV*xzcO<$#P1R##+J)B=f z6FIyQLtl=2rAH*j4$e?XJP>{nPY0VE=xOB9>TxzEM^pFu_-S0H08{4SGU{=TGqx1GdqlKe{XJ6Syz%dbCv4b|fU?dOWTyuyy6y5K^0 zS^dR%x-wfRBzY6ez_a0}x-f132)+dW2&>={I1NsQpOq%*55vE}SK+hp_wW(86y5{x zfHUBa;21a(4ut(+8`vCvs=H0B58MFP!H3}ka5TIYI&SIoE#Y12GOXvpUhqO#t$xY& zgYY@{6s&|5@Je_oJfhy#=|6;jfKR}GsGG9=2%HCRg8S6}+5T5}KfDJvhX1@i@p}Vq zh3nuN_*#i>hstjYdXTmRagLD3$ z!Vlm(a65bnJ`Ep-%iu5IZ1|sWG`tpG0sF!W;rXx$Jf^P6<@x}=1GmGM;4^S7ybaEV zC2$110``U7VHbEdYzgz=(Gf{`{ujOpH^TMsckmH72hM<3!z*EH*bE-kLqiMIyBfX^ zcfsf2Q*bH#Ijn>guoRZS!SHg}6Ly1NU!Cm#bGU1GjQch{r2C!A^{VLex8IqGE`se~ zE7$}c`&Qz&2kwS1!e`-HxC-729VhdCC&5xU5cY!|VLR9yHi1X;lYFY-+wgU`8NL8l zKt~X~|4QiJcCE+4QE&+C55EJ?h5o(R>5eu_=(wWw+t9H_>&@_SxB@POm2f;93;V-9 z@Lbpi=0pDw;{ANslybn$@CCR6J`5|NW2;U-7LJ0Kz(ROFJO{Rh&EPRTD>|RQ!`*NP z+yXbk74TuW7%qfU;CMI$_J`kr=fVP*503=e1-=S5!^hzYxDZyt@o+5c5BtD#VH+6V zg;4#g^_1jxcpJVBpMh(kpJVNJFPsV|!QpTqya0BDP2e#->o}ikco4o0UxsVpDtIrv z6Z+Z8`IN$eupjIQ+reYHY1seo@F3g+UxqKjRd5-+6V8F9(8F%$I~)##7r>6N4Qvj5 z6LY$w+}GZNZ)e@-Y~PH29Ik*1VI>?7_1F>f?GO9Fb732(+d=dbBFkx~ac~SA35#J* z*bR1q`B2f4q+Zmo8t#TW;5N7ku7N9I74(?Q?JyV4gynD?90NzfV%QUQgPmYL%!BE1 zsHQ!3!yRxN+yvLam9Ppffpg(ZSPsX*VQ>H}hCN|>SOD{(5Zj}ZD4$mL`<`gOZ;O4@ z-{>4D>?ZaW&w;H)J@OWwA-2oQ%Y3a2^)>T_c(%A#94z}zQI8yjTg9uz=f$DoYO$Yw z4*I3&eC`w5D&0>+=cCt-^AteLxPP+!$D+=)%tY*^*ssTajreb}`@JQnzZ`oXafs4) z6^D!GiQTl{W9l~sN$(e(|2v}7?-ZTh??$N&GB1lRx8Ip^J3K179hShmMW??_Y%Tu^ z(dnm&I?pmUVlNe2NnelsTI@ryUoN(k-Tj@@UxeMi>v^0O#G&FF>Th%{7Ooe)-?d_M z`TtgQy5*v(m|2SbXQCcmG7GWaj{O#Kh|*6Hhl~E5(d{x?^zj-j4ikHcBgA&1_t!$y zf0;A%KXo;kuk}9_E%SwF`w`Lh1ET)Rys!VMi^yyhU5`JCmy1t|dPFPywWy(3;UZCw z%7ybp4OI&L%atNHg%i;J{dA1<4@5oc7xoo31S#w;YN%0omgw}2MW_ElJGK8OqV4aA zZa4o<>vBFTx|~mmN|JGZ=z82Qx||C{m$O22Ilm=3|9`08asCHH>;FM-7d3P$d`a~F z+|OCBLf#`aE(^IwdAf=&PX}}hbR*H_|5Eoi*Ke=r zaius=Tq-)hpNVR!%tBFpRiXQ{PRi$IQB}xHXZwv}M{O??wX2NE9{rVP-uc?DYr>z* zmHOWR<#UOs?}ZnNE`MjSgZ$1BT`s5Zp-bH9<e`C-PNMDY#V%rj zc!8L&LhJrf0z*K<;7ZvJ!&i+H4Z~4144EfQF$38isr{3;_+}50>%zPqv4zbknki;V2r0&(JVbqhW9i+v#CAh=yT5 z8iqY+7zXIRrRSv*7%oG@P>hCQi|$`~o-2W26B>q%Xc#u2VOWob!7l|lKNy}w!>|So z!)i1PE7344N5fEshT$PJ3`@{3EJnky2o1wRGz|06FjS&pn2Uy?0u94VGz?SGFqETV zn1qI592$mFGz??VFqEKS7>R~q1R926Xcz{eVHkjh;W9J~#b_9MqhaWYhM_wehHhvW zx}ag`godF58iw|07}}y?C_ux|0u4hx8pi(I=W9r|$Nt^;8?eLJpWD73`#S6}_UE=giG2-r82fYES7Tp^9mf9L_T|{Cu*2A& z+x`&tCD>u?&uw3feGzsT`*YhDVxNy4#{S&)O6+s7!`PqOUV(ikb{P9}+oxhL#|~qE zZu=zcGG3Ij2d7_tAhG**C+9x^yW^v_&-4kY&l99S zNdAkOCibZxCH8HkKS+B2-s1EVu&=|u345u68n!Pvn4~YqUWNU4q+dk(J=hC;;plUK z@~4yQT!eil>5q_p6X}m*FDAWzr}F+kKa}kM2A!X-|2piB``LaNyWeZKeG&HQ}|Ah8CpZ2>nKS{rd_Iu0aZ>9ZletggSliq`V-dgAAy1hyHTG%N+lz9Nkn11~GP_}(kPWwKVqaV%5|E?VU zR*wG*Ir@>D`ug{+?EU-Sqh#ytIXbT_nZCH5X;PE9OlRjojm8ddDAA$&}6*VOiY@=v-#FJj745qFg>=l4b0?*aUt{u4skr9 zT6;2GW_MtPR?M1kbH(I%OsCJ$N(*H-l#I@9{CG6#%sY6>wLqxa2~!cDa%Nx>6{n|2 zJypq`ak{9?Gh;H}re_ImsA6v9e%Wh#c}KM?p=lAf#bQfNvYUwfh z#tTo9u6N0-n?37tSy|cm*|R6lswkUPq1Dk(sD^JZT1;-VXMW7CNn9%~UG}5qa!gC2 zQ)%#z^U9`8o;Fd_xSv?eqT;%9mejUHuhGRt#Y3mgm=G8K&}1T$XHC|X-_W$rCuZI2 z6pLfqyUtm6x~sgfOBJ+s1)BONr5<^U))Mg<)N`a)bJCJc(<^*()!ZDfyF~xWsW!j5 ziw)JEZ*~$botCs#&Fm^CYpU#Pu;%7pr$v&fMAcR&xW{Qk%Wjx7RV&@p+mI}{Q(K>` zg_cfIkvhAtS(&Fcr=nhIH9g3hF}XapS9W85!%n-tr1f{tsb_4=f5jBY=?=4N)nJ!epK}BKTz#$$b#aUZ=pxrPH7}y&@WP@D*>Jeo5Q4$PHVkA+z_yO*AQJk=y^I?&+D4?#Ktq2eQbm cebSWiCXcfZiBqMC^9BzqyU{Zz_sq-te_$L-D*ylh literal 0 HcmV?d00001 diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/obj.target/validation/src/validation.o b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/Release/obj.target/validation/src/validation.o new file mode 100644 index 0000000000000000000000000000000000000000..2afb7ebf4c5762a7e65e88d09675d09858e8ad5f GIT binary patch literal 77504 zcmdqK2Yggj+CP5oOePs-LP!u)LJIo##BK zmwWGX2S5HfO$fs)aDk)y6aYjH9DaS^_NKBo;pO~;5_}5r;HSeBB-fjO_(h|wvGuLe zFT>T5Mb%bV`zCv&kK~c}eZvqw0;Pyebko0Rv@W)+ZAG-cad{KLqS4ZY#q-KET4*^B zFOr~ZyUBZ|#}F4wuZl?qw7i;S%bH{D@$!B?QPSPS$_TM^nGD;miRCrdG_HuX#Oe2^ zQ*4~@`c^u_2ql!arlyvY>`w3hR9-`UV~k=>CU0Yk)JyAkd8(lmOQ>H%Q!O*NucTPFg!-xMjkDjCdnDs$ivQpn(o-HZL@()e%d2UvQN*}GzvAj*E4=*` z=^v&*pS>yT>idvKb=Lip^{uw4rXku$rin(K_3d6IZ-*++N53WOzeqO((Ffz|TWwQg zi)CcDyiF>7M^ht$-~M;0$|nCsqqWG2`oq00DS6-hONRB;VLSklzp$S@ zy-xd`vR`tZB5z{Hds4sadKiL=WKhBNEgG#^yS5<~ZLC>?+8bX#cI=mw>8f4Hd0&S2 zL*5P2>rhe4Yg)F7D~P-NJ^g`X8X|9E;p%lRNE-ZtAP-tx1z$9bnARBpUbzO7fr zjM{#8%rtf%I&eXC#-X!c?9s6lxJ?Hi?s49HBH9M%1=A0my{kt@gb?2YF@X?=p1NS( zA*LIJVDVW$eFR-O#+?1i7Sc^>!3*&p|~;*sJ_KRwdpvPb<-^*H-}qTlq>S3S=A zZ}>txkSJe!N@&-IznW88{BDd%i-peqq4A|s(i~wIprNc%-q)Y{LGGTx!ox5c#H35 zc+2BCClxPU^4R^0^f({t1aO0iyn{jCc)MY6 z#Gt@DKvuzdzVl8ZKEv?WGJjw#pyv?gd+&1;yn zn5ge05o=gP4+h)|fPw^xT4WHv=W#$YI}p5_q%lK~;t^T50`M}Cyr4B&$Krav0ic0e z<=JCO6j^Bi3?txP5>5z7omBu}H6dPeB92%}z*|A7^*n<0v_XPrke;mFauAEuMqK@# z?nXD#kW87jl7#eNA!o66X+-16B#4zPMg<=+z!f+Pc(&&Vg8-nD>FxF3#Mqq}4ZoCS z#q4_xNR{WQ-*$)JBT}r~%RnzM7Q#G%6e=|&VJmABI2M=^I8UYRS_0e+J4-lw40FDfOM2PxQ_PWgbz_N_h-7 zq$@B+gJx?`@Ln*808KJpM^E6Xa;N84z+4OGS>(pt2I$9xxi#f22r=yze3wKXc{}>E zMW}3k&xt8V1>yN8z?AJrPe8zPa*E-}BqpNsKTiTg8qs^ADTd)ukUlMd+(gh7M31WJ ziKAZ)datApzD7}J-v)-aiK)Sj>N^d1(eD_HRZ={jl>VQA%II$(DEc2kKoJ;$+d??c z-%>sxt<2)!I~7nPa6YboC;a^>eWu#+oZ`(G75F_@q;w-SIkkI<_Bx%~8>pgw!l@nb zH88_K@3|r6J3u_1OfO<%ik~FKC}5Wn#UE3;AudoLL3WBXiqCT+TTDt7folMIkiZY6 z_oX6)xi@-U6{)I4ppQ;i;TPpspvFRq(vOxA5z)nWv;i>jfdE@FHq=Fxui98kA zrvP^g;TPEOoRI8Y0DnxVg@ls1A<};aME3xcQSm*%STcsN_W%qD61B)6e%S*|^Qay` zqS@+MSsCsgpqRz=91Dci1IReCP6SZ)0IJORC3V(mK*%1ziLkf#GTJiI%HA;bNXEbakFLw64_gSAT|8r1`s$zoKHa!g2kAJhVa1#`zI@obgAE#Ox@gwA5Nhogs( zarNT8f zEUsrU5LWS&abzt6P!>-TPDYc|S(|{6#nXwf7f%`OF4D?gJf-MN(vFK~`WKL;Q~WHb z<5RqwRkWv=Awi-R8N@GBJTFtFSfZ&37g^npbf@?Y7T0qE5LSw199b&?lqn|RlwwJp zbukb!#ZH7h#WLD=NGp4arRYr3PFBb-Ad4#`cQURa*?X*A;6Bi(3RR5;8JYw+J&p<` zX)VdVDoiPj)U|Z=yxgrD8JQ}4APW$d_DZ)~m9&mQK&BC?=e2Isw0_PJi}cL^>HPQx zu=xD=fhq0z!H^(PiwxqI`EeJb;u0v)Umz&5t^?Xx0(~V=SM+=l2rEBi99ag|e`S7< zaLNx!oiz#wnIBF>CPm6-=BhX+8_M|n0|z3GTJm@Ir#^|h;aTdl#-i&0;hDrKz9FOx zqRC^W0+#^8hO6<7_b?_%6e92!Ah^3<<|~vevV%z@>7F|7j80$~+0OyO{ZP!-52gAs zyXQi1D>c2YLNJ**(}+g3%Dd6+uq_!CT#TRy^aWlO5&}>mA!))>gm* z+ReTNlulTCIj1*}0?pW{Exxr9ql`)_d)nBjtC=)=Bp__mcAHVt=K`wBE=hJ`~#1Ie`P8%64!P>GvFQpDXPHlYY%u1YkjNhC$NNn}*80fj;Yp3})1VGw|tL{65YNtCi2 z15qhs10oTbB$%5@-?U>OBK2gtdY-WLr`-Ty<2~8!XIm_Cjp0lZl15CPXV~IfSap%U z93b5~oex-i>$Ht2?X43-fOr~JWNCt9+FFh4|CcAPXqcL zVWxYC-i8Sti;oX`H3Wp8r;?_UNKV)L0X&vaU!>|JDh(zB zT1%LNZcGcHR}n^}>83#O+y>~Ygz>pCp90#043?Vail;B2lL?cSHWGE66jah?!(QAk z`_mpTWsK^V$&j9OS)BPh!Cfa;P@65>n#E@rove1-&xT4WHvEcq`( zGcNfOU4~(@$hs3~XUX5q;(ERggjMoo99aUkk|kdy#xJR}jsZfJd?&(Q@@2GpNh^E5 zEJf#(LIN*=-TDS8Kqj;?WRmi-s?>v!n>tE>yQzDJl}{rY)zq1(W>jz?`amJ^a!&5T zv(W;-0{=ZEBikSbD3f`~2x+|_6;lz|39K4FDvBT*)X_4^C_kbUI~q}`rIn`i2S;0( z`~%42Gg(BajMy`oAwi-R8N@F$`FH4cIg=%N1ZD;z>jj{lnH*qoJ--9O%48WwRzEZo zGLuO-WrU>8nh1o#pp)$iSu$PdCEBZ zvsuSLUY@e_I%6=#Awa3oKphag(z?+`p1lpw~8@%9vSQTtqmF&2goXdUTWxNyCG7k^?=?)m@C|vI|1ECn5$iw>;r%vg#>$Uu;K__ zRt%*A#{+y4q5fNQh`aZ`{gG}ifNZ-#h0x)0qP`%t9_C^LQbxNC*o$>dBN|T? z#mV;P(!r=;9a-cU#@7-)j)03#;4BE>vcAqB4$uV|k$n#kwj7Z0WxoTcEeB*&wj8(> zs@rlvalHCUf8=pj zmLdYnfmclq0nBW0G%Zq=qZN`e7LkUw3V3u8MEHi&HMMCRuAT*+z!PIt6e~Q}s3@q5 z4K4w5a2Z`>k6Q>fR;ArZN7YyaP6c)+oTtNybxV0p(_Q7HBPlAPp3v-AIM0n9Jv>;! z)(Q*rfSyxWk+m@lSuJ@$~ z-btYKCg>pqUnkH;4a)vEf&&WyI>#0*TRA%q;1daTzJ(HjW=++D(v!&ohKp!N` zZ*BD1j{)*3L9ehu18)N2Clg+sp&LGFhu%4Wjwj5u8j~GCknMe=B|@ee>CD%aZXO`usV=fO*|d zc4cVLWEX*@z-=Y`TYfbe*PK@a`Yd7Iv2pU`DzFFO6qF;+`);Nq0bM|t&umPb+U#0@ ze?zFBZ78;0_MZUWO{f%}$x77&{{mz%tlG+Xg>}_N0Vy&P8^!=zZ_OU zO>VJi!AJ!DtO=iU5%Mx77q`F%pvljKPOrIWSdLBDco4;i@J*j?Kde}>HKoz}U1+(| z`YWLCBkuQnq#N1V&iy=4KM?vumlm>*!vP{ODhue36QaXFtswO0F0@Qy3s5ZlOBY(I zb{^0y{Og41Yk=mJx^G=*QZ4Cfod_&DLWr~BJSnNGy4g}J0yhHsPr?M1OjMNC&798r0Pb)Zoe5oT}9}D=JZ~oWy4m9^4tdJIWAT0Yf9^jKnI|IA5)CoXOcC!<;2;=~_knmGoHj;&CIZ%HfbV)+=y+G|Jbg2t1 zOUbuDO_&ID#D$i^7Xx)8p=Y>KF46x4%0CI{s)XqNK$R1Efm1k25um>&%wiWN`({9& zCCn+7HAG-PAjV`s>)m;i1?XbJoa&S$dlFewRsw%5(KIJi^H!kVCv-~!5h8F9sGd`R zKEsI?fowqY2(#H)JVjs(pbH6eUaA_6sgjw!0?;c7v)xKx@G@kyEKGstfio~j@m%bV zZ4jW9g!xS>(VN9c1R4ST17UvavQG9+KEyhQe3>ecW?wt%c1u#H#e&o#jF64@gJ zm1KcO0sIio^Qekek*hrOmI)Dt^E{@^Z%l6 zJu$DjITNTigg6(@v)VihyBHPI5P{zS`WM2S>c-p-=uX14yD`rK`ftLV?#6rqXqpF@ zv)q_WKt~ehTsLMcpmPYb)s0yQ=vu;TcVjjH`dh+W;>P?Q(7Oq9r5p1wpnRJ0S~uq3 z!2C=+e{^F~y_lN9dH&+Y3=4fFbT+IL|X)y}qD$u0UzLlgj_IUUgzX$MYbddkFKKw*#{%s)CB=b3nVH z{Cb}E{@RH-2GA*l+2iJ!1?XzR>~-5I5=T7O1NtywUi0en1xkab0sVk5 zuX|~1V$lBb%1Ur%=<2kNdGm!x>umHs`0(Tvw%8# z1%}8zD}(rDub}r8{1VM$qC}mBM02N+=pdy;^T03BQ4ERZqZo;Hge00bFcO{9m+0rv z(2A_wN%ahecMRQX{Fe(q`xm%Y`@=h?C$v@2EKkH{{%J zGOO$evYJ716OsKyCV^m!OhtV+upcr7S%oOfp*DCEmqProxm1{G9Z`hH6@@ zv`ZBN)gZZmq8V)NUs?NHtYzRTpj8*Erq;4rI=Wb?*5?$hql=Z%*zB@x7VTd@pVs|F zkd7-x%>U*?rQN^M;r9^6{`HRt9|z}oAK^@I_Z0Msl+m6K5bos-^h21RZFte#eVq<8 zdgDpJRKt08o2N|~qlS6Ake+ub$uFhpWjn?AA)tI1^=0$Osluc{Yd(y+$Bh{XOevh_ zl{7u|RXo*z@}bwgPBlbe4KRFA_EqzstmH`}>v-Pbz20xg`a!agypS#A=@j1O8N35w ztS6&2O~X|Tz6`7!ej^Q!AsrfggATu$hR2l-4Zcl>-%9%lVUb!nn@Z>uqnkNAvu&x} zMT1I_Ml@(pu1}d-u5pdyJXOKh)lGQf* zY9MSoMpB>bCjhnW7|E!3A|&_h0&^TYMv7|;ptc<&2@TW(>e{n=h*KT73K-Xpk;42F z7}t)G#AJUBjBUqAY9@D#hF8Yz7)hofOMu~_^c3SUiW)z^o5lAN#vH?l~wcdS4G&Pm;W6465mf zDzF#9%##6n(ID*s3WjkPf&Kt8;w2LifSy7s{wfH)N_H;-XU9O##q2dVDsUE1S5O?U z8&r*Gu1t^Pi0m5y+(C$a#3T2`CFXfRKO)SBmNXIg0+3@VnO`ZLv?3sQ!>4Szl|B+R}o`FpP?%(Lc!IVZ+cXAn;Q_RnmLdlFqIA(EGb0cmo9E$qG)5 z9Ssk+|3fpQhetre10_f3{ z0Xi88Gl0`P^p3-)4Q;6rLLF*z_ke%mGHvStL-+gw;-T~e%clXIj=g`C>7Wg}BhmTF z4Bva3gnkIYb=WnL&^zyv(C*6;L3089D%Yfy6@2KdM9^W>0o{eRbmk;v7gsd0u{52l zEv2f~Nn1K}H6rLc!d6{%2K=3r9ieJ~toz#!pLQ6Hr_V$Bd+9-=a1C>jkAt+t z<)usbG$0D;Qba+!4LS%&WyKD>hJg3Nkjf-?Qkbnr>zxYtm=kDA4Q;u}h-~Cv5RfCX z0n}{d&1&!$tWChqFI^^T?urU&NF%GQrJqtjzPH4 z0|xR!XPdlq3I7JzLb`Zq{usWVh$)?64$|>;FI~d-5rSbfne@^n{4DYEg@d%v;-yRB zdqhm9W5RUgMqcQsqn9pWAI2VqbP3Y|xf8;4j@L_<@IU~^1q3tvd=RD;6Q zqmJ`SCL6D|w-p4VgHO9Y?O70dzbH zrzRhOlmU=p7~8vf_1nq3)R2T}wZ==A@ZUjDNWMbygDx5G4F9dmwOonAbbvG9519U3QZGDkG)Jg73fl7fGg{knA#9SPu+os?UlLD?% zcVAiLL5xODQ^E5zxV-EofP4Ccm_}?Gzfs{mEtw+nNjc`A3UIGxoL>GTz{VnQMmiuu z6Wpu`=2Tn;a3Sy&e}Lz7iel(^i|}e4#g&NSD-7*4rx`^h(`)c;n*PR2vd=KenQEAZ z)!?y*Z#LUUOeZV<0Vr+5ov#xIv|TWR-X`K}rUHKr6gsYn%-T%(P!d!`nt0}ggln0G zv9agk%KXPE)J74MPDOHtE9!i8_qCU10z63tr)%(fyt#@>#JN!C*nLusm(|@^a8HHK z+U2bJ)2UD-*G0zpsexk@(zl}Y&r_&Lmm()?@XC?}%DZ0OIs4o|-zr(6q{VutsZ7*l z^;+DVS+>z@5y)oQMzAL#%vq$_PE>3jouyii*2O2YRD;)OdYPr7m?~C1EcJ;ppSGB> z;O*(!QVZEqmy)Hvc9F5AH1KDg1&c}_houY^SDH@RiO88fwdnE@-yQ{=`-U{9v0&~D z9bYcTcP7$W^0AAID72y)_LIhiBBaY974)mW5t<%@Vp065vcbs;x?bIl1@kfQ zlEp;Y;1$?l&TjyWDUwxMQ0^udX?KhVaX!h9N<;RYrwDZ%#)3P*VAzG<1>s`YATVTt z;RQm=NfB zDVNg9?D$`!Mv}CY%g~Q`2hjxcT7Xh&$6tcmILJ-8+%tHXcl>`LNHL~d;TfFg&3XVq zI@O#~p*Cq`mz(t^;Iv1RGJ~X|$5HTO<|+7G4gU;LiL42L)236(d{WAyU!&nCYdF(S z?f^WcMs-}2Z2#9}Adz)35VY@+vYfgC62!~nJ$C^9EitVm7~w>gr%KVTCZPo9h1jg4 zHGGYR^FzQA8PdomjpSS)WRUD6M^RbxHCYRdSg3D1$O%0i@V~;PtaHNe2JBseuXn;f z1#Cby;2WIq;eah5_-Rh~DS%x_@Y9{}O96X?;Ac4DPXYEl!8bYKwA3DkOF7F4p8{AT z!8beM8vy$g!M8Z!w*mG#!OwQW{{>j)0>IC4!utSLPVjS`@M^%e5d1tR{MUdzOz`ua z@TUR$k>DLpc(;Wp&TuIgIN@c0wG(`+6TStoy9j=v6aFY*UlM$q6Kc@0B%!fG$}Dbx zFlePY(YzF2RU>GtTX6A-`2pyY4cCsKF}Ry|)0-?$cxet$b(N+(B-X_8B~XCeM%Xe69$@ zqO&MD7Z~Xt$*NzsZ7ewM9Flzjk75V7usr-SE>y!W)XoFQHg4R%gt()PVyA}Dpt$^Rl0Uc zeBS}9UyM9|uS}Uv(JD^Sv#&^~#>x~m$P`V<{|%*m4NaP`;zeo=LEAUvRmrnZ4R(>; zHxk{is5!mJE5z*WAdr$ak|giNw{Kjyn@G;4A`n257rsTobEtr{AejF#1-H-?GqPYj zwnk|=h*;8L3n&h-&b)^ULPxsfAB(Lg`7#UOf; zK-(T(x`bzfxsWc?sW_!W>H?M1d(0e{`O`-EGYdtyWTnD9rS3AX){$_UM%;|JjP(N+ zA(7Xky*^D*U90ZK`p5%`z{?|#Ap%W(v8G-gIbljX*f;!?1qr~OOEaf6Cx#>0P-rhE zVOo)!bQBxPdD(pnRZph4O<^9@6sIlay#u69lHLf?vgMGqi#*Nnd_>F`-8I+3g0SLk zNTM9w8OzBOh8T9PLOm6SD!vk^3E+D1b(UgI4vYogfSAXSN=$Yt2B;@3L=9J@`_j@TF}fysC6+GoDLasAgh#k$)_Ygt!SSyl_-CJh`$8^ zjPa-v$uJ)3YGc%#fI>2d3d%6<1CFBwP-EC=a2H{^KNQpM=uMdw1ZJ@2Wsj1xidIGO}| z*K(W1OITrEm7+B@UcTYwI$lNQrSR}`qzkW%^THL?Bwp&}#oRD0mxXzSklH3;WEn#< zj_`RivW(2sz8P^gAa4f7+*Uk6E1sSu!5*{K@4j~kw%m#DlVgK`b-9St`&v? z1j)0xTHA%^E4=$F4nS2}5n{Q(YNJXruw?Pc!dPA=hdY~SS zH%n|#7J&(lh^WF$lop+wPDb*eahYT*L5Oj(E{x9HoMIE?#1t!avy7g`lmKm0PqmC5 zPWK?&N`k21J)tDt(-k^`yo-X15m%(YVGovtfhGEBHZoM}av$JQQU!U5WV$0(v{qJ} zgq~>`l+rlM%0|M@4jS`un`4ij6t8p$mBnPPv=>>6O5nU9A{d+m-SLhKvVHX+;jv~s4z$aVT@+e4H6e=gIg4f9fgK!1CGXNGHHET@~JwXL?-EnM4n8^ znV%|6yM%2C=u%}oc%zm=D7{p?PO(`4hOV&)P!fq?*=S=LXV?_*i_=rkEQ_hgNW=Ic zqdO0msrANx^yu6cbn&_KTVl;ExvQF5R<_0(SLBXvY^sZmTCrhmZr_X>D%p%9_^by870YQHm&+F3~kjb!`o?mfV)6+SRev z(e+J9z@us#>SK+qPVBlCXSlJZ5mUOFhI)K9q`s+fbZspqx4mfeN?1ZreDI-rN5~MmIS!g5Uc%4%1|(2K-O?O8mWiz6X5W{Ih(+ zyi4}@g5Ucu_OI~y?)UZdALGCE_x`+VeZg~lK5tL|7rspYzb5)dR`@cnomk-;+3L$& zLHFgZJK$|T-#TBKKja(gUA^3Ac>BNW6RF_IeNG z>;d3N@9oje9DY7$vu}WR&}IO=-ixERY)F_coHC=_v+Us`HldJd#4BHwn38xl|5 zLvcgNgQAtW&)>KmkTKpTb0%>3o+u9UqAM6l9orJw~$&SvkXf0g(}Q(18frU-Z7Abr+IIUlJG&^zZj#T%TCzj81Fr& zkj`n|*UzN%4)R`uoudwWvo&7e4~72^HxknrigpiDDGw8)czvWc3) zKfRh^&;i}VKZ^tle$F4pv;3Q>z3KLT2Pp`x*RHUSp_FGz=yi%c8o5JUvuDoZOpt%<*@5Lsl>IUV#(F84~ z=L+ek?}nInzXIv?Ab=!|b=CINYe5M}Vu~6qC;tpF_k88mcYv-{Abkf&LCjmT@|~vc zAla(CzcoSgZcyI4Owe3vqv_{2QyV8+&*tm&R94K_UOhE3M3od{UG)^X6OucXKy!{9 zFXbLZVoA|fHDl7;FPiGb>7u#$`#E4VM78#QZ*s-e5Y2VKB$=zd7FU(_`qV;9f~uw` zj`g0)C->%DszTzmDNcmgiHRj;@74ReXsOo&ExBIOS%?%T~2#ATZ% zv82RRD81pHqL(~Gwe~JDL2=m>x2O`|rt7s7bKNDLM5FZD53HxX@0)SQB}$X%9p?lF zJ`57gt@oZ0xAyA&W+T2x=-q9-w+}AuouhV$p^n~M*L&y*H2ei5&6Z%ZcqmVeLRk^T z|>Fb^y zxKC|rYKUP(Plw0K+8S$VNM9XWv$mn8H74lvSb0rjT|;a_d(jCelr%Ncr${FWI_g&e z|B6^MpJcL4GNSl4VOwphprdb9^(z`{THBgqf)2|S*Ve{bTB`9$NHf=_J@3 zo)ZW<$vCsAwx)q2lR&M?Bta+6s%o3-*S5}Su3u5#DCm%-l-bl=LEkc^MQWS2mCf~Q z>RapEW0Uw$R3&C%^(~l!wc5l_5~m~n$oke;b7M_IadUIc23C;H-DztGTDP;wRThI4 zFbAF8wq_ZOK&RepB%0OIp0-dHm~$pUXESZ|IvJ+HCJ8!zX&FNqdy=57{o>{oZEKK@ z7D2C7s%&mr8*6UeP}~ZGE^9-sod|tP>64hWP-=_CZBbfhw4#vMNrKM0T2PrwlLW0; zT2Q2<4Q8d?1DkvWr4xv%qzGCYwbB`@a+08fwQ>0*Xz^H*R@EXO1f6-d z5@Z&Y!F`~bvj8e<(eX`-!AdEe)sXru#8g?(kxZ8W(^_;i*qOKd%4%B*%;H1q%Qm>J zywNd=sXSR1bl#G+gFTUE`j}_V%$W;j<+M`@ni_M$qw{ly)i&VsyraVTg`@LE=NBEH zGi+H+ORO#jXr?U~o!`>lcs$kvX%!^5-7NdGUf^C6`G0WjLaeT6X`;rgqOmTvUex20 zuJvf$>TBy;qczPdTB5r30frj^tYpyYMjJkyD;lt1gM}1P-x95*Z@5y+7OkymXo!ks zXg5Xl#Mv|Q+l$7THFC^M=7_Wl>4~SPUmvL#RAXyeo0?BrLQzF!ZB@bw+4P}MMaz6> zyqrD(3URiOmMICxRJ6=)YiM8+^2A}Qo7-ZHC65z3wxXr1rlCcm+2_O;RkX;O!u-x1xXEMI{^}-Y?H?n9R&%HFRKY`e z^IMk}nQfsJ&{}D_qFFT?s2{7TZ=BuKSlm)uUk~%d16_QD3!CdvyDD4j>+1#kMO$MF zMw{q>T2?kSw~98@&(N4?tBWy|$J8>SO2nqv86dE9-t1x+$Mlqd2*@inJpqUk8KaU? zsEd}PU`aD6TqTu8)f+|7qI0+8ijNvp-6+8lF0-QD&`5177sSPpKWVpi*A6DAHKlREH0WM?|O8BHF&TwRuQ#arjhqd+V}>D5#?JHEeNaC$ycQ z9EQywlbmvFX?@GuChowL&zUKm@zzvD=FLEjF)QOlQ69rcYyRvRv*#?F9gWN`nNwOZ zdzz?TI43%FadpIk6jzm0R9Hc6KDX@)lUfrwn-pixMx5E@5?+vJ6ToG`N$!jx0fCh_ ziIC26mp@i#K?2%v9frq01J`|#EP0c!GCCty)VOK^{-=|S7DfzQ%R>w51D=~Ijmz)IJbe7{07keT# zehGhJNll|&I}`~Q#1&y7OhSX>R_r9!ENehBM)~OicFiL*n+EWz4KK)F5NobqzCoMR znG;rn*;%uKGT>-YyOKz1+EmCd>WrbQJnoFwX^(LwD`T~*g5^R7)x5i93<#L z2bFn=a*VK48*YumOd@k&rBj}OhDTJL;CU2fO`C11IfhDRoQX$DDgxP9gu>NLr7?^| z8dPf9g%hB2${Zb;gX`)hYZOkUNrnol(Dm9{1glv>!%z@KFf=x9@g^d`B`_9>iFvV_ zx;c#v8$>AvNRh_2HS8&kHK5!lSoKi`x5d3GN#xd8i5giNDHY_)k&>l+QCSnl2>-Jb zXjWa~luFcLCDRDF1k|gXu>26n;w;wEnb3G@r^tfx8aKaLW+Iqiv^dGfNb9>|v~j51 zapXlP#0E*os`@F6+HEw>I9WxPCA1zca43Wf(`!^om8>kAjbRVXB+S^!FXtENu_723 z0T9a9l4x-?5~(a}uNsR|J0@CD6{fzeI2x%c4qBWVyH#?;zG!*?@c8FZ7v2#7$D zBF}9df?2SrH8Y8v0`iN>>Kp4Su&P$mSc}y;ldNPVW`nNM8yce*mh_Ttgz8Ku0#sy0 znm$2mY}H#`A!PB1BrU;*3==I&XzSdt(mWuFe9s|39&YPAk968k*Ezz_uFT(Hz%F=Gxn6NoD(8Te!RTgonnZ&xb>a6$@^(#8E z&ifOKs*~z!5hbak76DgGM{%6n7Hi(%ETM8F-)!$E)hNutNmDkt&8O@VSJewq)_Gg< z7gY9D$*VQZ_w2)XRN%xldzA}g)oT~Vs?`vsYIS0TaU2BOt5!Nx&2~Xm-F88xiCsW# zWEZ$=qlOVl3ASj9&5g+}8&Ec>wTX*HOg3N%#UQ2aIHW@7WC*{krgn9b{0(7uqj_~P z@8gu0Tx{PaM`Z8t4^qhnhrX%Q&t~qs4Hn|!x9O0$>V^@OiMH6cxr6TCxp9? zS0q---RQ%xiN`;RJ#K8o0+M#~iAt%??s1M0nl-+1&zj=no``Svr}jPO=>kvvmqKJfd`s?X2iK?-=ZKopcR$vLu~@oy0zFi_5(bEl+8K%ek`- zQ7NQ^6ls8a%FsE!p89TGN8vRqw<_JHSoF*|3gRTT+6EHiTKK_2yF4;tS8goo098`A zAic`VYm`I~m#nd{0CVz^MM9Dg?TTT&C)V5?tFvhBkT|ito*@r;&BS>aIg*nWc0=QM zdC^c(l{7W9t!a$sw!`E2Iaeir1s%kJ_Bs?#OKFo>c}CGruTCvCtL@ilBMcZ7X0QPX zi#9D^-V$rIL2FrM6K|hdMFKY2aNnJzSlwA)dHD{vHq*+5cZ#8UWoirLYsw8cOjl$Ms*lMtiK<-0hqtI$0 z9VvmitQm%lk4@)T#l>`vHlHf0=@bZU?eIZYK2;On?%O1}8*+PdAKyKM^Z@Js(y&S~ zt6WYH%NV_cq8F!7B;BB-+!)ZW#7>@LzX6tkSrj5ECof#fHBDQ@=Gvr&CFjpa<5|*B z(}ELKU9_MkRdQiP5LRAaR~NHZM{yV`9++IhSiOs6CV{KA+*vYV$@xRr1JP!8^#@ij zWTP=pO?PRj{ISJ#=JKyz>yX1loI!K-VP@C!|JCM&?l}`K6=A5GJSjw_DkX7N z=(1+f7=W7#s{jAnR(y1E(RtccRWG0wFE#v6!pqaxgd?E;Ez{v7iYL!}Y9}-k4Z{9! zn67pzPH8fhPTR%Sw~m?C(6p?EjtxtX3mwX*t?=5FCY3$trVXQwX|++i>aICAr5$Y> zolA_I{D4=Uguz-ECfH4l%j;LP;jA=)vS(GwqqBV z#~9QC@yG&Z$Mi0ORSij_a@DP_Chl~s;Yy*2F1+~Qf{+B7^Ch!H2&b-{0vzEk+3|qDY#dN7MV(qnV ztK>>gS81jBAU%YFhZcCuf_YFY4MLo-u3`;^o%GPz2@n-{E+a+{HgR>I-&)^*$G2iF z?uwxXw3LK-G0Y=l*giIwkrL;%Rp}hL-4$`7Tuzd!Rt)j^+BJ(SFS}&0a z5mBi?B9*AD&|znu%d3E!ZVZaIUW%-4S{-wDKDzPXr4R?9OHzITUjI@WTVB)F;1bv_ zl3-jC_&|sw3aPmR9En9w$?ezXT{buHRB*;ZEP*~ zy>u!dA;u+PTm^O7G{Qg(rr$+GWs(o-I7bQ&qY~cDhJ01$dz8;bP>@&L(9pE51h1f( z7Hg!v7YrL{A5BglS}=F&oWLy)kKH5{#*);tSzQsitcc<8S5gtK zVUIQ1rzV&m*f|U#N`WzQsmR?4Q#M#LEQFkEN_?U^lqY#P16&Kb{8TPhbNOz|xWkD~ zPpquWmra`DMGarGwjM`(okiuBtE=QV4bi3aIPECq4h0M5wKbxsscg0|>63NpUVAV?n4gBZ-(~Sf`-S*y^_C#yO3)ooqBLaWEGX?@rN#4tvv3 zgl1Mqc!dq(VhE#V$R%1i9Zpsj;(8Km8J8K|ViD`%h$Zc8bS^1lfnm!l^kwa2ZE#7e~d( zaaIOen|P?8^OK^3Qe(;HpGf6vRxd*5D9SA;0lFa-L zN*)WY)}W~yT4O9(m>_-REdi>Ep5skIo(PB5CJA*srmUf<#!X$yCktI7*wzxJQ^6rJ zf!m~3yQMk?WNbyFgN;p^tVG38x+0*iKCa}%F{uX2UBcq%oWjF&cA;zGob$3p>@tFQ z6NL*7E0Ru&bzC|U6>oZonmTEIHjvsY9dIT(#K$G9g?D?~$h(Ef4op}qE`ehyqGHS$ z)w@E_@g+aI5l8D3#*;MHSTlaR+%cL?vPbTq$uHn1|6+BV7q&B07_}zBxbzBDVU7*% zPKKRbP&ftV7tU|2rSokGbxMk}ukFh@L81wfdHSWZ^WP>>CsQAjCdquq#M>u1a`wiX zbW+7gB*AvSO?$^0m?_ddUpF`CX??_ z@r$-Jr7h%^sm!1$?Lil5NTyg*+CnbEJWi;lvjR@CF_BZ-@HU;eRzyg+lNc*3cwJX* zYcd`?*2OtyZd-jV-fCojJ&+sZVhJg{dQ*@sh)gUek0^_FaKs~(0`kdK8E7}(x~5(M zFWTiR+(cbVQ*n9d9@M8P;eG;1-&4#Il3Hg^b$37bxn;?Cyd_nDDX0QCQtCv z$JMiH*6RTozbi&|ZM4rPWNcCQZl1IAb8nV*-36lTC1vVz9TOO;=;t7vh>?*MmUQgg7>99!O^-_*yY z=}cS6f{-v3#!#LGKZs(&SJpS;9I;N{ z82M($kn=R3qrb96>1LCU54&xt+iZRyl&z#r)=R@{R0>%tCOlxARIXR6mzx)LQD zdskX)E2A!Q%nciHtN;z;Y1mY9v&jM!;e(=@%AE?1a7{uep1e+is9i(vPY6XT+xewH zE~Qu`p6`G>WN1Ic=OD>r?<=f?4`2|+CN>Ww} z8yBn0iDaCd6A9yFb0%XWb0*<}$&zwuoLfmUXEG*@a`S{j*i5M7m9?4t3K3W7v|&48 zo!Tku)c87q=N)S5oT>CU6YTD3@*pw^hkxOl^KMz9ReCCG+6f!9@isA6W3AvqKAqFT4`x@F(fc1~h6>%l)@Hmx3df7cNG&+m zF<(*U=Ow{}3h;Je9;ip#P>a`8*3#>SEdUoK@uZv(w!UcMl@p$YH5j;aL;`8II%c2^mY$)hnCy-=TFv}x87 zbINP3cc0M~?mFAQHvGp=jf}$Qtv#ojHwoIdB5w z*5RFv?KZMH`R23)l!Y^6HF)_ky|f$mM6!zJ)vRO7M=I%A%y_G4d?IEmm#udZQ^uPHq>Crq;+hMSu_Tlv2^K|u5(;M`P5Q%6V`7$>p|P`T8mm_}H?8A6G&=i-$pYRh z;Q}fXTKQ*FC}|#>G@a$JMI@6hD>*M3I#=^t>zLzAZ0ASG>~x(hs@lntEUel|^x`U7 zfP^+dHL{aO5>){!<;)^xRtHaqzENBo>hUgifl*d-%`zHC3t2}+3x8v! zRkYF@9GAtGH{sJlHOnzT;rB_}K1wQhg<7wMiKb<%1YS6>rey`uh-LhplV}|sF;{*> zjW2!Z5Ye~dJE~%>SXv2@8}+B#L0^_YttI{seNs84q~(-}F1k(2G|FL&G4Oqouhm7))q zJH#TbIu{T_RQoGBp3q)^S7?8E?RPUfzLAQ4XSAiUPb-Qfly^L#q(5e+sJw$rI0Z>x z0w5QHa#cdMajx7q6XuX?3DFBQT9<%|j&I0hTTb_jwwXGo&e?HhG_+&ZIp4JUHK@F! zgz`89>erm0paquJilGL$wd_diIn9dB`Iws7Sg-<5A&k3=47?RIENW@qNa*XyKm zrp$867I02s;S-G31zSrpk)Mr+p6n>eY)zAKJ;r45xo6WPu{Boc2os}Mc8;#sXtoC1 zpnNMnFHWt=4zwm-t$iVDk4!5dh_n+tR+FGosjBtP62BCJRv}r%z?a*ksSYzINZ|h; zenuBRkL%}EzZCrNiEGZCO?g6W%E^Fx(z_|ge-LhS!Y`U(SfQ5YFv2m{-0Ul9W%bQ1 ztvQ%$@VEQ0Xc4Px$!TrMSr*Hw$&sTF{n0S@=h5On8Bd>7V6#V_^PA87yQI1xY2O*9c>h2fiFjjFx(ipBDe`~li`TJ821vm z2wVjmb*ASxJbu;Dn|dF({fyK6Prh9A`M7f~KjZt^6Rtcy{j~c+uPtAA;;WCmbm!D7 z%325CfBwI=9r&;Lo?|!rR!q)V`olf1UG(~S6CS!U>#w7Ck3RP9jR!`(nfHgDul(ip zv)*4a?(_AbKKqNR7hX2^@el9bwf6C=9+|s1w(PveZ$GC0`N1hovsTJZUb0e=~P z{`FH1v{i0sSn^$Z^)*w@`}o<>$37E1W$(u?ym9cq9+~pr-<oDKJ9xO?HAgxd%A z8C(jAUoW^YTq#^NTpe61+}UuygZm@gU2xCC?T7mcE(IS4JsNHpToGJ3+# z2zMRaU2u=Xy#)6zTnY+#FSsFaQ{XD$>fl=8&WF1k?iRQQ;GTxt2lo-&k8u7vA@IFe z5ksN;4HZoMzAmFWB*e=zI4E9*Eufz8m!nL>?L{H>0M`uXRXpd`N}ea?OP;e9%kbs+ zG%NkiRpFBqejhqcisMtN5cu7q@b{~)CT~WiCVV%AU$5vls&KBNzlAD4elIGW`zd`U zftk{EOf^0u3rD{qCG&D6qe|&Kn1-+Ty$6}dKJf~|sS7cUf;$OrD;)hMAT6ZR-;}&u z#UE7sTPr304VA{@p)=`lKWui{iGjI*OCxqu=W;d#nH-#XS&w z^t%{&LSe1@9;HjZ%Co>6DYFm4e^Rg|QOUDW>3@{6$D>M*oBR_0k)r=n;jdHl*-9tM z8~WV>n|D<%a+gS%PbnEEpuCaZHL6@xBjC=beXt|J79p)iDcv4bHljSy`BCaB1HGro z{JQ~sFT%Bf_fK#SsPxQn#r?D5uUBQRMTP&N()WtW&j%rwepjnJ(Dv!8?4!%me@>BU z8%5)1`~og}9fytr*~aOj{HL_(GO*MY_s5_m8`$%27z!uZPS?v9drF!qD1XaT8GHa= znkAYmkT>+Z3gv*p7bzS3Q-yWCYgXmvHdSU9);QB}4iqLmsNCtg`>Em?qw3liRo3j~ z{7a;V_(-m969y^!>-uqn%f81#N78krs-M)>QhiL8pFb*oU49K^AKeCA3Ow1H>ICU( zZ{vb04PU8re+WG3Yqw>ItG?V6k~Gu?X?spmWk9!6OLHX+$<^(`Nv?E#tJ)D=?&^?e zO1G|OS1R489irb@h1cy)yQ@y=u@K3iU%I@pOd1Q(7(~-}fg_iQhSoe0;9fcTLt z($9-r_VMUU@{tadZdIp`6f1!&!yftLr znGf7@!w==TPh9`wuRp21_kKcSsNa6^eBa4C&+4}C zsX03fuFrq@B;(n(*!swO*|X<1Wu~{^l~%Q?E$5_(-@o#kjTcsS`}2rfzpc4wZm*pa zX6`#;!KzfPWa^b;My zw+?(nkT)V4b<$aCm)lcMQhOfA|Z9_qA&Lw9IykgJIx1g{*4EG}3zu>-u3!;z= zfD6IVm~J85D!5<4ZHM~<+}&`y;P%0N3gJ3M2>3F!=a5Zo=o;wfjO1S@m+X1&5 z?%#0V!=xU1oAgL@S2Ww`g@et^qB1senxhMNZ0NA+<7%BU@%nkb%}BCF+v5hRBI zLeztjYPbhgWC%mV7riBD8BmJRkI5BLEk*@apfkrwPzUO(0v%MX<4fGSa*XYYiavI) zsG1arzDTb?^~XtB^pST3+NOHHk5I)GXm<}u6}43LK`iNY3s5RS>G*UzfhNEt3RI~q z(H=Hs<((v{=)3ev{K;sPi19MyfdU;jK!TpK#4iGtsAzVgsD4z&Sx{oexL5Tz-=Z;6 zREwrb##e@$AU(>YZ`doUUr(1*`?&LA6H@#Q_ZD-)K(gejoIFI|<1wE!u``=&_7b5NL6$vOEL z2}*$gMOBs~K|P_DlJ%1^be@$%S7u16$53Y!vF;-iQZzD*c`(Y8qc&!>=j6=y*s?JrmzZJ<>R2?dZI;pNhH@d#8LW#|isO3+9QHWcG#mD^ufnYJQFQk7W=qQNi8Ixx><^bL}rd7x6Vo&^O_ zp&8TJQKi!Oj-}Vrst$dZWilQGE@GTiY%*T1YVBg=kkbB|VVdVJfxSFF(8_>vR-fCH_fOn0BKSY7?rea08T3 zpei)6#F%b1?jNQ~&`rf=8ka*XQO&g4iE&<%;ipL@Pwr?|xfCSyquD*rl4**3+Fl8y zDLW0(4H}zkAk76BNP~6_q{#|5wKQANKw9PC%%v%f2GSIYfi&0AK$=hR{EQ|P*Q(f; z!GD|b(*gxgerbhb{HszuTB*?c-!%K+89PnaS1JOUl5_Jya|ONgP3saoN1zFU-rlB( zH%}C3=KZ~5rx`o1=Fn13>@^uFE$N)5{Ism|_3IK&%RIUJq@R|0K30BO^6CACgwwJF zO%V;6A8H{q59hfXP24V5l4(MIv+~o#e1Vci6ZCrJr-}L@<)?)bnlO84rmTg~Y*{nX ztdmz*XxgbI(=v;eM#~5+fz}bU1e$Ye2{hm2xfxA3hpSZ6LK98&44UOlQb1aLpw%G{ zEj>J~fV33xy7JQ!4iOl%hNFSB2E+4sn#3DAy|y&cWSkemXf|G?2x!L63eXhu8wI2# zIiCL0+~2Q^PwNr9W?QM27^lq>#`;Vn zGd<0}9Yu2_;QiqCrqRsj(4hlV^XONck$JACq7k|Na?T3}F{6C)+vmLJzWbitB=0Fx z_n@nWepsjHnde%p!ijhp4#1As2KVWu*xT>IsdzJv#^IQUy|63h;7NE`eSpvRGu(~u z<6F1}SK-6>AXec-+^Sb`pYOXkALrsMoQfCW`M7CJ)P6nIVJ+T)({Y3P6z%`a*RdX# z;S{_Hhu|f67IwjQn29^ok@@^Tz}KWMMKTxQ2Ae=Bap zwfH(nLH`+^Pyai71Run^@kYEJi_m{Q=xB{2r{dgxOd^0_=tz z@SsJ@^UwZ#xqHw*5A?haU&W=k2=BxhScWB7fC=n?t?>)>*1nv*xCz(eQ}`GzLcdn> zb~ErctiUoHjl=N*JO{gAHXcy_>ht{^-@`X?C9c3<;r&>N6?iRafv77`C9J1a+tDz zrt0&rkly|}>GQuvwwI%%uXjJ``=O`w{csxk&py2UNmBbP_lTa~-hRK-byV)>((Air zw(@rBTd2QH{SDbp^)=N0K>Z2n*X0jM-*2VTugfo$KHog4^D4Ku^yT{N&8hMX>g}ca z%iPw~Po{oU&wJl5Ur66Cd!_G}kEI{4x1`Rg#A-QOE|$K$M`SC_?;)8f{q;!aQtlk; z)2ZJ^eWKK%%>9YL+$dpYI2YI(W5Mf&{o zp_2dHHCNk|QJ}18{P*4l>saWMw2a&z%OQGPHUIf%;sWXGbGFp~gS$yzj<-+f)baMc zrN0hbB#UIe>?OV4LwdcdJXh*m^`FzW8>W@e_oZk>WVAtKv_djke=?e%k3K)NK4i3B zWVD`Sv>s%%?qsyCWVB9Xv>Y;8TQXV}8O_LO8Dunnp7{2l9U`N7Kkjw3FUe@$zk3~R z9~o^A8EqFC&7Y6n9&H;LZ3`J~GZ}3Y8EqpOZ9N%nEg5YM8ErKgZ50{q1v1*RWVCuR z+Hx}5GBVl{GFlxOZ6O)0mW(!^j5d#qHkXW6Lq?lJMw?AWt0tpOBcoN3(JIMk6Uk^5 zWVG>Qv@$YUDH*MVj8;rW8%ahRMn)?lqYWaX6_U~VlhN|XXnn|Ny~t=i$!I;uXx+(Z zUCC&j$Y?oaw6wT#AqK=_I^LkI}J*Z>o z&%EB9dROWg`ZKS0qMkz?Lx1M=w$!tzW9ZMkZqzfVW9ZMkepKhZ4E>qsL*xUIp+EEb zmt+k6nNR;Y)9<5>p+EEb9_qWOW9ZMk{xS7!)G_pDUf)7}Gj$C8nb$W_-$)%pf9Cb| z)YnqS(4TpI4fWO3G4y9%Uq$@|>KOVnuRlw@o;rs9%UGpH^k-gQ zNWGRihW^a!^Qq6Hj-fyE`dsQY)G_pDUY|pKHgydBnb)hSPos{ZKl6GO^-Ag(`ZKRj zq+UTCLx1M=@zl$xW9ZMkUP`@$I)?tt>&4VZQpeDrd3_l50-gVQf0XIZKer8jqq^sR z;wJnfK7)(!UYv=(J>Gs44nY6B#UJmCr{V#}&8)XSLOqMzmwFC)KlNhrQ0nu^BgqTN z_mY>87m$~eSCRLV*O8Bsw~|lMPWJWtgxrqYXIHfT{mK8OKA3!vJc*pc^i#;^kY|ze z$cxE?$jixfZ2vRl=g2R6)(0(Fhcd#Kj#);w|HD`7EM?VuP#>Vjv)k5Wtlk<;;J--C<0kLo`-HL4$?{!7}A-4@kvrhPs24f+tl+y8BQRDXx|b%&$;Tk0FB zze0T)b^kq4Z~x6l(ezWOkEH$y?f*mje^Xye{Y5=Ly?q(&3#j*?exW|>@cMcFMqgj2 z=zR3;-$#81_3@nV71S$^MAQ54#CiK`s1K!HNc~yX??dK)9`zjROWD64vi}~*ik82b z{r7uc|LmHKie1t2ZujLW*OI&FVL!fYHJyK*8`|qL3ECg@>2(g*a(w-BpxEgUey2O0 zr)VDW?5{WR?4P&CGoL?)W&1zAF7f)xl=)q$^@-OXP01H&f5qz)Qt|+u5ApiWl>BYV z^!{^*`1Jcza{H9_RVjH&%JgGW_TP$>`fKWc;>+{n63>2J7|$Q1CJzc#0 zr73xP%J$c#EdS(`?aL@HAJ@d-edW=3eN7BA77gU0LVPrq7RIA(JfdC`GmJ)eNtt1D zLj%%&KmQ_jmeW^8u>UiOx^$L z@F&N~O27PYW1?utxnqA_tb3DqgGT%9IAa_7^xxlTPHYZC2M)Yy(yTGJPo6MUBhZby zFvj?oW>!xM2V%-B4MSW$ zu5@gCkA+>BHd4=V2g+vC8jZhq+~M$ORG2)jO>9bhn8o~X+jd5%@}cpX+psX?as0*? zzM!y8CpyG+bpEjt(Q_bns0KFN`F3E#cx}F3@iChk?x1k&WDWzoH8f3WkR0kNdaC#+ zYtbXRElczy-~@+!xI*_iiE{Z3GiFSh zUR^%DTK7L?pGI4gUzk2hz_D5M8(WxPIHGFmgfI}d{(L{Wk9Tv!D>j~WzvJAj(x*;4 z(NA>Lbrz`=JvJhI(}O>j{6b@U`$^l({p*L1DJXAvee?}ayLo)m^G)thZ=ze7M0=}Y zJcE{YRs3n+&`G52k!ZXx?W;zUhq1oO$4#u#$n#C^NCv<^wtr$bK4@V>(iYz^QvR_y z<@ZZ&rjPK@!WNc}O+UbmcJal-ZkaJnBc(<=t%W(I&!>eQ7WRz>tzf8+99E!hY8qh)b(LrOO}E_HEv-x<}@ zb@#5|#n@k#hx@&hm-kJ+YBsu$L*pTtV`Ew}hXNeX!km(~acF5?X=`ypck_t0>Jrwa z5C=B3PVxDk;Q7OB^YfD5F(o0Qf{)4MB^j8^#F;nw zfa(qg)FH&uJ?>U_tz}QybMWl$S?a2*l+_?qz#|s%qqJ2^mG(>uc3Eo?QJMYy@BQDI zmq`NXX?u3hnR{~Y|Np!HkNdwL@4lJ$?&K$D-#a%^(`-4KrsX3~LOz_QX`5n~gVVIX zMDCrSX}aFzUeP3~Mi%ywP-6_A8r*0^De1at^qP{Fc%;7-MN&4cKvKeJHl1>{SQ?PO zh#75(q^dIGGruTBZ?gtrTS{Bg3fLd@nUeIUcNl>#BP{U9_?oU1YT9mgLiqgg`9i@+ zy2F|A75|m=cP0BHoGYuwEOp%%F~j~~J4-YD&H9oQ>|%ckD`z8OMWwDsgFXIWtL_iB zg#;ei-*=`-%dPA{VTPlKt{WYCTiDxaNKs~dy;sTl7zaX_*4%iB*4_Q+i>_C@np_qw zJM+a%Gcc_;@~g&D*IWAnt*i?&oovXUip>75o-So-UJ0wQN?WBl zngjq6lh-|6%t$PE5lu0v!k@z8}K7=e<)ZI z47Fkn(J~?3)ecR&2lSJ#Y1&RWxeR;*GNqe2{{^TQ8C@AS4OK|}7^v7{5Dd)`T{hHC z%NKK|TvahY@2Oqd)k}Zy=*k(WD@NAxE``CRS|L~`GU>`COlYE38jXZY1O67u5wq2I z-E}4NO0eYoEf6D(vdei4wYCvAUgtox)E?S)(u|u{Zq-Lt1&tAyZA&_bhVb8 z`S)P9Ze8w)|JrqntJ$^O)zs*ABQm07`y>e9Y6ath~}v{Th#v;RTMpS{S}_U zJTE?px{IDN^R%kL!iA0@PsK+?{q!BlHCS(R^`FZrx^Eqj$M%Y6aLVK*zG;uw*87G z8a@i1qB#ue-uPb8!+Tt@Pxlmk_xXYsi#Gm<^kbiXP;}o9!8wM|sl4u7$H5bK-R#T# zkEOPwZpU%QLB|otFWioUuH-RSa{r>ug$v#Blde6-^Ii8J5*1Sxx{97URBij!o?oHv zbx}8s>Rz|KxaS>Iy~TE@ddv3eo_AB0a0N@3?Po4wdPw>?=oy^)>q|6k(RGE7Z}=#g zEY^CaZrG#E!rUhJqXJ)bitej9mrUO7TIpKly2GWf+Pif|GMW4l>b4_qMt%}_8}cCX zI^;Iw<& zcC~S=py&|k?I68@1z(?x(L3!plGrW$>?K$KBG~=UV&b&K76OCii_t63z|;b$lghPd z%`>=Q0cxK`XvxG|P^*paBUu~RIy4mjjVJ!R<6vT^z#k@VB2OZ+mE7~Odo&q(&(W8s z1U+5y?61U?AgK5<=GjjOu}pdp-|;;Cdx|~r0?$CCz|;RifhWE{(N9K@vWYFlmL68EpaDHq^6j}HK;-hI?c!|@U`(5h;5YB?ZjV2R=zWJ@zaS1 zKTan5&)A|96UWJ8UgBM916}aMUr6-G_C${)PeWKKRu}?(9D!nmvFsWlL88oFC|3@zPs+AN@|3K2yW z&9MX8KTp(A#lU(@{?rZluJ4Jzo%j-0QE0=T;LA7Kzf!h;g&>nCj9oRG1lG=fMbSc?TfH6Is_-hJOU9t%2r!u5g8PXddySgc) z{jx((OR-yu50SWs#5<(;VJW^wig%H?m&9?1#dxn8ZUakwqjmprVg=d7e<@b;s^Mmq zdE#e01HI^E(!3;DS-fia$F${Rwx#?1bd?QGijw;k>T(si|JSlnX<7Zl0{n`MK!)L=U`_>7F_JyBmi zUy9Fw?CK!#KS=S*QhcQppCGZH#Oomzqant?GmuwLLG%w{H_1`amnWWsDkg0X)g<-- z5F@sPpD>{D$x93n+FvC%C=vZ$4Ap>a`w?*+Oo~(6^k^lKM8PxhCfM{$yi9IVGw~8o za^|yenw&W#%I8J7Pn0i+@_;B`6y-~zd|8x-MR`P&uZi*vQ63fL&qevBDBl+4F;TuF z%HyK^jVKeM{H-WYit=}&JSEEaMfrg!PmA)5D9?)W6H%TMrH0W>&YU31Tv6tu6dz>X z!X-C+=5!KwrmKEN{7K2bPW&;+ze4C9nfM0D{}b`mlK&p@PRTz^{PmLmJL2t<{|@m} zBtJ-ef#l=FfAR;>_f5o~lKckZk4ye8;*UzckNB4)Pdn-GkmTy_e%a&;scUjPW)=gf0Oto$uA||1D@vZ|8ufkAWg_fIh=Bl@{p+S zu`*=)WaI-mF?+#TNHU4nKw0pzt z(M}_1+JlDC8nK%pdy8TB+FPP+ZFt#KX9U~L4y|-~#0W=9*MuS+CTdE>gYX;L`?^ZY zN*wqr7Xi@Iaa|ggbWi{{EUb;(+_J{-nX|sCRnxQoTf*M1&*Cj4to0t4!DWXx*cveO zlG=#7v&-y*Q(1#{f~FA;dIL3nBhXqEiU!f5eWATfyT$OXt?~u}EneSR3Mp#X7c8*D z?F|drn>Jmbu+bLLr3U=h8{TlW;WazZ#_}L!+Vs(lTdJrTs0t^d-DNPjTlQV_Ic;rq zjeCMdFSBs#Pra3y1s=CHN;Vt*rfU;y^v1Cg6_lg=_KLROUkSCyB*@$A4tD3(xs&I*)O$E z_q5HdS5G>WlwM80@Jf30J#Z)M&B^SS#osi@c4IH1^eFkFr2JLM{*b?F=*`BSOX(G% zWYM$u3qbEp*28ybnUp_<4!!4D@82`^ zEdC0y6u`xm5S8NF0PdY>2S z*69)tF;(xwSJQVoqyW=irW=`VW%>xy9Za8KI>huPrbn3`XL^e1Crk_Ig(T7xrgo;+ zGj%epX4=4XCDRV3Ces+xEleL`x}E7xrn{LQVEP)C;Sym>ytynCVfb$CxIVo??22DUQa9 zeiSe*W=iMlseg8+bdH*E8B-_IN~RvB4NO-sje+9faDFc+{_?vRAN}%_f-SjoAGO77 z^-oNA)ON&n=``m~+qcy789a*7T9H+vcYap95x6bKrd?V#Z5ipntyY4<#M4MhGr`ZW z;2Xh{jY@#x86Z7FqC~hJg%y)O^}K=lMg9~9E9}*CL&9Wl#bp1)gC~21!3ulz zoQ^QrTQS+I=SdHMCwqmT1V2`PV(>!=V&N}F0Esa9JHpSWPJ$2%D#Y4Xa%DUUD|@wmmAw^{{mX1WpY0V^_UgH#vbSQg z|1I0svAx2|-YLUN#c#!A|CKAGLI>L`tn6ujp+vYIg%y+i5w^dV?G;w`-xAhodn+dU zdVWK&jqMdy_SW&D_N|!gSFk;uUZM4%u(G#~7uhTP8uYJ0R=*$mZ-x0eBE7+~bL>Ru z9k~@>n}MmlRw_7ELH=c6^?R(9j03zMg@EZSi)w$f!rcBg;3C+o{p%@#W2yb(cZ}8b z^<}&*DYC^<`vDz*AgtEYZH(3W3^7*g{Rzfu{TybjuCIK&g&}`xeav92#(N=SHGeA@ ztMw38nD?7wgfEL}>UsG4jMa1T%L=Hy^!|D|VLSw0JzWm)3}A@Qsqwxw1BWy4KnC8O zf$9Cd)!$USfw$tS47@!9zm|dN=Pp)zX9n)fz(2^qzt6x|qwiLKB^h{O2KHs(?_}VE z8F+HxNdI*ixHSXclYxJlfeUf|%o?vf1Fy)y;S9V*;22!UMCaLTS{nbO)aSdkrtN@c zBQB|JpiQ7VL7PF3F?||z1#p7tQP5j~4};zY>P0gx$Ufv&WCOVkxgEI!*^j&ic`b4P zxf3~v9766wz7si&96>gbqsa6bg$^I}BKIM$L#FpqvBdg({DdXL}i(CK}9sd(w$8ETCNj7VuD zRbKm5DRF~=X#@m}BGsz=Q>XJ>;?8}m2>eoN{%$)Mg*mOcaFD62)aALi=} zcm3>c=RA)%Vo+P(Sf~%YFtbwAMOY>OKXqZb4vm?}6ORmf7LOyQ*u{dea?x)>!D#0Zfk#)}D(4wFzg9NLHhbJV5}lU&3I{3#dhh?O7o z21}6<^5;3Ll9~ZWS^CVzTspXsu_`dk=~eL=TEc?HDHwH*Wz6Orv#n=UM7xf%1{}Nb z;}@7gQ!Injyf$=?T9efd_ZXFBTq)L zYyaFD{kU5E5?^n@kIB)Z`USoG-L>9^D@qT9LTjU43tVQI z?ToV@WvZ!J7!hM?-He*F&$A*&IXT}tyWkZ@9T?^D0;_Y}6+eDQN3Q5U%Px$bUFmDg9CRIEQ1gVX7M+Z#opo zXvKN8_=gtWpwCdzoljd_Ep*cyGaR~Qj<{ns&e7>6iVaw=X!N%Sy=F8__f{46>}IE9 zc5P&FAk^Xw&>nt)p(p)0)&58qW=R@dghBaIZ+LBO5T73m6hZSMOy;YYQYQ{C!pJEj z!sj8}AB?bpA-%AbbE$W&ah{$xbT`y=WB4-eXXz!-Sgu=#v!V(g1HFyGt65gPAbzT7*U(Rs1I$a&rQPD5?U(a p6S7mCPiBu6NmGbF=!u2W8jwpM*wYCYx;KLJ(54FcUU{|mGr&~g9( literal 0 HcmV?d00001 diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/binding.Makefile b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/binding.Makefile new file mode 100644 index 0000000..a69b3d2 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/binding.Makefile @@ -0,0 +1,6 @@ +# This file is generated by gyp; do not edit. + +export builddir_name ?= build/./. +.PHONY: all +all: + $(MAKE) bufferutil validation diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/bufferutil.target.mk b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/bufferutil.target.mk new file mode 100644 index 0000000..303a6fa --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/bufferutil.target.mk @@ -0,0 +1,158 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := bufferutil +DEFS_Debug := \ + '-D_DARWIN_USE_64_BIT_INODE=1' \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-DBUILDING_NODE_EXTENSION' \ + '-DDEBUG' \ + '-D_DEBUG' + +# Flags passed to all source files. +CFLAGS_Debug := \ + -O0 \ + -gdwarf-2 \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -Wall \ + -Wendif-labels \ + -W \ + -Wno-unused-parameter + +# Flags passed to only C files. +CFLAGS_C_Debug := \ + -fno-strict-aliasing + +# Flags passed to only C++ files. +CFLAGS_CC_Debug := \ + -fno-rtti \ + -fno-exceptions \ + -fno-threadsafe-statics \ + -fno-strict-aliasing + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Debug := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Debug := + +INCS_Debug := \ + -I/Users/joshteng/.node-gyp/0.10.15/src \ + -I/Users/joshteng/.node-gyp/0.10.15/deps/uv/include \ + -I/Users/joshteng/.node-gyp/0.10.15/deps/v8/include \ + -I/Users/joshteng/Desktop/chat_with_node/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan + +DEFS_Release := \ + '-D_DARWIN_USE_64_BIT_INODE=1' \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-DBUILDING_NODE_EXTENSION' + +# Flags passed to all source files. +CFLAGS_Release := \ + -Os \ + -gdwarf-2 \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -Wall \ + -Wendif-labels \ + -W \ + -Wno-unused-parameter + +# Flags passed to only C files. +CFLAGS_C_Release := \ + -fno-strict-aliasing + +# Flags passed to only C++ files. +CFLAGS_CC_Release := \ + -fno-rtti \ + -fno-exceptions \ + -fno-threadsafe-statics \ + -fno-strict-aliasing + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Release := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Release := + +INCS_Release := \ + -I/Users/joshteng/.node-gyp/0.10.15/src \ + -I/Users/joshteng/.node-gyp/0.10.15/deps/uv/include \ + -I/Users/joshteng/.node-gyp/0.10.15/deps/v8/include \ + -I/Users/joshteng/Desktop/chat_with_node/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan + +OBJS := \ + $(obj).target/$(TARGET)/src/bufferutil.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Debug := \ + -Wl,-search_paths_first \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -L$(builddir) \ + -install_name @rpath/bufferutil.node + +LIBTOOLFLAGS_Debug := \ + -Wl,-search_paths_first + +LDFLAGS_Release := \ + -Wl,-search_paths_first \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -L$(builddir) \ + -install_name @rpath/bufferutil.node + +LIBTOOLFLAGS_Release := \ + -Wl,-search_paths_first + +LIBS := \ + -undefined dynamic_lookup + +$(builddir)/bufferutil.node: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/bufferutil.node: LIBS := $(LIBS) +$(builddir)/bufferutil.node: GYP_LIBTOOLFLAGS := $(LIBTOOLFLAGS_$(BUILDTYPE)) +$(builddir)/bufferutil.node: TOOLSET := $(TOOLSET) +$(builddir)/bufferutil.node: $(OBJS) FORCE_DO_CMD + $(call do_cmd,solink_module) + +all_deps += $(builddir)/bufferutil.node +# Add target alias +.PHONY: bufferutil +bufferutil: $(builddir)/bufferutil.node + +# Short alias for building this executable. +.PHONY: bufferutil.node +bufferutil.node: $(builddir)/bufferutil.node + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/bufferutil.node + diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/config.gypi b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/config.gypi new file mode 100644 index 0000000..efd98f9 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/config.gypi @@ -0,0 +1,112 @@ +# Do not edit. File was generated by node-gyp's "configure" step +{ + "target_defaults": { + "cflags": [], + "default_configuration": "Release", + "defines": [], + "include_dirs": [], + "libraries": [] + }, + "variables": { + "clang": 1, + "host_arch": "x64", + "node_install_npm": "true", + "node_prefix": "/usr/local/Cellar/node/0.10.15", + "node_shared_cares": "false", + "node_shared_http_parser": "false", + "node_shared_libuv": "false", + "node_shared_openssl": "false", + "node_shared_v8": "false", + "node_shared_zlib": "false", + "node_tag": "", + "node_unsafe_optimizations": 0, + "node_use_dtrace": "true", + "node_use_etw": "false", + "node_use_openssl": "true", + "node_use_perfctr": "false", + "python": "/usr/bin/python", + "target_arch": "x64", + "v8_enable_gdbjit": 0, + "v8_no_strict_aliasing": 1, + "v8_use_snapshot": "true", + "nodedir": "/Users/joshteng/.node-gyp/0.10.15", + "copy_dev_lib": "true", + "standalone_static_library": 1, + "save_dev": "", + "browser": "", + "viewer": "man", + "rollback": "true", + "usage": "", + "globalignorefile": "/usr/local/etc/npmignore", + "init_author_url": "", + "shell": "/bin/zsh", + "parseable": "", + "shrinkwrap": "true", + "userignorefile": "/Users/joshteng/.npmignore", + "cache_max": "null", + "init_author_email": "", + "sign_git_tag": "", + "ignore": "", + "long": "", + "registry": "https://registry.npmjs.org/", + "fetch_retries": "2", + "npat": "", + "message": "%s", + "versions": "", + "globalconfig": "/usr/local/etc/npmrc", + "always_auth": "", + "cache_lock_retries": "10", + "fetch_retry_mintimeout": "10000", + "proprietary_attribs": "true", + "coverage": "", + "json": "", + "pre": "", + "description": "true", + "engine_strict": "", + "https_proxy": "", + "init_module": "/Users/joshteng/.npm-init.js", + "userconfig": "/Users/joshteng/.npmrc", + "npaturl": "http://npat.npmjs.org/", + "node_version": "v0.10.15", + "user": "", + "editor": "vi", + "save": "", + "tag": "latest", + "global": "", + "optional": "true", + "username": "", + "bin_links": "true", + "force": "", + "searchopts": "", + "depth": "null", + "rebuild_bundle": "true", + "searchsort": "name", + "unicode": "true", + "yes": "", + "fetch_retry_maxtimeout": "60000", + "strict_ssl": "true", + "dev": "", + "fetch_retry_factor": "10", + "group": "20", + "cache_lock_stale": "60000", + "version": "", + "cache_min": "10", + "cache": "/Users/joshteng/.npm", + "searchexclude": "", + "color": "true", + "save_optional": "", + "user_agent": "node/v0.10.15 darwin x64", + "cache_lock_wait": "10000", + "production": "", + "save_bundle": "", + "init_version": "0.0.0", + "umask": "18", + "git": "git", + "init_author_name": "", + "onload_script": "", + "tmp": "/var/folders/f1/j393h3fd2rn_97c0qx67bbsm0000gn/T/", + "unsafe_perm": "true", + "link": "", + "prefix": "/usr/local" + } +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/gyp-mac-tool b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/gyp-mac-tool new file mode 100755 index 0000000..2f5e141 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/gyp-mac-tool @@ -0,0 +1,224 @@ +#!/usr/bin/env python +# Generated by gyp. Do not edit. +# Copyright (c) 2012 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Utility functions to perform Xcode-style build steps. + +These functions are executed via gyp-mac-tool when using the Makefile generator. +""" + +import fcntl +import os +import plistlib +import re +import shutil +import string +import subprocess +import sys + + +def main(args): + executor = MacTool() + exit_code = executor.Dispatch(args) + if exit_code is not None: + sys.exit(exit_code) + + +class MacTool(object): + """This class performs all the Mac tooling steps. The methods can either be + executed directly, or dispatched from an argument list.""" + + def Dispatch(self, args): + """Dispatches a string command to a method.""" + if len(args) < 1: + raise Exception("Not enough arguments") + + method = "Exec%s" % self._CommandifyName(args[0]) + return getattr(self, method)(*args[1:]) + + def _CommandifyName(self, name_string): + """Transforms a tool name like copy-info-plist to CopyInfoPlist""" + return name_string.title().replace('-', '') + + def ExecCopyBundleResource(self, source, dest): + """Copies a resource file to the bundle/Resources directory, performing any + necessary compilation on each resource.""" + extension = os.path.splitext(source)[1].lower() + if os.path.isdir(source): + # Copy tree. + if os.path.exists(dest): + shutil.rmtree(dest) + shutil.copytree(source, dest) + elif extension == '.xib': + return self._CopyXIBFile(source, dest) + elif extension == '.strings': + self._CopyStringsFile(source, dest) + else: + shutil.copyfile(source, dest) + + def _CopyXIBFile(self, source, dest): + """Compiles a XIB file with ibtool into a binary plist in the bundle.""" + tools_dir = os.environ.get('DEVELOPER_BIN_DIR', '/usr/bin') + args = [os.path.join(tools_dir, 'ibtool'), '--errors', '--warnings', + '--notices', '--output-format', 'human-readable-text', '--compile', + dest, source] + ibtool_section_re = re.compile(r'/\*.*\*/') + ibtool_re = re.compile(r'.*note:.*is clipping its content') + ibtoolout = subprocess.Popen(args, stdout=subprocess.PIPE) + current_section_header = None + for line in ibtoolout.stdout: + if ibtool_section_re.match(line): + current_section_header = line + elif not ibtool_re.match(line): + if current_section_header: + sys.stdout.write(current_section_header) + current_section_header = None + sys.stdout.write(line) + return ibtoolout.returncode + + def _CopyStringsFile(self, source, dest): + """Copies a .strings file using iconv to reconvert the input into UTF-16.""" + input_code = self._DetectInputEncoding(source) or "UTF-8" + + # Xcode's CpyCopyStringsFile / builtin-copyStrings seems to call + # CFPropertyListCreateFromXMLData() behind the scenes; at least it prints + # CFPropertyListCreateFromXMLData(): Old-style plist parser: missing + # semicolon in dictionary. + # on invalid files. Do the same kind of validation. + import CoreFoundation + s = open(source).read() + d = CoreFoundation.CFDataCreate(None, s, len(s)) + _, error = CoreFoundation.CFPropertyListCreateFromXMLData(None, d, 0, None) + if error: + return + + fp = open(dest, 'w') + args = ['/usr/bin/iconv', '--from-code', input_code, '--to-code', + 'UTF-16', source] + subprocess.call(args, stdout=fp) + fp.close() + + def _DetectInputEncoding(self, file_name): + """Reads the first few bytes from file_name and tries to guess the text + encoding. Returns None as a guess if it can't detect it.""" + fp = open(file_name, 'rb') + try: + header = fp.read(3) + except e: + fp.close() + return None + fp.close() + if header.startswith("\xFE\xFF"): + return "UTF-16BE" + elif header.startswith("\xFF\xFE"): + return "UTF-16LE" + elif header.startswith("\xEF\xBB\xBF"): + return "UTF-8" + else: + return None + + def ExecCopyInfoPlist(self, source, dest): + """Copies the |source| Info.plist to the destination directory |dest|.""" + # Read the source Info.plist into memory. + fd = open(source, 'r') + lines = fd.read() + fd.close() + + # Go through all the environment variables and replace them as variables in + # the file. + for key in os.environ: + if key.startswith('_'): + continue + evar = '${%s}' % key + lines = string.replace(lines, evar, os.environ[key]) + + # Write out the file with variables replaced. + fd = open(dest, 'w') + fd.write(lines) + fd.close() + + # Now write out PkgInfo file now that the Info.plist file has been + # "compiled". + self._WritePkgInfo(dest) + + def _WritePkgInfo(self, info_plist): + """This writes the PkgInfo file from the data stored in Info.plist.""" + plist = plistlib.readPlist(info_plist) + if not plist: + return + + # Only create PkgInfo for executable types. + package_type = plist['CFBundlePackageType'] + if package_type != 'APPL': + return + + # The format of PkgInfo is eight characters, representing the bundle type + # and bundle signature, each four characters. If that is missing, four + # '?' characters are used instead. + signature_code = plist.get('CFBundleSignature', '????') + if len(signature_code) != 4: # Wrong length resets everything, too. + signature_code = '?' * 4 + + dest = os.path.join(os.path.dirname(info_plist), 'PkgInfo') + fp = open(dest, 'w') + fp.write('%s%s' % (package_type, signature_code)) + fp.close() + + def ExecFlock(self, lockfile, *cmd_list): + """Emulates the most basic behavior of Linux's flock(1).""" + # Rely on exception handling to report errors. + fd = os.open(lockfile, os.O_RDONLY|os.O_NOCTTY|os.O_CREAT, 0o666) + fcntl.flock(fd, fcntl.LOCK_EX) + return subprocess.call(cmd_list) + + def ExecFilterLibtool(self, *cmd_list): + """Calls libtool and filters out 'libtool: file: foo.o has no symbols'.""" + libtool_re = re.compile(r'^libtool: file: .* has no symbols$') + libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE) + _, err = libtoolout.communicate() + for line in err.splitlines(): + if not libtool_re.match(line): + print >>sys.stderr, line + return libtoolout.returncode + + def ExecPackageFramework(self, framework, version): + """Takes a path to Something.framework and the Current version of that and + sets up all the symlinks.""" + # Find the name of the binary based on the part before the ".framework". + binary = os.path.basename(framework).split('.')[0] + + CURRENT = 'Current' + RESOURCES = 'Resources' + VERSIONS = 'Versions' + + if not os.path.exists(os.path.join(framework, VERSIONS, version, binary)): + # Binary-less frameworks don't seem to contain symlinks (see e.g. + # chromium's out/Debug/org.chromium.Chromium.manifest/ bundle). + return + + # Move into the framework directory to set the symlinks correctly. + pwd = os.getcwd() + os.chdir(framework) + + # Set up the Current version. + self._Relink(version, os.path.join(VERSIONS, CURRENT)) + + # Set up the root symlinks. + self._Relink(os.path.join(VERSIONS, CURRENT, binary), binary) + self._Relink(os.path.join(VERSIONS, CURRENT, RESOURCES), RESOURCES) + + # Back to where we were before! + os.chdir(pwd) + + def _Relink(self, dest, link): + """Creates a symlink to |dest| named |link|. If |link| already exists, + it is overwritten.""" + if os.path.lexists(link): + os.remove(link) + os.symlink(dest, link) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/validation.target.mk b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/validation.target.mk new file mode 100644 index 0000000..70866fc --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/build/validation.target.mk @@ -0,0 +1,158 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := validation +DEFS_Debug := \ + '-D_DARWIN_USE_64_BIT_INODE=1' \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-DBUILDING_NODE_EXTENSION' \ + '-DDEBUG' \ + '-D_DEBUG' + +# Flags passed to all source files. +CFLAGS_Debug := \ + -O0 \ + -gdwarf-2 \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -Wall \ + -Wendif-labels \ + -W \ + -Wno-unused-parameter + +# Flags passed to only C files. +CFLAGS_C_Debug := \ + -fno-strict-aliasing + +# Flags passed to only C++ files. +CFLAGS_CC_Debug := \ + -fno-rtti \ + -fno-exceptions \ + -fno-threadsafe-statics \ + -fno-strict-aliasing + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Debug := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Debug := + +INCS_Debug := \ + -I/Users/joshteng/.node-gyp/0.10.15/src \ + -I/Users/joshteng/.node-gyp/0.10.15/deps/uv/include \ + -I/Users/joshteng/.node-gyp/0.10.15/deps/v8/include \ + -I/Users/joshteng/Desktop/chat_with_node/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan + +DEFS_Release := \ + '-D_DARWIN_USE_64_BIT_INODE=1' \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-DBUILDING_NODE_EXTENSION' + +# Flags passed to all source files. +CFLAGS_Release := \ + -Os \ + -gdwarf-2 \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -Wall \ + -Wendif-labels \ + -W \ + -Wno-unused-parameter + +# Flags passed to only C files. +CFLAGS_C_Release := \ + -fno-strict-aliasing + +# Flags passed to only C++ files. +CFLAGS_CC_Release := \ + -fno-rtti \ + -fno-exceptions \ + -fno-threadsafe-statics \ + -fno-strict-aliasing + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Release := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Release := + +INCS_Release := \ + -I/Users/joshteng/.node-gyp/0.10.15/src \ + -I/Users/joshteng/.node-gyp/0.10.15/deps/uv/include \ + -I/Users/joshteng/.node-gyp/0.10.15/deps/v8/include \ + -I/Users/joshteng/Desktop/chat_with_node/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan + +OBJS := \ + $(obj).target/$(TARGET)/src/validation.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Debug := \ + -Wl,-search_paths_first \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -L$(builddir) \ + -install_name @rpath/validation.node + +LIBTOOLFLAGS_Debug := \ + -Wl,-search_paths_first + +LDFLAGS_Release := \ + -Wl,-search_paths_first \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -L$(builddir) \ + -install_name @rpath/validation.node + +LIBTOOLFLAGS_Release := \ + -Wl,-search_paths_first + +LIBS := \ + -undefined dynamic_lookup + +$(builddir)/validation.node: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/validation.node: LIBS := $(LIBS) +$(builddir)/validation.node: GYP_LIBTOOLFLAGS := $(LIBTOOLFLAGS_$(BUILDTYPE)) +$(builddir)/validation.node: TOOLSET := $(TOOLSET) +$(builddir)/validation.node: $(OBJS) FORCE_DO_CMD + $(call do_cmd,solink_module) + +all_deps += $(builddir)/validation.node +# Add target alias +.PHONY: validation +validation: $(builddir)/validation.node + +# Short alias for building this executable. +.PHONY: validation.node +validation.node: $(builddir)/validation.node + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/validation.node + diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/builderror.log b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/builderror.log new file mode 100644 index 0000000..34f34d3 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/builderror.log @@ -0,0 +1,2 @@ +gyp http GET http://nodejs.org/dist/v0.10.15/node-v0.10.15.tar.gz +gyp http 200 http://nodejs.org/dist/v0.10.15/node-v0.10.15.tar.gz diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/doc/ws.md b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/doc/ws.md new file mode 100644 index 0000000..d84fd62 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/doc/ws.md @@ -0,0 +1,181 @@ +# ws + +## Class: ws.Server + +This class is a WebSocket server. It is an `EventEmitter`. + +### new ws.Server([options], [callback]) + +* `options` Object + * `host` String + * `port` Number + * `server` http.Server + * `verifyClient` Function + * `path` String + * `noServer` Boolean + * `disableHixie` Boolean + * `clientTracking` Boolean +* `callback` Function + +Construct a new server object. + +Either `port` or `server` must be provided, otherwise you might enable +`noServer` if you want to pass the requests directly. Please note that the +`callback` is only used when you supply the a `port` number in the options. + +### server.close([code], [data]) + +Close the server and terminate all clients + +### server.handleUpgrade(request, socket, upgradeHead, callback) + +Handles a HTTP Upgrade request. `request` is an instance of `http.ServerRequest`, `socket` is an instance of `net.Socket`. + +When the Upgrade was successfully, the `callback` will be called with a `ws.WebSocket` object as parameter. + +### Event: 'error' + +`function (error) { }` + +If the underlying server emits an error, it will be forwarded here. + +### Event: 'headers' + +`function (headers) { }` + +Emitted with the object of HTTP headers that are going to be written to the `Stream` as part of the handshake. + +### Event: 'connection' + +`function (socket) { }` + +When a new WebSocket connection is established. `socket` is an object of type `ws.WebSocket`. + + +## Class: ws.WebSocket + +This class represents a WebSocket connection. It is an `EventEmitter`. + +### new ws.WebSocket(address, [options]) + +* `address` String|Array +* `options` Object + * `protocol` String + * `agent` Agent + * `headers` Object + * `protocolVersion` Number|String + -- the following only apply if `address` is a String + * `host` String + * `origin` String + * `pfx` String|Buffer + * `key` String|Buffer + * `passphrase` String + * `cert` String|Buffer + * `ca` Array + * `ciphers` String + * `rejectUnauthorized` Boolean + +Instantiating with an `address` creates a new WebSocket client object. If `address` is an Array (request, socket, rest), it is instantiated as a Server client (e.g. called from the `ws.Server`). + +### websocket.bytesReceived + +Received bytes count. + +### websocket.readyState + +Possible states are `WebSocket.CONNECTING`, `WebSocket.OPEN`, `WebSocket.CLOSING`, `WebSocket.CLOSED`. + +### websocket.protocolVersion + +The WebSocket protocol version used for this connection, `8`, `13` or `hixie-76` (the latter only for server clients). + +### websocket.url + +The URL of the WebSocket server (only for clients) + +### websocket.supports + +Describes the feature of the used protocol version. E.g. `supports.binary` is a boolean that describes if the connection supports binary messages. + +### websocket.close([code], [data]) + +Gracefully closes the connection, after sending a description message + +### websocket.pause() + +Pause the client stream + +### websocket.ping([data], [options], [dontFailWhenClosed]) + +Sends a ping. `data` is sent, `options` is an object with members `mask` and `binary`. `dontFailWhenClosed` indicates whether or not to throw if the connection isnt open. + +### websocket.pong([data], [options], [dontFailWhenClosed]) + +Sends a pong. `data` is sent, `options` is an object with members `mask` and `binary`. `dontFailWhenClosed` indicates whether or not to throw if the connection isnt open. + + +### websocket.resume() + +Resume the client stream + +### websocket.send(data, [options], [callback]) + +Sends `data` through the connection. `options` can be an object with members `mask` and `binary`. The optional `callback` is executed after the send completes. + +### websocket.stream([options], callback) + +Streams data through calls to a user supplied function. `options` can be an object with members `mask` and `binary`. `callback` is executed on successive ticks of which send is `function (data, final)`. + +### websocket.terminate() + +Immediately shuts down the connection + +### websocket.onopen +### websocket.onerror +### websocket.onclose +### websocket.onmessage + +Emulates the W3C Browser based WebSocket interface using function members. + +### websocket.addEventListener(method, listener) + +Emulates the W3C Browser based WebSocket interface using addEventListener. + +### Event: 'error' + +`function (error) { }` + +If the client emits an error, this event is emitted (errors from the underlying `net.Socket` are forwarded here). + +### Event: 'close' + +`function (code, message) { }` + +Is emitted when the connection is closed. `code` is defined in the WebSocket specification. + +The `close` event is also emitted when then underlying `net.Socket` closes the connection (`end` or `close`). + +### Event: 'message' + +`function (data, flags) { }` + +Is emitted when data is received. `flags` is an object with member `binary`. + +### Event: 'ping' + +`function (data, flags) { }` + +Is emitted when a ping is received. `flags` is an object with member `binary`. + +### Event: 'pong' + +`function (data, flags) { }` + +Is emitted when a pong is received. `flags` is an object with member `binary`. + +### Event: 'open' + +`function () { }` + +Emitted when the connection is established. + diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/.npmignore b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/.npmignore new file mode 100644 index 0000000..dcd5756 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/.npmignore @@ -0,0 +1 @@ +uploaded diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/package.json b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/package.json new file mode 100644 index 0000000..7816f27 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/package.json @@ -0,0 +1,18 @@ +{ + "author": "", + "name": "fileapi", + "version": "0.0.0", + "repository": { + "type": "git", + "url": "git://github.com/einaros/ws.git" + }, + "engines": { + "node": "~0.6.8" + }, + "dependencies": { + "express": "latest", + "ansi": "https://github.com/einaros/ansi.js/tarball/master" + }, + "devDependencies": {}, + "optionalDependencies": {} +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/public/app.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/public/app.js new file mode 100644 index 0000000..e812cc3 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/public/app.js @@ -0,0 +1,39 @@ +function onFilesSelected(e) { + var button = e.srcElement; + button.disabled = true; + var progress = document.querySelector('div#progress'); + progress.innerHTML = '0%'; + var files = e.target.files; + var totalFiles = files.length; + var filesSent = 0; + if (totalFiles) { + var uploader = new Uploader('ws://localhost:8080', function () { + Array.prototype.slice.call(files, 0).forEach(function(file) { + if (file.name == '.') { + --totalFiles; + return; + } + uploader.sendFile(file, function(error) { + if (error) { + console.log(error); + return; + } + ++filesSent; + progress.innerHTML = ~~(filesSent / totalFiles * 100) + '%'; + console.log('Sent: ' + file.name); + }); + }); + }); + } + uploader.ondone = function() { + uploader.close(); + progress.innerHTML = '100% done, ' + totalFiles + ' files sent.'; + } +} + +window.onload = function() { + var importButtons = document.querySelectorAll('[type="file"]'); + Array.prototype.slice.call(importButtons, 0).forEach(function(importButton) { + importButton.addEventListener('change', onFilesSelected, false); + }); +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/public/index.html b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/public/index.html new file mode 100644 index 0000000..0d463dd --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/public/index.html @@ -0,0 +1,22 @@ + + + + + + + + +

    This example will upload an entire directory tree to the node.js server via a fast and persistent WebSocket connection.

    +

    Note that the example is Chrome only for now.

    +

    + Upload status: +
    Please select a directory to upload.
    + + diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/public/uploader.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/public/uploader.js new file mode 100644 index 0000000..0c34a7f --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/public/uploader.js @@ -0,0 +1,55 @@ +function Uploader(url, cb) { + this.ws = new WebSocket(url); + if (cb) this.ws.onopen = cb; + this.sendQueue = []; + this.sending = null; + this.sendCallback = null; + this.ondone = null; + var self = this; + this.ws.onmessage = function(event) { + var data = JSON.parse(event.data); + if (data.event == 'complete') { + if (data.path != self.sending.path) { + self.sendQueue = []; + self.sending = null; + self.sendCallback = null; + throw new Error('Got message for wrong file!'); + } + self.sending = null; + var callback = self.sendCallback; + self.sendCallback = null; + if (callback) callback(); + if (self.sendQueue.length === 0 && self.ondone) self.ondone(null); + if (self.sendQueue.length > 0) { + var args = self.sendQueue.pop(); + setTimeout(function() { self.sendFile.apply(self, args); }, 0); + } + } + else if (data.event == 'error') { + self.sendQueue = []; + self.sending = null; + var callback = self.sendCallback; + self.sendCallback = null; + var error = new Error('Server reported send error for file ' + data.path); + if (callback) callback(error); + if (self.ondone) self.ondone(error); + } + } +} + +Uploader.prototype.sendFile = function(file, cb) { + if (this.ws.readyState != WebSocket.OPEN) throw new Error('Not connected'); + if (this.sending) { + this.sendQueue.push(arguments); + return; + } + var fileData = { name: file.name, path: file.webkitRelativePath }; + this.sending = fileData; + this.sendCallback = cb; + this.ws.send(JSON.stringify(fileData)); + this.ws.send(file); +} + +Uploader.prototype.close = function() { + this.ws.close(); +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/server.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/server.js new file mode 100644 index 0000000..badfeba --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/fileapi/server.js @@ -0,0 +1,103 @@ +var WebSocketServer = require('../../').Server + , express = require('express') + , fs = require('fs') + , http = require('http') + , util = require('util') + , path = require('path') + , app = express.createServer() + , events = require('events') + , ansi = require('ansi') + , cursor = ansi(process.stdout); + +function BandwidthSampler(ws, interval) { + interval = interval || 2000; + var previousByteCount = 0; + var self = this; + var intervalId = setInterval(function() { + var byteCount = ws.bytesReceived; + var bytesPerSec = (byteCount - previousByteCount) / (interval / 1000); + previousByteCount = byteCount; + self.emit('sample', bytesPerSec); + }, interval); + ws.on('close', function() { + clearInterval(intervalId); + }); +} +util.inherits(BandwidthSampler, events.EventEmitter); + +function makePathForFile(filePath, prefix, cb) { + if (typeof cb !== 'function') throw new Error('callback is required'); + filePath = path.dirname(path.normalize(filePath)).replace(/^(\/|\\)+/, ''); + var pieces = filePath.split(/(\\|\/)/); + var incrementalPath = prefix; + function step(error) { + if (error) return cb(error); + if (pieces.length == 0) return cb(null, incrementalPath); + incrementalPath += '/' + pieces.shift(); + fs.exists(incrementalPath, function(exists) { + if (!exists) fs.mkdir(incrementalPath, step); + else process.nextTick(step); + }); + } + step(); +} + +cursor.eraseData(2).goto(1, 1); +app.use(express.static(__dirname + '/public')); + +var clientId = 0; +var wss = new WebSocketServer({server: app}); +wss.on('connection', function(ws) { + var thisId = ++clientId; + cursor.goto(1, 4 + thisId).eraseLine(); + console.log('Client #%d connected', thisId); + + var sampler = new BandwidthSampler(ws); + sampler.on('sample', function(bps) { + cursor.goto(1, 4 + thisId).eraseLine(); + console.log('WebSocket #%d incoming bandwidth: %d MB/s', thisId, Math.round(bps / (1024*1024))); + }); + + var filesReceived = 0; + var currentFile = null; + ws.on('message', function(data, flags) { + if (!flags.binary) { + currentFile = JSON.parse(data); + // note: a real-world app would want to sanity check the data + } + else { + if (currentFile == null) return; + makePathForFile(currentFile.path, __dirname + '/uploaded', function(error, path) { + if (error) { + console.log(error); + ws.send(JSON.stringify({event: 'error', path: currentFile.path, message: error.message})); + return; + } + fs.writeFile(path + '/' + currentFile.name, data, function(error) { + ++filesReceived; + // console.log('received %d bytes long file, %s', data.length, currentFile.path); + ws.send(JSON.stringify({event: 'complete', path: currentFile.path})); + currentFile = null; + }); + }); + } + }); + + ws.on('close', function() { + cursor.goto(1, 4 + thisId).eraseLine(); + console.log('Client #%d disconnected. %d files received.', thisId, filesReceived); + }); + + ws.on('error', function(e) { + cursor.goto(1, 4 + thisId).eraseLine(); + console.log('Client #%d error: %s', thisId, e.message); + }); +}); + +fs.mkdir(__dirname + '/uploaded', function(error) { + // ignore errors, most likely means directory exists + console.log('Uploaded files will be saved to %s/uploaded.', __dirname); + console.log('Remember to wipe this directory if you upload lots and lots.'); + app.listen(8080); + console.log('Listening on http://localhost:8080'); +}); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats-express_3/package.json b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats-express_3/package.json new file mode 100644 index 0000000..99722c4 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats-express_3/package.json @@ -0,0 +1,17 @@ +{ + "author": "", + "name": "serverstats", + "version": "0.0.0", + "repository": { + "type": "git", + "url": "git://github.com/einaros/ws.git" + }, + "engines": { + "node": ">0.4.0" + }, + "dependencies": { + "express": "~3.0.0" + }, + "devDependencies": {}, + "optionalDependencies": {} +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats-express_3/public/index.html b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats-express_3/public/index.html new file mode 100644 index 0000000..24d84e1 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats-express_3/public/index.html @@ -0,0 +1,33 @@ + + + + + + + + Server Stats
    + RSS:

    + Heap total:

    + Heap used:

    + + diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats-express_3/server.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats-express_3/server.js new file mode 100644 index 0000000..88bbc9e --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats-express_3/server.js @@ -0,0 +1,21 @@ +var WebSocketServer = require('../../').Server + , http = require('http') + , express = require('express') + , app = express(); + +app.use(express.static(__dirname + '/public')); + +var server = http.createServer(app); +server.listen(8080); + +var wss = new WebSocketServer({server: server}); +wss.on('connection', function(ws) { + var id = setInterval(function() { + ws.send(JSON.stringify(process.memoryUsage()), function() { /* ignore errors */ }); + }, 100); + console.log('started client interval'); + ws.on('close', function() { + console.log('stopping client interval'); + clearInterval(id); + }); +}); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats/package.json b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats/package.json new file mode 100644 index 0000000..65c900a --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats/package.json @@ -0,0 +1,17 @@ +{ + "author": "", + "name": "serverstats", + "version": "0.0.0", + "repository": { + "type": "git", + "url": "git://github.com/einaros/ws.git" + }, + "engines": { + "node": ">0.4.0" + }, + "dependencies": { + "express": "2.x" + }, + "devDependencies": {}, + "optionalDependencies": {} +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats/public/index.html b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats/public/index.html new file mode 100644 index 0000000..24d84e1 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats/public/index.html @@ -0,0 +1,33 @@ + + + + + + + + Server Stats
    + RSS:

    + Heap total:

    + Heap used:

    + + diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats/server.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats/server.js new file mode 100644 index 0000000..0bbce36 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/serverstats/server.js @@ -0,0 +1,19 @@ +var WebSocketServer = require('../../').Server + , http = require('http') + , express = require('express') + , app = express.createServer(); + +app.use(express.static(__dirname + '/public')); +app.listen(8080); + +var wss = new WebSocketServer({server: app}); +wss.on('connection', function(ws) { + var id = setInterval(function() { + ws.send(JSON.stringify(process.memoryUsage()), function() { /* ignore errors */ }); + }, 100); + console.log('started client interval'); + ws.on('close', function() { + console.log('stopping client interval'); + clearInterval(id); + }) +}); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/ssl.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/ssl.js new file mode 100644 index 0000000..bf1bf53 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/examples/ssl.js @@ -0,0 +1,59 @@ + +(function(){ + + "use strict"; + + var fs = require('fs'); + + // you'll probably load configuration from config + var cfg = { + ssl: true, + port: 8080, + ssl_key: '/path/to/you/ssl.key', + ssl_cert: '/path/to/you/ssl.crt' + }; + + var httpServ = ( cfg.ssl ) ? require('https') : require('http'); + + var WebSocketServer = require('../').Server; + + var app = null; + + // dummy request processing + var processRequest = function( req, res ) { + + res.writeHead(200); + res.end("All glory to WebSockets!\n"); + }; + + if ( cfg.ssl ) { + + app = httpServ.createServer({ + + // providing server with SSL key/cert + key: fs.readFileSync( cfg.ssl_key ), + cert: fs.readFileSync( cfg.ssl_cert ) + + }, processRequest ).listen( cfg.port ); + + } else { + + app = httpServ.createServer( processRequest ).listen( cfg.port ); + } + + // passing or reference to web server so WS would knew port and SSL capabilities + var wss = new WebSocketServer( { server: app } ); + + + wss.on( 'connection', function ( wsConnect ) { + + wsConnect.on( 'message', function ( message ) { + + console.log( message ); + + }); + + }); + + +}()); \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/index.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/index.js new file mode 100644 index 0000000..3423ff2 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/index.js @@ -0,0 +1,26 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +module.exports = require('./lib/WebSocket'); +module.exports.Server = require('./lib/WebSocketServer'); +module.exports.Sender = require('./lib/Sender'); +module.exports.Receiver = require('./lib/Receiver'); + +module.exports.createServer = function (options, connectionListener) { + var server = new module.exports.Server(options); + if (typeof connectionListener === 'function') { + server.on('connection', connectionListener); + } + return server; +}; + +module.exports.connect = module.exports.createConnection = function (address, openListener) { + var client = new module.exports(address); + if (typeof openListener === 'function') { + client.on('open', openListener); + } + return client; +}; diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/BufferPool.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/BufferPool.js new file mode 100644 index 0000000..faf8637 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/BufferPool.js @@ -0,0 +1,59 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var util = require('util'); + +function BufferPool(initialSize, growStrategy, shrinkStrategy) { + if (typeof initialSize === 'function') { + shrinkStrategy = growStrategy; + growStrategy = initialSize; + initialSize = 0; + } + else if (typeof initialSize === 'undefined') { + initialSize = 0; + } + this._growStrategy = (growStrategy || function(db, size) { + return db.used + size; + }).bind(null, this); + this._shrinkStrategy = (shrinkStrategy || function(db) { + return initialSize; + }).bind(null, this); + this._buffer = initialSize ? new Buffer(initialSize) : null; + this._offset = 0; + this._used = 0; + this._changeFactor = 0; + this.__defineGetter__('size', function(){ + return this._buffer == null ? 0 : this._buffer.length; + }); + this.__defineGetter__('used', function(){ + return this._used; + }); +} + +BufferPool.prototype.get = function(length) { + if (this._buffer == null || this._offset + length > this._buffer.length) { + var newBuf = new Buffer(this._growStrategy(length)); + this._buffer = newBuf; + this._offset = 0; + } + this._used += length; + var buf = this._buffer.slice(this._offset, this._offset + length); + this._offset += length; + return buf; +} + +BufferPool.prototype.reset = function(forceNewBuffer) { + var len = this._shrinkStrategy(); + if (len < this.size) this._changeFactor -= 1; + if (forceNewBuffer || this._changeFactor < -2) { + this._changeFactor = 0; + this._buffer = len ? new Buffer(len) : null; + } + this._offset = 0; + this._used = 0; +} + +module.exports = BufferPool; diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/BufferUtil.fallback.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/BufferUtil.fallback.js new file mode 100644 index 0000000..508542c --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/BufferUtil.fallback.js @@ -0,0 +1,47 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +module.exports.BufferUtil = { + merge: function(mergedBuffer, buffers) { + var offset = 0; + for (var i = 0, l = buffers.length; i < l; ++i) { + var buf = buffers[i]; + buf.copy(mergedBuffer, offset); + offset += buf.length; + } + }, + mask: function(source, mask, output, offset, length) { + var maskNum = mask.readUInt32LE(0, true); + var i = 0; + for (; i < length - 3; i += 4) { + var num = maskNum ^ source.readUInt32LE(i, true); + if (num < 0) num = 4294967296 + num; + output.writeUInt32LE(num, offset + i, true); + } + switch (length % 4) { + case 3: output[offset + i + 2] = source[i + 2] ^ mask[2]; + case 2: output[offset + i + 1] = source[i + 1] ^ mask[1]; + case 1: output[offset + i] = source[i] ^ mask[0]; + case 0:; + } + }, + unmask: function(data, mask) { + var maskNum = mask.readUInt32LE(0, true); + var length = data.length; + var i = 0; + for (; i < length - 3; i += 4) { + var num = maskNum ^ data.readUInt32LE(i, true); + if (num < 0) num = 4294967296 + num; + data.writeUInt32LE(num, i, true); + } + switch (length % 4) { + case 3: data[i + 2] = data[i + 2] ^ mask[2]; + case 2: data[i + 1] = data[i + 1] ^ mask[1]; + case 1: data[i] = data[i] ^ mask[0]; + case 0:; + } + } +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/BufferUtil.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/BufferUtil.js new file mode 100644 index 0000000..15d35b9 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/BufferUtil.js @@ -0,0 +1,16 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +try { + module.exports = require('../build/Release/bufferutil'); +} catch (e) { try { + module.exports = require('../build/default/bufferutil'); +} catch (e) { try { + module.exports = require('./BufferUtil.fallback'); +} catch (e) { + console.error('bufferutil.node seems to not have been built. Run npm install.'); + throw e; +}}} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/ErrorCodes.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/ErrorCodes.js new file mode 100644 index 0000000..55ebd52 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/ErrorCodes.js @@ -0,0 +1,24 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +module.exports = { + isValidErrorCode: function(code) { + return (code >= 1000 && code <= 1011 && code != 1004 && code != 1005 && code != 1006) || + (code >= 3000 && code <= 4999); + }, + 1000: 'normal', + 1001: 'going away', + 1002: 'protocol error', + 1003: 'unsupported data', + 1004: 'reserved', + 1005: 'reserved for extensions', + 1006: 'reserved for extensions', + 1007: 'inconsistent or invalid data', + 1008: 'policy violation', + 1009: 'message too big', + 1010: 'extension handshake missing', + 1011: 'an unexpected condition prevented the request from being fulfilled', +}; \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Receiver.hixie.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Receiver.hixie.js new file mode 100644 index 0000000..f54ad96 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Receiver.hixie.js @@ -0,0 +1,180 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var util = require('util'); + +/** + * State constants + */ + +var EMPTY = 0 + , BODY = 1; +var BINARYLENGTH = 2 + , BINARYBODY = 3; + +/** + * Hixie Receiver implementation + */ + +function Receiver () { + this.state = EMPTY; + this.buffers = []; + this.messageEnd = -1; + this.spanLength = 0; + this.dead = false; + + this.onerror = function() {}; + this.ontext = function() {}; + this.onbinary = function() {}; + this.onclose = function() {}; + this.onping = function() {}; + this.onpong = function() {}; +} + +module.exports = Receiver; + +/** + * Add new data to the parser. + * + * @api public + */ + +Receiver.prototype.add = function(data) { + var self = this; + function doAdd() { + if (self.state === EMPTY) { + if (data.length == 2 && data[0] == 0xFF && data[1] == 0x00) { + self.reset(); + self.onclose(); + return; + } + if (data[0] === 0x80) { + self.messageEnd = 0; + self.state = BINARYLENGTH; + data = data.slice(1); + } else { + + if (data[0] !== 0x00) { + self.error('payload must start with 0x00 byte', true); + return; + } + data = data.slice(1); + self.state = BODY; + + } + } + if (self.state === BINARYLENGTH) { + var i = 0; + while ((i < data.length) && (data[i] & 0x80)) { + self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f); + ++i; + } + if (i < data.length) { + self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f); + self.state = BINARYBODY; + ++i; + } + if (i > 0) + data = data.slice(i); + } + if (self.state === BINARYBODY) { + var dataleft = self.messageEnd - self.spanLength; + if (data.length >= dataleft) { + // consume the whole buffer to finish the frame + self.buffers.push(data); + self.spanLength += dataleft; + self.messageEnd = dataleft; + return self.parse(); + } + // frame's not done even if we consume it all + self.buffers.push(data); + self.spanLength += data.length; + return; + } + self.buffers.push(data); + if ((self.messageEnd = bufferIndex(data, 0xFF)) != -1) { + self.spanLength += self.messageEnd; + return self.parse(); + } + else self.spanLength += data.length; + } + while(data) data = doAdd(); +} + +/** + * Releases all resources used by the receiver. + * + * @api public + */ + +Receiver.prototype.cleanup = function() { + this.dead = true; + this.state = EMPTY; + this.buffers = []; +} + +/** + * Process buffered data. + * + * @api public + */ + +Receiver.prototype.parse = function() { + var output = new Buffer(this.spanLength); + var outputIndex = 0; + for (var bi = 0, bl = this.buffers.length; bi < bl - 1; ++bi) { + var buffer = this.buffers[bi]; + buffer.copy(output, outputIndex); + outputIndex += buffer.length; + } + var lastBuffer = this.buffers[this.buffers.length - 1]; + if (this.messageEnd > 0) lastBuffer.copy(output, outputIndex, 0, this.messageEnd); + if (this.state !== BODY) --this.messageEnd; + var tail = null; + if (this.messageEnd < lastBuffer.length - 1) { + tail = lastBuffer.slice(this.messageEnd + 1); + } + this.reset(); + this.ontext(output.toString('utf8')); + return tail; +} + +/** + * Handles an error + * + * @api private + */ + +Receiver.prototype.error = function (reason, terminate) { + this.reset(); + this.onerror(reason, terminate); + return this; +} + +/** + * Reset parser state + * + * @api private + */ + +Receiver.prototype.reset = function (reason) { + if (this.dead) return; + this.state = EMPTY; + this.buffers = []; + this.messageEnd = -1; + this.spanLength = 0; +} + +/** + * Internal api + */ + +function bufferIndex(buffer, byte) { + for (var i = 0, l = buffer.length; i < l; ++i) { + if (buffer[i] === byte) return i; + } + return -1; +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Receiver.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Receiver.js new file mode 100644 index 0000000..2752726 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Receiver.js @@ -0,0 +1,591 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var util = require('util') + , Validation = require('./Validation').Validation + , ErrorCodes = require('./ErrorCodes') + , BufferPool = require('./BufferPool') + , bufferUtil = require('./BufferUtil').BufferUtil; + +/** + * Node version 0.4 and 0.6 compatibility + */ + +var isNodeV4 = /^v0\.4/.test(process.version); + +/** + * HyBi Receiver implementation + */ + +function Receiver () { + // memory pool for fragmented messages + var fragmentedPoolPrevUsed = -1; + this.fragmentedBufferPool = new BufferPool(1024, function(db, length) { + return db.used + length; + }, function(db) { + return fragmentedPoolPrevUsed = fragmentedPoolPrevUsed >= 0 ? + (fragmentedPoolPrevUsed + db.used) / 2 : + db.used; + }); + + // memory pool for unfragmented messages + var unfragmentedPoolPrevUsed = -1; + this.unfragmentedBufferPool = new BufferPool(1024, function(db, length) { + return db.used + length; + }, function(db) { + return unfragmentedPoolPrevUsed = unfragmentedPoolPrevUsed >= 0 ? + (unfragmentedPoolPrevUsed + db.used) / 2 : + db.used; + }); + + this.state = { + activeFragmentedOperation: null, + lastFragment: false, + masked: false, + opcode: 0, + fragmentedOperation: false + }; + this.overflow = []; + this.headerBuffer = new Buffer(10); + this.expectOffset = 0; + this.expectBuffer = null; + this.expectHandler = null; + this.currentMessage = []; + this.expectHeader(2, this.processPacket); + this.dead = false; + + this.onerror = function() {}; + this.ontext = function() {}; + this.onbinary = function() {}; + this.onclose = function() {}; + this.onping = function() {}; + this.onpong = function() {}; +}; + +module.exports = Receiver; + +/** + * Add new data to the parser. + * + * @api public + */ + +Receiver.prototype.add = function(data) { + var dataLength = data.length; + if (dataLength == 0) return; + if (this.expectBuffer == null) { + this.overflow.push(data); + return; + } + var toRead = Math.min(dataLength, this.expectBuffer.length - this.expectOffset); + fastCopy(toRead, data, this.expectBuffer, this.expectOffset); + this.expectOffset += toRead; + if (toRead < dataLength) { + this.overflow.push(data.slice(toRead)); + } + while (this.expectBuffer && this.expectOffset == this.expectBuffer.length) { + var bufferForHandler = this.expectBuffer; + this.expectBuffer = null; + this.expectOffset = 0; + this.expectHandler.call(this, bufferForHandler); + } +} + +/** + * Releases all resources used by the receiver. + * + * @api public + */ + +Receiver.prototype.cleanup = function() { + this.dead = true; + this.overflow = null; + this.headerBuffer = null; + this.expectBuffer = null; + this.expectHandler = null; + this.unfragmentedBufferPool = null; + this.fragmentedBufferPool = null; + this.state = null; + this.currentMessage = null; + this.onerror = null; + this.ontext = null; + this.onbinary = null; + this.onclose = null; + this.onping = null; + this.onpong = null; +} + +/** + * Waits for a certain amount of header bytes to be available, then fires a callback. + * + * @api private + */ + +Receiver.prototype.expectHeader = function(length, handler) { + if (length == 0) { + handler(null); + return; + } + this.expectBuffer = this.headerBuffer.slice(this.expectOffset, this.expectOffset + length); + this.expectHandler = handler; + var toRead = length; + while (toRead > 0 && this.overflow.length > 0) { + var fromOverflow = this.overflow.pop(); + if (toRead < fromOverflow.length) this.overflow.push(fromOverflow.slice(toRead)); + var read = Math.min(fromOverflow.length, toRead); + fastCopy(read, fromOverflow, this.expectBuffer, this.expectOffset); + this.expectOffset += read; + toRead -= read; + } +} + +/** + * Waits for a certain amount of data bytes to be available, then fires a callback. + * + * @api private + */ + +Receiver.prototype.expectData = function(length, handler) { + if (length == 0) { + handler(null); + return; + } + this.expectBuffer = this.allocateFromPool(length, this.state.fragmentedOperation); + this.expectHandler = handler; + var toRead = length; + while (toRead > 0 && this.overflow.length > 0) { + var fromOverflow = this.overflow.pop(); + if (toRead < fromOverflow.length) this.overflow.push(fromOverflow.slice(toRead)); + var read = Math.min(fromOverflow.length, toRead); + fastCopy(read, fromOverflow, this.expectBuffer, this.expectOffset); + this.expectOffset += read; + toRead -= read; + } +} + +/** + * Allocates memory from the buffer pool. + * + * @api private + */ + +Receiver.prototype.allocateFromPool = !isNodeV4 + ? function(length, isFragmented) { return (isFragmented ? this.fragmentedBufferPool : this.unfragmentedBufferPool).get(length); } + : function(length) { return new Buffer(length); }; + +/** + * Start processing a new packet. + * + * @api private + */ + +Receiver.prototype.processPacket = function (data) { + if ((data[0] & 0x70) != 0) { + this.error('reserved fields must be empty', 1002); + return; + } + this.state.lastFragment = (data[0] & 0x80) == 0x80; + this.state.masked = (data[1] & 0x80) == 0x80; + var opcode = data[0] & 0xf; + if (opcode === 0) { + // continuation frame + this.state.fragmentedOperation = true; + this.state.opcode = this.state.activeFragmentedOperation; + if (!(this.state.opcode == 1 || this.state.opcode == 2)) { + this.error('continuation frame cannot follow current opcode', 1002); + return; + } + } + else { + if (opcode < 3 && this.state.activeFragmentedOperation != null) { + this.error('data frames after the initial data frame must have opcode 0', 1002); + return; + } + this.state.opcode = opcode; + if (this.state.lastFragment === false) { + this.state.fragmentedOperation = true; + this.state.activeFragmentedOperation = opcode; + } + else this.state.fragmentedOperation = false; + } + var handler = opcodes[this.state.opcode]; + if (typeof handler == 'undefined') this.error('no handler for opcode ' + this.state.opcode, 1002); + else { + handler.start.call(this, data); + } +} + +/** + * Endprocessing a packet. + * + * @api private + */ + +Receiver.prototype.endPacket = function() { + if (!this.state.fragmentedOperation) this.unfragmentedBufferPool.reset(true); + else if (this.state.lastFragment) this.fragmentedBufferPool.reset(false); + this.expectOffset = 0; + this.expectBuffer = null; + this.expectHandler = null; + if (this.state.lastFragment && this.state.opcode === this.state.activeFragmentedOperation) { + // end current fragmented operation + this.state.activeFragmentedOperation = null; + } + this.state.lastFragment = false; + this.state.opcode = this.state.activeFragmentedOperation != null ? this.state.activeFragmentedOperation : 0; + this.state.masked = false; + this.expectHeader(2, this.processPacket); +} + +/** + * Reset the parser state. + * + * @api private + */ + +Receiver.prototype.reset = function() { + if (this.dead) return; + this.state = { + activeFragmentedOperation: null, + lastFragment: false, + masked: false, + opcode: 0, + fragmentedOperation: false + }; + this.fragmentedBufferPool.reset(true); + this.unfragmentedBufferPool.reset(true); + this.expectOffset = 0; + this.expectBuffer = null; + this.expectHandler = null; + this.overflow = []; + this.currentMessage = []; +} + +/** + * Unmask received data. + * + * @api private + */ + +Receiver.prototype.unmask = function (mask, buf, binary) { + if (mask != null && buf != null) bufferUtil.unmask(buf, mask); + if (binary) return buf; + return buf != null ? buf.toString('utf8') : ''; +} + +/** + * Concatenates a list of buffers. + * + * @api private + */ + +Receiver.prototype.concatBuffers = function(buffers) { + var length = 0; + for (var i = 0, l = buffers.length; i < l; ++i) length += buffers[i].length; + var mergedBuffer = new Buffer(length); + bufferUtil.merge(mergedBuffer, buffers); + return mergedBuffer; +} + +/** + * Handles an error + * + * @api private + */ + +Receiver.prototype.error = function (reason, protocolErrorCode) { + this.reset(); + this.onerror(reason, protocolErrorCode); + return this; +} + +/** + * Buffer utilities + */ + +function readUInt16BE(start) { + return (this[start]<<8) + + this[start+1]; +} + +function readUInt32BE(start) { + return (this[start]<<24) + + (this[start+1]<<16) + + (this[start+2]<<8) + + this[start+3]; +} + +function fastCopy(length, srcBuffer, dstBuffer, dstOffset) { + switch (length) { + default: srcBuffer.copy(dstBuffer, dstOffset, 0, length); break; + case 16: dstBuffer[dstOffset+15] = srcBuffer[15]; + case 15: dstBuffer[dstOffset+14] = srcBuffer[14]; + case 14: dstBuffer[dstOffset+13] = srcBuffer[13]; + case 13: dstBuffer[dstOffset+12] = srcBuffer[12]; + case 12: dstBuffer[dstOffset+11] = srcBuffer[11]; + case 11: dstBuffer[dstOffset+10] = srcBuffer[10]; + case 10: dstBuffer[dstOffset+9] = srcBuffer[9]; + case 9: dstBuffer[dstOffset+8] = srcBuffer[8]; + case 8: dstBuffer[dstOffset+7] = srcBuffer[7]; + case 7: dstBuffer[dstOffset+6] = srcBuffer[6]; + case 6: dstBuffer[dstOffset+5] = srcBuffer[5]; + case 5: dstBuffer[dstOffset+4] = srcBuffer[4]; + case 4: dstBuffer[dstOffset+3] = srcBuffer[3]; + case 3: dstBuffer[dstOffset+2] = srcBuffer[2]; + case 2: dstBuffer[dstOffset+1] = srcBuffer[1]; + case 1: dstBuffer[dstOffset] = srcBuffer[0]; + } +} + +/** + * Opcode handlers + */ + +var opcodes = { + // text + '1': { + start: function(data) { + var self = this; + // decode length + var firstLength = data[1] & 0x7f; + if (firstLength < 126) { + opcodes['1'].getData.call(self, firstLength); + } + else if (firstLength == 126) { + self.expectHeader(2, function(data) { + opcodes['1'].getData.call(self, readUInt16BE.call(data, 0)); + }); + } + else if (firstLength == 127) { + self.expectHeader(8, function(data) { + if (readUInt32BE.call(data, 0) != 0) { + self.error('packets with length spanning more than 32 bit is currently not supported', 1008); + return; + } + opcodes['1'].getData.call(self, readUInt32BE.call(data, 4)); + }); + } + }, + getData: function(length) { + var self = this; + if (self.state.masked) { + self.expectHeader(4, function(data) { + var mask = data; + self.expectData(length, function(data) { + opcodes['1'].finish.call(self, mask, data); + }); + }); + } + else { + self.expectData(length, function(data) { + opcodes['1'].finish.call(self, null, data); + }); + } + }, + finish: function(mask, data) { + var packet = this.unmask(mask, data, true); + if (packet != null) this.currentMessage.push(packet); + if (this.state.lastFragment) { + var messageBuffer = this.concatBuffers(this.currentMessage); + if (!Validation.isValidUTF8(messageBuffer)) { + this.error('invalid utf8 sequence', 1007); + return; + } + this.ontext(messageBuffer.toString('utf8'), {masked: this.state.masked, buffer: messageBuffer}); + this.currentMessage = []; + } + this.endPacket(); + } + }, + // binary + '2': { + start: function(data) { + var self = this; + // decode length + var firstLength = data[1] & 0x7f; + if (firstLength < 126) { + opcodes['2'].getData.call(self, firstLength); + } + else if (firstLength == 126) { + self.expectHeader(2, function(data) { + opcodes['2'].getData.call(self, readUInt16BE.call(data, 0)); + }); + } + else if (firstLength == 127) { + self.expectHeader(8, function(data) { + if (readUInt32BE.call(data, 0) != 0) { + self.error('packets with length spanning more than 32 bit is currently not supported', 1008); + return; + } + opcodes['2'].getData.call(self, readUInt32BE.call(data, 4, true)); + }); + } + }, + getData: function(length) { + var self = this; + if (self.state.masked) { + self.expectHeader(4, function(data) { + var mask = data; + self.expectData(length, function(data) { + opcodes['2'].finish.call(self, mask, data); + }); + }); + } + else { + self.expectData(length, function(data) { + opcodes['2'].finish.call(self, null, data); + }); + } + }, + finish: function(mask, data) { + var packet = this.unmask(mask, data, true); + if (packet != null) this.currentMessage.push(packet); + if (this.state.lastFragment) { + var messageBuffer = this.concatBuffers(this.currentMessage); + this.onbinary(messageBuffer, {masked: this.state.masked, buffer: messageBuffer}); + this.currentMessage = []; + } + this.endPacket(); + } + }, + // close + '8': { + start: function(data) { + var self = this; + if (self.state.lastFragment == false) { + self.error('fragmented close is not supported', 1002); + return; + } + + // decode length + var firstLength = data[1] & 0x7f; + if (firstLength < 126) { + opcodes['8'].getData.call(self, firstLength); + } + else { + self.error('control frames cannot have more than 125 bytes of data', 1002); + } + }, + getData: function(length) { + var self = this; + if (self.state.masked) { + self.expectHeader(4, function(data) { + var mask = data; + self.expectData(length, function(data) { + opcodes['8'].finish.call(self, mask, data); + }); + }); + } + else { + self.expectData(length, function(data) { + opcodes['8'].finish.call(self, null, data); + }); + } + }, + finish: function(mask, data) { + var self = this; + data = self.unmask(mask, data, true); + if (data && data.length == 1) { + self.error('close packets with data must be at least two bytes long', 1002); + return; + } + var code = data && data.length > 1 ? readUInt16BE.call(data, 0) : 1000; + if (!ErrorCodes.isValidErrorCode(code)) { + self.error('invalid error code', 1002); + return; + } + var message = ''; + if (data && data.length > 2) { + var messageBuffer = data.slice(2); + if (!Validation.isValidUTF8(messageBuffer)) { + self.error('invalid utf8 sequence', 1007); + return; + } + message = messageBuffer.toString('utf8'); + } + this.onclose(code, message, {masked: self.state.masked}); + this.reset(); + }, + }, + // ping + '9': { + start: function(data) { + var self = this; + if (self.state.lastFragment == false) { + self.error('fragmented ping is not supported', 1002); + return; + } + + // decode length + var firstLength = data[1] & 0x7f; + if (firstLength < 126) { + opcodes['9'].getData.call(self, firstLength); + } + else { + self.error('control frames cannot have more than 125 bytes of data', 1002); + } + }, + getData: function(length) { + var self = this; + if (self.state.masked) { + self.expectHeader(4, function(data) { + var mask = data; + self.expectData(length, function(data) { + opcodes['9'].finish.call(self, mask, data); + }); + }); + } + else { + self.expectData(length, function(data) { + opcodes['9'].finish.call(self, null, data); + }); + } + }, + finish: function(mask, data) { + this.onping(this.unmask(mask, data, true), {masked: this.state.masked, binary: true}); + this.endPacket(); + } + }, + // pong + '10': { + start: function(data) { + var self = this; + if (self.state.lastFragment == false) { + self.error('fragmented pong is not supported', 1002); + return; + } + + // decode length + var firstLength = data[1] & 0x7f; + if (firstLength < 126) { + opcodes['10'].getData.call(self, firstLength); + } + else { + self.error('control frames cannot have more than 125 bytes of data', 1002); + } + }, + getData: function(length) { + var self = this; + if (this.state.masked) { + this.expectHeader(4, function(data) { + var mask = data; + self.expectData(length, function(data) { + opcodes['10'].finish.call(self, mask, data); + }); + }); + } + else { + this.expectData(length, function(data) { + opcodes['10'].finish.call(self, null, data); + }); + } + }, + finish: function(mask, data) { + this.onpong(this.unmask(mask, data, true), {masked: this.state.masked, binary: true}); + this.endPacket(); + } + } +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Sender.hixie.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Sender.hixie.js new file mode 100644 index 0000000..1754afb --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Sender.hixie.js @@ -0,0 +1,123 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var events = require('events') + , util = require('util') + , EventEmitter = events.EventEmitter; + +/** + * Hixie Sender implementation + */ + +function Sender(socket) { + this.socket = socket; + this.continuationFrame = false; + this.isClosed = false; +} + +module.exports = Sender; + +/** + * Inherits from EventEmitter. + */ + +util.inherits(Sender, events.EventEmitter); + +/** + * Frames and writes data. + * + * @api public + */ + +Sender.prototype.send = function(data, options, cb) { + if (this.isClosed) return; +/* + if (options && options.binary) { + this.error('hixie websockets do not support binary'); + return; + } +*/ + var isString = typeof data == 'string' + , length = isString ? Buffer.byteLength(data) : data.length + , lengthbytes = (length > 127) ? 2 : 1 // assume less than 2**14 bytes + , writeStartMarker = this.continuationFrame == false + , writeEndMarker = !options || !(typeof options.fin != 'undefined' && !options.fin) + , buffer = new Buffer((writeStartMarker ? ((options && options.binary) ? (1 + lengthbytes) : 1) : 0) + length + ((writeEndMarker && !(options && options.binary)) ? 1 : 0)) + , offset = writeStartMarker ? 1 : 0; + + if (writeStartMarker) { + if (options && options.binary) { + buffer.write('\x80', 'binary'); + // assume length less than 2**14 bytes + if (lengthbytes > 1) + buffer.write(String.fromCharCode(128+length/128), offset++, 'binary'); + buffer.write(String.fromCharCode(length&0x7f), offset++, 'binary'); + } else + buffer.write('\x00', 'binary'); + } + + if (isString) buffer.write(data, offset, 'utf8'); + else data.copy(buffer, offset, 0); + + if (writeEndMarker) { + if (options && options.binary) { + // sending binary, not writing end marker + } else + buffer.write('\xff', offset + length, 'binary'); + this.continuationFrame = false; + } + else this.continuationFrame = true; + + try { + this.socket.write(buffer, 'binary', cb); + } catch (e) { + this.error(e.toString()); + } +} + +/** + * Sends a close instruction to the remote party. + * + * @api public + */ + +Sender.prototype.close = function(code, data, mask, cb) { + if (this.isClosed) return; + this.isClosed = true; + try { + if (this.continuationFrame) this.socket.write(new Buffer([0xff], 'binary')); + this.socket.write(new Buffer([0xff, 0x00]), 'binary', cb); + } catch (e) { + this.error(e.toString()); + } +} + +/** + * Sends a ping message to the remote party. Not available for hixie. + * + * @api public + */ + +Sender.prototype.ping = function(data, options) {} + +/** + * Sends a pong message to the remote party. Not available for hixie. + * + * @api public + */ + +Sender.prototype.pong = function(data, options) {} + +/** + * Handles an error + * + * @api private + */ + +Sender.prototype.error = function (reason) { + this.emit('error', reason); + return this; +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Sender.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Sender.js new file mode 100644 index 0000000..fc3b437 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Sender.js @@ -0,0 +1,227 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var events = require('events') + , util = require('util') + , EventEmitter = events.EventEmitter + , ErrorCodes = require('./ErrorCodes') + , bufferUtil = require('./BufferUtil').BufferUtil; + +/** + * HyBi Sender implementation + */ + +function Sender(socket) { + this._socket = socket; + this.firstFragment = true; +} + +/** + * Inherits from EventEmitter. + */ + +util.inherits(Sender, events.EventEmitter); + +/** + * Sends a close instruction to the remote party. + * + * @api public + */ + +Sender.prototype.close = function(code, data, mask) { + if (typeof code !== 'undefined') { + if (typeof code !== 'number' || + !ErrorCodes.isValidErrorCode(code)) throw new Error('first argument must be a valid error code number'); + } + code = code || 1000; + var dataBuffer = new Buffer(2 + (data ? Buffer.byteLength(data) : 0)); + writeUInt16BE.call(dataBuffer, code, 0); + if (dataBuffer.length > 2) dataBuffer.write(data, 2); + this.frameAndSend(0x8, dataBuffer, true, mask); +} + +/** + * Sends a ping message to the remote party. + * + * @api public + */ + +Sender.prototype.ping = function(data, options) { + var mask = options && options.mask; + this.frameAndSend(0x9, data || '', true, mask); +} + +/** + * Sends a pong message to the remote party. + * + * @api public + */ + +Sender.prototype.pong = function(data, options) { + var mask = options && options.mask; + this.frameAndSend(0xa, data || '', true, mask); +} + +/** + * Sends text or binary data to the remote party. + * + * @api public + */ + +Sender.prototype.send = function(data, options, cb) { + var finalFragment = options && options.fin === false ? false : true; + var mask = options && options.mask; + var opcode = options && options.binary ? 2 : 1; + if (this.firstFragment === false) opcode = 0; + else this.firstFragment = false; + if (finalFragment) this.firstFragment = true + this.frameAndSend(opcode, data, finalFragment, mask, cb); +} + +/** + * Frames and sends a piece of data according to the HyBi WebSocket protocol. + * + * @api private + */ + +Sender.prototype.frameAndSend = function(opcode, data, finalFragment, maskData, cb) { + var canModifyData = false; + + if (!data) { + try { + this._socket.write(new Buffer([opcode | (finalFragment ? 0x80 : 0), 0 | (maskData ? 0x80 : 0)].concat(maskData ? [0, 0, 0, 0] : [])), 'binary', cb); + } + catch (e) { + if (typeof cb == 'function') cb(e); + else this.emit('error', e); + } + return; + } + + if (!Buffer.isBuffer(data)) { + canModifyData = true; + if (data && (typeof data.byteLength !== 'undefined' || typeof data.buffer !== 'undefined')) { + data = getArrayBuffer(data); + } else { + data = new Buffer(data); + } + } + + var dataLength = data.length + , dataOffset = maskData ? 6 : 2 + , secondByte = dataLength; + + if (dataLength >= 65536) { + dataOffset += 8; + secondByte = 127; + } + else if (dataLength > 125) { + dataOffset += 2; + secondByte = 126; + } + + var mergeBuffers = dataLength < 32768 || (maskData && !canModifyData); + var totalLength = mergeBuffers ? dataLength + dataOffset : dataOffset; + var outputBuffer = new Buffer(totalLength); + outputBuffer[0] = finalFragment ? opcode | 0x80 : opcode; + + switch (secondByte) { + case 126: + writeUInt16BE.call(outputBuffer, dataLength, 2); + break; + case 127: + writeUInt32BE.call(outputBuffer, 0, 2); + writeUInt32BE.call(outputBuffer, dataLength, 6); + } + + if (maskData) { + outputBuffer[1] = secondByte | 0x80; + var mask = this._randomMask || (this._randomMask = getRandomMask()); + outputBuffer[dataOffset - 4] = mask[0]; + outputBuffer[dataOffset - 3] = mask[1]; + outputBuffer[dataOffset - 2] = mask[2]; + outputBuffer[dataOffset - 1] = mask[3]; + if (mergeBuffers) { + bufferUtil.mask(data, mask, outputBuffer, dataOffset, dataLength); + try { + this._socket.write(outputBuffer, 'binary', cb); + } + catch (e) { + if (typeof cb == 'function') cb(e); + else this.emit('error', e); + } + } + else { + bufferUtil.mask(data, mask, data, 0, dataLength); + try { + this._socket.write(outputBuffer, 'binary'); + this._socket.write(data, 'binary', cb); + } + catch (e) { + if (typeof cb == 'function') cb(e); + else this.emit('error', e); + } + } + } + else { + outputBuffer[1] = secondByte; + if (mergeBuffers) { + data.copy(outputBuffer, dataOffset); + try { + this._socket.write(outputBuffer, 'binary', cb); + } + catch (e) { + if (typeof cb == 'function') cb(e); + else this.emit('error', e); + } + } + else { + try { + this._socket.write(outputBuffer, 'binary'); + this._socket.write(data, 'binary', cb); + } + catch (e) { + if (typeof cb == 'function') cb(e); + else this.emit('error', e); + } + } + } +} + +module.exports = Sender; + +function writeUInt16BE(value, offset) { + this[offset] = (value & 0xff00)>>8; + this[offset+1] = value & 0xff; +} + +function writeUInt32BE(value, offset) { + this[offset] = (value & 0xff000000)>>24; + this[offset+1] = (value & 0xff0000)>>16; + this[offset+2] = (value & 0xff00)>>8; + this[offset+3] = value & 0xff; +} + +function getArrayBuffer(data) { + // data is either an ArrayBuffer or ArrayBufferView. + var array = new Uint8Array(data.buffer || data) + , l = data.byteLength || data.length + , o = data.byteOffset || 0 + , buffer = new Buffer(l); + for (var i = 0; i < l; ++i) { + buffer[i] = array[o+i]; + } + return buffer; +} + +function getRandomMask() { + return new Buffer([ + ~~(Math.random() * 255), + ~~(Math.random() * 255), + ~~(Math.random() * 255), + ~~(Math.random() * 255) + ]); +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Validation.fallback.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Validation.fallback.js new file mode 100644 index 0000000..2c7c4fd --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Validation.fallback.js @@ -0,0 +1,12 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +module.exports.Validation = { + isValidUTF8: function(buffer) { + return true; + } +}; + diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Validation.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Validation.js new file mode 100644 index 0000000..0f3109a --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/Validation.js @@ -0,0 +1,16 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +try { + module.exports = require('../build/Release/validation'); +} catch (e) { try { + module.exports = require('../build/default/validation'); +} catch (e) { try { + module.exports = require('./Validation.fallback'); +} catch (e) { + console.error('validation.node seems to not have been built. Run npm install.'); + throw e; +}}} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/WebSocket.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/WebSocket.js new file mode 100644 index 0000000..cc04dd9 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/WebSocket.js @@ -0,0 +1,802 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var util = require('util') + , events = require('events') + , http = require('http') + , https = require('https') + , crypto = require('crypto') + , url = require('url') + , fs = require('fs') + , Options = require('options') + , Sender = require('./Sender') + , Receiver = require('./Receiver') + , SenderHixie = require('./Sender.hixie') + , ReceiverHixie = require('./Receiver.hixie'); + +/** + * Constants + */ + +// Default protocol version + +var protocolVersion = 13; + +// Close timeout + +var closeTimeout = 30000; // Allow 5 seconds to terminate the connection cleanly + +/** + * Node version 0.4 and 0.6 compatibility + */ + +var isNodeV4 = /^v0\.4/.test(process.version); + +/** + * WebSocket implementation + */ + +function WebSocket(address, options) { + var self = this; + + this._socket = null; + this.bytesReceived = 0; + this.readyState = null; + this.supports = {}; + + if (Object.prototype.toString.call(address) == '[object Array]') { + initAsServerClient.apply(this, address.concat(options)); + } + else initAsClient.apply(this, arguments); +} + +/** + * Inherits from EventEmitter. + */ + +util.inherits(WebSocket, events.EventEmitter); + +/** + * Ready States + */ + +["CONNECTING", "OPEN", "CLOSING", "CLOSED"].forEach(function (state, index) { + WebSocket.prototype[state] = WebSocket[state] = index; +}); + +/** + * Gracefully closes the connection, after sending a description message to the server + * + * @param {Object} data to be sent to the server + * @api public + */ + +WebSocket.prototype.close = function(code, data) { + if (this.readyState == WebSocket.CLOSING || this.readyState == WebSocket.CLOSED) return; + if (this.readyState == WebSocket.CONNECTING) { + this.readyState = WebSocket.CLOSED; + return; + } + try { + this.readyState = WebSocket.CLOSING; + this._closeCode = code; + this._closeMessage = data; + var mask = !this._isServer; + this._sender.close(code, data, mask); + } + catch (e) { + this.emit('error', e); + } + finally { + this.terminate(); + } +} + +/** + * Pause the client stream + * + * @api public + */ + +WebSocket.prototype.pause = function() { + if (this.readyState != WebSocket.OPEN) throw new Error('not opened'); + return this._socket.pause(); +} + +/** + * Sends a ping + * + * @param {Object} data to be sent to the server + * @param {Object} Members - mask: boolean, binary: boolean + * @param {boolean} dontFailWhenClosed indicates whether or not to throw if the connection isnt open + * @api public + */ + +WebSocket.prototype.ping = function(data, options, dontFailWhenClosed) { + if (this.readyState != WebSocket.OPEN) { + if (dontFailWhenClosed === true) return; + throw new Error('not opened'); + } + options = options || {}; + if (typeof options.mask == 'undefined') options.mask = !this._isServer; + this._sender.ping(data, options); +} + +/** + * Sends a pong + * + * @param {Object} data to be sent to the server + * @param {Object} Members - mask: boolean, binary: boolean + * @param {boolean} dontFailWhenClosed indicates whether or not to throw if the connection isnt open + * @api public + */ + +WebSocket.prototype.pong = function(data, options, dontFailWhenClosed) { + if (this.readyState != WebSocket.OPEN) { + if (dontFailWhenClosed === true) return; + throw new Error('not opened'); + } + options = options || {}; + if (typeof options.mask == 'undefined') options.mask = !this._isServer; + this._sender.pong(data, options); +} + +/** + * Resume the client stream + * + * @api public + */ + +WebSocket.prototype.resume = function() { + if (this.readyState != WebSocket.OPEN) throw new Error('not opened'); + return this._socket.resume(); +} + +/** + * Sends a piece of data + * + * @param {Object} data to be sent to the server + * @param {Object} Members - mask: boolean, binary: boolean + * @param {function} Optional callback which is executed after the send completes + * @api public + */ + +WebSocket.prototype.send = function(data, options, cb) { + if (typeof options == 'function') { + cb = options; + options = {}; + } + if (this.readyState != WebSocket.OPEN) { + if (typeof cb == 'function') cb(new Error('not opened')); + else throw new Error('not opened'); + return; + } + if (!data) data = ''; + if (this._queue) { + var self = this; + this._queue.push(function() { self.send(data, options, cb); }); + return; + } + options = options || {}; + options.fin = true; + if (typeof options.binary == 'undefined') { + options.binary = (data instanceof ArrayBuffer || data instanceof Buffer || + data instanceof Uint8Array || + data instanceof Uint16Array || + data instanceof Uint32Array || + data instanceof Int8Array || + data instanceof Int16Array || + data instanceof Int32Array || + data instanceof Float32Array || + data instanceof Float64Array); + } + if (typeof options.mask == 'undefined') options.mask = !this._isServer; + if (data instanceof fs.ReadStream) { + startQueue(this); + var self = this; + sendStream(this, data, options, function(error) { + process.nextTick(function() { executeQueueSends(self); }); + if (typeof cb == 'function') cb(error); + }); + } + else this._sender.send(data, options, cb); +} + +/** + * Streams data through calls to a user supplied function + * + * @param {Object} Members - mask: boolean, binary: boolean + * @param {function} 'function (error, send)' which is executed on successive ticks of which send is 'function (data, final)'. + * @api public + */ + +WebSocket.prototype.stream = function(options, cb) { + if (typeof options == 'function') { + cb = options; + options = {}; + } + if (typeof cb != 'function') throw new Error('callback must be provided'); + if (this.readyState != WebSocket.OPEN) { + if (typeof cb == 'function') cb(new Error('not opened')); + else throw new Error('not opened'); + return; + } + if (this._queue) { + var self = this; + this._queue.push(function() { self.stream(options, cb); }); + return; + } + options = options || {}; + if (typeof options.mask == 'undefined') options.mask = !this._isServer; + startQueue(this); + var self = this; + var send = function(data, final) { + try { + if (self.readyState != WebSocket.OPEN) throw new Error('not opened'); + options.fin = final === true; + self._sender.send(data, options); + if (!final) process.nextTick(cb.bind(null, null, send)); + else executeQueueSends(self); + } + catch (e) { + if (typeof cb == 'function') cb(e); + else { + delete self._queue; + self.emit('error', e); + } + } + } + process.nextTick(cb.bind(null, null, send)); +} + +/** + * Immediately shuts down the connection + * + * @api public + */ + +WebSocket.prototype.terminate = function() { + if (this.readyState == WebSocket.CLOSED) return; + if (this._socket) { + try { + // End the connection + this._socket.end(); + } + catch (e) { + // Socket error during end() call, so just destroy it right now + cleanupWebsocketResources.call(this, true); + return; + } + + // Add a timeout to ensure that the connection is completely + // cleaned up within 30 seconds, even if the clean close procedure + // fails for whatever reason + this._closeTimer = setTimeout(cleanupWebsocketResources.bind(this, true), closeTimeout); + } + else if (this.readyState == WebSocket.CONNECTING) { + cleanupWebsocketResources.call(this, true); + } +}; + +/** + * Expose bufferedAmount + * + * @api public + */ + +Object.defineProperty(WebSocket.prototype, 'bufferedAmount', { + get: function get() { + return this._socket ? this._socket.bufferSize : 0; + } +}); + +/** + * Emulates the W3C Browser based WebSocket interface using function members. + * + * @see http://dev.w3.org/html5/websockets/#the-websocket-interface + * @api public + */ + +['open', 'error', 'close', 'message'].forEach(function(method) { + Object.defineProperty(WebSocket.prototype, 'on' + method, { + /** + * Returns the current listener + * + * @returns {Mixed} the set function or undefined + * @api public + */ + + get: function get() { + var listener = this.listeners(method)[0]; + return listener ? (listener._listener ? listener._listener : listener) : undefined; + }, + + /** + * Start listening for events + * + * @param {Function} listener the listener + * @returns {Mixed} the set function or undefined + * @api public + */ + + set: function set(listener) { + this.removeAllListeners(method); + this.addEventListener(method, listener); + } + }); +}); + +/** + * Emulates the W3C Browser based WebSocket interface using addEventListener. + * + * @see https://developer.mozilla.org/en/DOM/element.addEventListener + * @see http://dev.w3.org/html5/websockets/#the-websocket-interface + * @api public + */ +WebSocket.prototype.addEventListener = function(method, listener) { + var target = this; + if (typeof listener === 'function') { + if (method === 'message') { + function onMessage (data, flags) { + listener.call(this, new MessageEvent(data, flags.binary ? 'Binary' : 'Text', target)); + } + // store a reference so we can return the original function from the addEventListener hook + onMessage._listener = listener; + this.on(method, onMessage); + } else if (method === 'close') { + function onClose (code, message) { + listener.call(this, new CloseEvent(code, message, target)); + } + // store a reference so we can return the original function from the addEventListener hook + onClose._listener = listener; + this.on(method, onClose); + } else if (method === 'error') { + function onError (event) { + event.target = target; + listener.call(this, event); + } + // store a reference so we can return the original function from the addEventListener hook + onError._listener = listener; + this.on(method, onError); + } else if (method === 'open') { + function onOpen () { + listener.call(this, new OpenEvent(target)); + } + // store a reference so we can return the original function from the addEventListener hook + onOpen._listener = listener; + this.on(method, onOpen); + } else { + this.on(method, listener); + } + } +} + +module.exports = WebSocket; + +/** + * W3C MessageEvent + * + * @see http://www.w3.org/TR/html5/comms.html + * @api private + */ + +function MessageEvent(dataArg, typeArg, target) { + this.data = dataArg; + this.type = typeArg; + this.target = target; +} + +/** + * W3C CloseEvent + * + * @see http://www.w3.org/TR/html5/comms.html + * @api private + */ + +function CloseEvent(code, reason, target) { + this.wasClean = (typeof code == 'undefined' || code == 1000); + this.code = code; + this.reason = reason; + this.target = target; +} + +/** + * W3C OpenEvent + * + * @see http://www.w3.org/TR/html5/comms.html + * @api private + */ + +function OpenEvent(target) { + this.target = target; +} + +/** + * Entirely private apis, + * which may or may not be bound to a sepcific WebSocket instance. + */ + +function initAsServerClient(req, socket, upgradeHead, options) { + options = new Options({ + protocolVersion: protocolVersion, + protocol: null + }).merge(options); + + // expose state properties + this.protocol = options.value.protocol; + this.protocolVersion = options.value.protocolVersion; + this.supports.binary = (this.protocolVersion != 'hixie-76'); + this.upgradeReq = req; + this.readyState = WebSocket.CONNECTING; + this._isServer = true; + + // establish connection + if (options.value.protocolVersion == 'hixie-76') establishConnection.call(this, ReceiverHixie, SenderHixie, socket, upgradeHead); + else establishConnection.call(this, Receiver, Sender, socket, upgradeHead); +} + +function initAsClient(address, options) { + options = new Options({ + origin: null, + protocolVersion: protocolVersion, + host: null, + headers: null, + protocol: null, + agent: null, + + // ssl-related options + pfx: null, + key: null, + passphrase: null, + cert: null, + ca: null, + ciphers: null, + rejectUnauthorized: null + }).merge(options); + if (options.value.protocolVersion != 8 && options.value.protocolVersion != 13) { + throw new Error('unsupported protocol version'); + } + + // verify url and establish http class + var serverUrl = url.parse(address); + var isUnixSocket = serverUrl.protocol === 'ws+unix:'; + if (!serverUrl.host && !isUnixSocket) throw new Error('invalid url'); + var isSecure = serverUrl.protocol === 'wss:' || serverUrl.protocol === 'https:'; + var httpObj = isSecure ? https : http; + var port = serverUrl.port || (isSecure ? 443 : 80); + var auth = serverUrl.auth; + + // expose state properties + this._isServer = false; + this.url = address; + this.protocolVersion = options.value.protocolVersion; + this.supports.binary = (this.protocolVersion != 'hixie-76'); + + // begin handshake + var key = new Buffer(options.value.protocolVersion + '-' + Date.now()).toString('base64'); + var shasum = crypto.createHash('sha1'); + shasum.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'); + var expectedServerKey = shasum.digest('base64'); + + var agent = options.value.agent; + // node<=v0.4.x compatibility + if (!agent && isNodeV4) { + isNodeV4 = true; + agent = new httpObj.Agent({ + host: serverUrl.hostname, + port: port + }); + } + + var headerHost = serverUrl.hostname; + // Append port number to Host and Origin header, only if specified in the url and non-default + if(serverUrl.port) { + if((isSecure && (port != 443)) || (!isSecure && (port != 80))){ + headerHost = headerHost + ':' + port; + } + } + + var requestOptions = { + port: port, + host: serverUrl.hostname, + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Host': headerHost, + 'Origin': headerHost, + 'Sec-WebSocket-Version': options.value.protocolVersion, + 'Sec-WebSocket-Key': key + } + }; + + // If we have basic auth. + if (auth) { + requestOptions.headers['Authorization'] = 'Basic ' + new Buffer(auth).toString('base64'); + } + + if (options.value.protocol) { + requestOptions.headers['Sec-WebSocket-Protocol'] = options.value.protocol; + } + + if (options.value.host) { + requestOptions.headers['Host'] = options.value.host; + } + + if (options.value.headers) { + for (var header in options.value.headers) { + if (options.value.headers.hasOwnProperty(header)) { + requestOptions.headers[header] = options.value.headers[header]; + } + } + } + + if (options.isDefinedAndNonNull('pfx') + || options.isDefinedAndNonNull('key') + || options.isDefinedAndNonNull('passphrase') + || options.isDefinedAndNonNull('cert') + || options.isDefinedAndNonNull('ca') + || options.isDefinedAndNonNull('ciphers') + || options.isDefinedAndNonNull('rejectUnauthorized')) { + + if (isNodeV4) { + throw new Error('Client side certificates are not supported on Node 0.4.x'); + } + + if (options.isDefinedAndNonNull('pfx')) requestOptions.pfx = options.value.pfx; + if (options.isDefinedAndNonNull('key')) requestOptions.key = options.value.key; + if (options.isDefinedAndNonNull('passphrase')) requestOptions.passphrase = options.value.passphrase; + if (options.isDefinedAndNonNull('cert')) requestOptions.cert = options.value.cert; + if (options.isDefinedAndNonNull('ca')) requestOptions.ca = options.value.ca; + if (options.isDefinedAndNonNull('ciphers')) requestOptions.ciphers = options.value.ciphers; + if (options.isDefinedAndNonNull('rejectUnauthorized')) requestOptions.rejectUnauthorized = options.value.rejectUnauthorized; + + if (!agent) { + // global agent ignores client side certificates + agent = new httpObj.Agent(requestOptions); + } + } + + if (isNodeV4) { + requestOptions.path = (serverUrl.pathname || '/') + (serverUrl.search || ''); + } + else requestOptions.path = serverUrl.path || '/'; + + if (agent) { + requestOptions.agent = agent; + } + + if (isUnixSocket) { + requestOptions.socketPath = serverUrl.pathname; + } + if (options.value.origin) { + if (options.value.protocolVersion < 13) requestOptions.headers['Sec-WebSocket-Origin'] = options.value.origin; + else requestOptions.headers['Origin'] = options.value.origin; + } + + var self = this; + var req = httpObj.request(requestOptions); + + (isNodeV4 ? agent : req).on('error', function(error) { + self.emit('error', error); + cleanupWebsocketResources.call(this, error); + }); + (isNodeV4 ? agent : req).once('response', function(res) { + var error = new Error('unexpected server response (' + res.statusCode + ')'); + self.emit('error', error); + cleanupWebsocketResources.call(this, error); + }); + (isNodeV4 ? agent : req).once('upgrade', function(res, socket, upgradeHead) { + if (self.readyState == WebSocket.CLOSED) { + // client closed before server accepted connection + self.emit('close'); + removeAllListeners(self); + socket.end(); + return; + } + var serverKey = res.headers['sec-websocket-accept']; + if (typeof serverKey == 'undefined' || serverKey !== expectedServerKey) { + self.emit('error', 'invalid server key'); + removeAllListeners(self); + socket.end(); + return; + } + + var serverProt = res.headers['sec-websocket-protocol']; + var protList = (options.value.protocol || "").split(/, */); + var protError = null; + if (!options.value.protocol && serverProt) { + protError = 'server sent a subprotocol even though none requested'; + } else if (options.value.protocol && !serverProt) { + protError = 'server sent no subprotocol even though requested'; + } else if (serverProt && protList.indexOf(serverProt) === -1) { + protError = 'server responded with an invalid protocol'; + } + if (protError) { + self.emit('error', protError); + removeAllListeners(self); + socket.end(); + return; + } else if (serverProt) { + self.protocol = serverProt; + } + + establishConnection.call(self, Receiver, Sender, socket, upgradeHead); + + // perform cleanup on http resources + removeAllListeners(isNodeV4 ? agent : req); + req = null; + agent = null; + }); + + req.end(); + this.readyState = WebSocket.CONNECTING; +} + +function establishConnection(ReceiverClass, SenderClass, socket, upgradeHead) { + this._socket = socket; + socket.setTimeout(0); + socket.setNoDelay(true); + var self = this; + this._receiver = new ReceiverClass(); + + // socket cleanup handlers + socket.on('end', cleanupWebsocketResources.bind(this)); + socket.on('close', cleanupWebsocketResources.bind(this)); + socket.on('error', cleanupWebsocketResources.bind(this)); + + // ensure that the upgradeHead is added to the receiver + function firstHandler(data) { + if (self.readyState != WebSocket.OPEN) return; + if (upgradeHead && upgradeHead.length > 0) { + self.bytesReceived += upgradeHead.length; + var head = upgradeHead; + upgradeHead = null; + self._receiver.add(head); + } + dataHandler = realHandler; + if (data) { + self.bytesReceived += data.length; + self._receiver.add(data); + } + } + // subsequent packets are pushed straight to the receiver + function realHandler(data) { + if (data) self.bytesReceived += data.length; + self._receiver.add(data); + } + var dataHandler = firstHandler; + // if data was passed along with the http upgrade, + // this will schedule a push of that on to the receiver. + // this has to be done on next tick, since the caller + // hasn't had a chance to set event handlers on this client + // object yet. + process.nextTick(firstHandler); + + // receiver event handlers + self._receiver.ontext = function (data, flags) { + flags = flags || {}; + self.emit('message', data, flags); + }; + self._receiver.onbinary = function (data, flags) { + flags = flags || {}; + flags.binary = true; + self.emit('message', data, flags); + }; + self._receiver.onping = function(data, flags) { + flags = flags || {}; + self.pong(data, {mask: !self._isServer, binary: flags.binary === true}, true); + self.emit('ping', data, flags); + }; + self._receiver.onpong = function(data, flags) { + self.emit('pong', data, flags); + }; + self._receiver.onclose = function(code, data, flags) { + flags = flags || {}; + self.close(code, data); + }; + self._receiver.onerror = function(reason, errorCode) { + // close the connection when the receiver reports a HyBi error code + self.close(typeof errorCode != 'undefined' ? errorCode : 1002, ''); + self.emit('error', reason, errorCode); + }; + + // finalize the client + this._sender = new SenderClass(socket); + this._sender.on('error', function(error) { + self.close(1002, ''); + self.emit('error', error); + }); + this.readyState = WebSocket.OPEN; + this.emit('open'); + + socket.on('data', dataHandler); +} + +function startQueue(instance) { + instance._queue = instance._queue || []; +} + +function executeQueueSends(instance) { + var queue = instance._queue; + if (typeof queue == 'undefined') return; + delete instance._queue; + for (var i = 0, l = queue.length; i < l; ++i) { + queue[i](); + } +} + +function sendStream(instance, stream, options, cb) { + stream.on('data', function(data) { + if (instance.readyState != WebSocket.OPEN) { + if (typeof cb == 'function') cb(new Error('not opened')); + else { + delete instance._queue; + instance.emit('error', new Error('not opened')); + } + return; + } + options.fin = false; + instance._sender.send(data, options); + }); + stream.on('end', function() { + if (instance.readyState != WebSocket.OPEN) { + if (typeof cb == 'function') cb(new Error('not opened')); + else { + delete instance._queue; + instance.emit('error', new Error('not opened')); + } + return; + } + options.fin = true; + instance._sender.send(null, options); + if (typeof cb == 'function') cb(null); + }); +} + +function cleanupWebsocketResources(error) { + if (this.readyState == WebSocket.CLOSED) return; + var emitClose = this.readyState != WebSocket.CONNECTING; + this.readyState = WebSocket.CLOSED; + + clearTimeout(this._closeTimer); + this._closeTimer = null; + if (emitClose) this.emit('close', this._closeCode || 1000, this._closeMessage || ''); + + if (this._socket) { + removeAllListeners(this._socket); + // catch all socket error after removing all standard handlers + var socket = this._socket; + this._socket.on('error', function() { + try { socket.destroy(); } catch (e) {} + }); + try { + if (!error) this._socket.end(); + else this._socket.destroy(); + } + catch (e) { /* Ignore termination errors */ } + this._socket = null; + } + if (this._sender) { + removeAllListeners(this._sender); + this._sender = null; + } + if (this._receiver) { + this._receiver.cleanup(); + this._receiver = null; + } + removeAllListeners(this); + this.on('error', function() {}); // catch all errors after this + delete this._queue; +} + +function removeAllListeners(instance) { + if (isNodeV4) { + // node v4 doesn't *actually* remove all listeners globally, + // so we do that instead + instance._events = {}; + } + else instance.removeAllListeners(); +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/WebSocketServer.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/WebSocketServer.js new file mode 100644 index 0000000..da759f8 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/WebSocketServer.js @@ -0,0 +1,460 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var util = require('util') + , events = require('events') + , http = require('http') + , crypto = require('crypto') + , url = require('url') + , Options = require('options') + , WebSocket = require('./WebSocket') + , tls = require('tls') + , url = require('url'); + +/** + * WebSocket Server implementation + */ + +function WebSocketServer(options, callback) { + options = new Options({ + host: '0.0.0.0', + port: null, + server: null, + verifyClient: null, + handleProtocols: null, + path: null, + noServer: false, + disableHixie: false, + clientTracking: true + }).merge(options); + + if (!options.isDefinedAndNonNull('port') && !options.isDefinedAndNonNull('server') && !options.value.noServer) { + throw new TypeError('`port` or a `server` must be provided'); + } + + var self = this; + + if (options.isDefinedAndNonNull('port')) { + this._server = http.createServer(function (req, res) { + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end('Not implemented'); + }); + this._server.listen(options.value.port, options.value.host, callback); + this._closeServer = function() { self._server.close(); }; + } + else if (options.value.server) { + this._server = options.value.server; + if (options.value.path) { + // take note of the path, to avoid collisions when multiple websocket servers are + // listening on the same http server + if (this._server._webSocketPaths && options.value.server._webSocketPaths[options.value.path]) { + throw new Error('two instances of WebSocketServer cannot listen on the same http server path'); + } + if (typeof this._server._webSocketPaths !== 'object') { + this._server._webSocketPaths = {}; + } + this._server._webSocketPaths[options.value.path] = 1; + } + } + if (this._server) this._server.once('listening', function() { self.emit('listening'); }); + + if (typeof this._server != 'undefined') { + this._server.on('error', function(error) { + self.emit('error', error) + }); + this._server.on('upgrade', function(req, socket, upgradeHead) { + //copy upgradeHead to avoid retention of large slab buffers used in node core + var head = new Buffer(upgradeHead.length); + upgradeHead.copy(head); + + self.handleUpgrade(req, socket, head, function(client) { + self.emit('connection'+req.url, client); + self.emit('connection', client); + }); + }); + } + + this.options = options.value; + this.path = options.value.path; + this.clients = []; +} + +/** + * Inherits from EventEmitter. + */ + +util.inherits(WebSocketServer, events.EventEmitter); + +/** + * Immediately shuts down the connection. + * + * @api public + */ + +WebSocketServer.prototype.close = function() { + // terminate all associated clients + var error = null; + try { + for (var i = 0, l = this.clients.length; i < l; ++i) { + this.clients[i].terminate(); + } + } + catch (e) { + error = e; + } + + // remove path descriptor, if any + if (this.path && this._server._webSocketPaths) { + delete this._server._webSocketPaths[this.path]; + if (Object.keys(this._server._webSocketPaths).length == 0) { + delete this._server._webSocketPaths; + } + } + + // close the http server if it was internally created + try { + if (typeof this._closeServer !== 'undefined') { + this._closeServer(); + } + } + finally { + delete this._server; + } + if (error) throw error; +} + +/** + * Handle a HTTP Upgrade request. + * + * @api public + */ + +WebSocketServer.prototype.handleUpgrade = function(req, socket, upgradeHead, cb) { + // check for wrong path + if (this.options.path) { + var u = url.parse(req.url); + if (u && u.pathname !== this.options.path) return; + } + + if (typeof req.headers.upgrade === 'undefined' || req.headers.upgrade.toLowerCase() !== 'websocket') { + abortConnection(socket, 400, 'Bad Request'); + return; + } + + if (req.headers['sec-websocket-key1']) handleHixieUpgrade.apply(this, arguments); + else handleHybiUpgrade.apply(this, arguments); +} + +module.exports = WebSocketServer; + +/** + * Entirely private apis, + * which may or may not be bound to a sepcific WebSocket instance. + */ + +function handleHybiUpgrade(req, socket, upgradeHead, cb) { + // handle premature socket errors + var errorHandler = function() { + try { socket.destroy(); } catch (e) {} + } + socket.on('error', errorHandler); + + // verify key presence + if (!req.headers['sec-websocket-key']) { + abortConnection(socket, 400, 'Bad Request'); + return; + } + + // verify version + var version = parseInt(req.headers['sec-websocket-version']); + if ([8, 13].indexOf(version) === -1) { + abortConnection(socket, 400, 'Bad Request'); + return; + } + + // verify protocol + var protocols = req.headers['sec-websocket-protocol']; + + // verify client + var origin = version < 13 ? + req.headers['sec-websocket-origin'] : + req.headers['origin']; + + // handler to call when the connection sequence completes + var self = this; + var completeHybiUpgrade2 = function(protocol) { + + // calc key + var key = req.headers['sec-websocket-key']; + var shasum = crypto.createHash('sha1'); + shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); + key = shasum.digest('base64'); + + var headers = [ + 'HTTP/1.1 101 Switching Protocols' + , 'Upgrade: websocket' + , 'Connection: Upgrade' + , 'Sec-WebSocket-Accept: ' + key + ]; + + if (typeof protocol != 'undefined') { + headers.push('Sec-WebSocket-Protocol: ' + protocol); + } + + // allows external modification/inspection of handshake headers + self.emit('headers', headers); + + socket.setTimeout(0); + socket.setNoDelay(true); + try { + socket.write(headers.concat('', '').join('\r\n')); + } + catch (e) { + // if the upgrade write fails, shut the connection down hard + try { socket.destroy(); } catch (e) {} + return; + } + + var client = new WebSocket([req, socket, upgradeHead], { + protocolVersion: version, + protocol: protocol + }); + + if (self.options.clientTracking) { + self.clients.push(client); + client.on('close', function() { + var index = self.clients.indexOf(client); + if (index != -1) { + self.clients.splice(index, 1); + } + }); + } + + // signal upgrade complete + socket.removeListener('error', errorHandler); + cb(client); + } + + // optionally call external protocol selection handler before + // calling completeHybiUpgrade2 + var completeHybiUpgrade1 = function() { + // choose from the sub-protocols + if (typeof self.options.handleProtocols == 'function') { + var protList = (protocols || "").split(/, */); + var callbackCalled = false; + var res = self.options.handleProtocols(protList, function(result, protocol) { + callbackCalled = true; + if (!result) abortConnection(socket, 404, 'Unauthorized') + else completeHybiUpgrade2(protocol); + }); + if (!callbackCalled) { + // the handleProtocols handler never called our callback + abortConnection(socket, 501, 'Could not process protocols'); + } + return; + } else { + if (typeof protocols !== 'undefined') { + completeHybiUpgrade2(protocols.split(/, */)[0]); + } + else { + completeHybiUpgrade2(); + } + } + } + + // optionally call external client verification handler + if (typeof this.options.verifyClient == 'function') { + var info = { + origin: origin, + secure: typeof req.connection.authorized !== 'undefined' || typeof req.connection.encrypted !== 'undefined', + req: req + }; + if (this.options.verifyClient.length == 2) { + this.options.verifyClient(info, function(result) { + if (!result) abortConnection(socket, 401, 'Unauthorized') + else completeHybiUpgrade1(); + }); + return; + } + else if (!this.options.verifyClient(info)) { + abortConnection(socket, 401, 'Unauthorized'); + return; + } + } + + completeHybiUpgrade1(); +} + +function handleHixieUpgrade(req, socket, upgradeHead, cb) { + // handle premature socket errors + var errorHandler = function() { + try { socket.destroy(); } catch (e) {} + } + socket.on('error', errorHandler); + + // bail if options prevent hixie + if (this.options.disableHixie) { + abortConnection(socket, 401, 'Hixie support disabled'); + return; + } + + // verify key presence + if (!req.headers['sec-websocket-key2']) { + abortConnection(socket, 400, 'Bad Request'); + return; + } + + var origin = req.headers['origin'] + , self = this; + + // setup handshake completion to run after client has been verified + var onClientVerified = function() { + var wshost; + if (!req.headers['x-forwarded-host']) + wshost = req.headers.host; + else + wshost = req.headers['x-forwarded-host']; + var location = ((req.headers['x-forwarded-proto'] === 'https' || socket.encrypted) ? 'wss' : 'ws') + '://' + wshost + req.url + , protocol = req.headers['sec-websocket-protocol']; + + // handshake completion code to run once nonce has been successfully retrieved + var completeHandshake = function(nonce, rest) { + // calculate key + var k1 = req.headers['sec-websocket-key1'] + , k2 = req.headers['sec-websocket-key2'] + , md5 = crypto.createHash('md5'); + + [k1, k2].forEach(function (k) { + var n = parseInt(k.replace(/[^\d]/g, '')) + , spaces = k.replace(/[^ ]/g, '').length; + if (spaces === 0 || n % spaces !== 0){ + abortConnection(socket, 400, 'Bad Request'); + return; + } + n /= spaces; + md5.update(String.fromCharCode( + n >> 24 & 0xFF, + n >> 16 & 0xFF, + n >> 8 & 0xFF, + n & 0xFF)); + }); + md5.update(nonce.toString('binary')); + + var headers = [ + 'HTTP/1.1 101 Switching Protocols' + , 'Upgrade: WebSocket' + , 'Connection: Upgrade' + , 'Sec-WebSocket-Location: ' + location + ]; + if (typeof protocol != 'undefined') headers.push('Sec-WebSocket-Protocol: ' + protocol); + if (typeof origin != 'undefined') headers.push('Sec-WebSocket-Origin: ' + origin); + + socket.setTimeout(0); + socket.setNoDelay(true); + try { + // merge header and hash buffer + var headerBuffer = new Buffer(headers.concat('', '').join('\r\n')); + var hashBuffer = new Buffer(md5.digest('binary'), 'binary'); + var handshakeBuffer = new Buffer(headerBuffer.length + hashBuffer.length); + headerBuffer.copy(handshakeBuffer, 0); + hashBuffer.copy(handshakeBuffer, headerBuffer.length); + + // do a single write, which - upon success - causes a new client websocket to be setup + socket.write(handshakeBuffer, 'binary', function(err) { + if (err) return; // do not create client if an error happens + var client = new WebSocket([req, socket, rest], { + protocolVersion: 'hixie-76', + protocol: protocol + }); + if (self.options.clientTracking) { + self.clients.push(client); + client.on('close', function() { + var index = self.clients.indexOf(client); + if (index != -1) { + self.clients.splice(index, 1); + } + }); + } + + // signal upgrade complete + socket.removeListener('error', errorHandler); + cb(client); + }); + } + catch (e) { + try { socket.destroy(); } catch (e) {} + return; + } + } + + // retrieve nonce + var nonceLength = 8; + if (upgradeHead && upgradeHead.length >= nonceLength) { + var nonce = upgradeHead.slice(0, nonceLength); + var rest = upgradeHead.length > nonceLength ? upgradeHead.slice(nonceLength) : null; + completeHandshake.call(self, nonce, rest); + } + else { + // nonce not present in upgradeHead, so we must wait for enough data + // data to arrive before continuing + var nonce = new Buffer(nonceLength); + upgradeHead.copy(nonce, 0); + var received = upgradeHead.length; + var rest = null; + var handler = function (data) { + var toRead = Math.min(data.length, nonceLength - received); + if (toRead === 0) return; + data.copy(nonce, received, 0, toRead); + received += toRead; + if (received == nonceLength) { + socket.removeListener('data', handler); + if (toRead < data.length) rest = data.slice(toRead); + completeHandshake.call(self, nonce, rest); + } + } + socket.on('data', handler); + } + } + + // verify client + if (typeof this.options.verifyClient == 'function') { + var info = { + origin: origin, + secure: typeof req.connection.authorized !== 'undefined' || typeof req.connection.encrypted !== 'undefined', + req: req + }; + if (this.options.verifyClient.length == 2) { + var self = this; + this.options.verifyClient(info, function(result) { + if (!result) abortConnection(socket, 401, 'Unauthorized') + else onClientVerified.apply(self); + }); + return; + } + else if (!this.options.verifyClient(info)) { + abortConnection(socket, 401, 'Unauthorized'); + return; + } + } + + // no client verification required + onClientVerified(); +} + +function abortConnection(socket, code, name) { + try { + var response = [ + 'HTTP/1.1 ' + code + ' ' + name, + 'Content-type: text/html' + ]; + socket.write(response.concat('', '').join('\r\n')); + } + catch (e) { /* ignore errors - we've aborted this connection */ } + finally { + // ensure that an early aborted connection is shut down completely + try { socket.destroy(); } catch (e) {} + } +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/browser.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/browser.js new file mode 100644 index 0000000..37cafe1 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/lib/browser.js @@ -0,0 +1,5 @@ +/// shim for browser packaging + +module.exports = function() { + return global.WebSocket || global.MozWebSocket; +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/.npmignore b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/.travis.yml b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/.travis.yml new file mode 100644 index 0000000..f1d0f13 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.4 + - 0.6 diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/History.md b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/History.md new file mode 100644 index 0000000..4961d2e --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/History.md @@ -0,0 +1,107 @@ + +0.6.1 / 2012-06-01 +================== + + * Added: append (yes or no) on confirmation + * Added: allow node.js v0.7.x + +0.6.0 / 2012-04-10 +================== + + * Added `.prompt(obj, callback)` support. Closes #49 + * Added default support to .choose(). Closes #41 + * Fixed the choice example + +0.5.1 / 2011-12-20 +================== + + * Fixed `password()` for recent nodes. Closes #36 + +0.5.0 / 2011-12-04 +================== + + * Added sub-command option support [itay] + +0.4.3 / 2011-12-04 +================== + + * Fixed custom help ordering. Closes #32 + +0.4.2 / 2011-11-24 +================== + + * Added travis support + * Fixed: line-buffered input automatically trimmed. Closes #31 + +0.4.1 / 2011-11-18 +================== + + * Removed listening for "close" on --help + +0.4.0 / 2011-11-15 +================== + + * Added support for `--`. Closes #24 + +0.3.3 / 2011-11-14 +================== + + * Fixed: wait for close event when writing help info [Jerry Hamlet] + +0.3.2 / 2011-11-01 +================== + + * Fixed long flag definitions with values [felixge] + +0.3.1 / 2011-10-31 +================== + + * Changed `--version` short flag to `-V` from `-v` + * Changed `.version()` so it's configurable [felixge] + +0.3.0 / 2011-10-31 +================== + + * Added support for long flags only. Closes #18 + +0.2.1 / 2011-10-24 +================== + + * "node": ">= 0.4.x < 0.7.0". Closes #20 + +0.2.0 / 2011-09-26 +================== + + * Allow for defaults that are not just boolean. Default peassignment only occurs for --no-*, optional, and required arguments. [Jim Isaacs] + +0.1.0 / 2011-08-24 +================== + + * Added support for custom `--help` output + +0.0.5 / 2011-08-18 +================== + + * Changed: when the user enters nothing prompt for password again + * Fixed issue with passwords beginning with numbers [NuckChorris] + +0.0.4 / 2011-08-15 +================== + + * Fixed `Commander#args` + +0.0.3 / 2011-08-15 +================== + + * Added default option value support + +0.0.2 / 2011-08-15 +================== + + * Added mask support to `Command#password(str[, mask], fn)` + * Added `Command#password(str, fn)` + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/Makefile b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/Makefile new file mode 100644 index 0000000..0074625 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/Makefile @@ -0,0 +1,7 @@ + +TESTS = $(shell find test/test.*.js) + +test: + @./test/run $(TESTS) + +.PHONY: test \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/Readme.md b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/Readme.md new file mode 100644 index 0000000..b8328c3 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/Readme.md @@ -0,0 +1,262 @@ +# Commander.js + + The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander). + + [![Build Status](https://secure.travis-ci.org/visionmedia/commander.js.png)](http://travis-ci.org/visionmedia/commander.js) + +## Installation + + $ npm install commander + +## Option parsing + + Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('commander'); + +program + .version('0.0.1') + .option('-p, --peppers', 'Add peppers') + .option('-P, --pineapple', 'Add pineapple') + .option('-b, --bbq', 'Add bbq sauce') + .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble') + .parse(process.argv); + +console.log('you ordered a pizza with:'); +if (program.peppers) console.log(' - peppers'); +if (program.pineapple) console.log(' - pineappe'); +if (program.bbq) console.log(' - bbq'); +console.log(' - %s cheese', program.cheese); +``` + + Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc. + +## Automated --help + + The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free: + +``` + $ ./examples/pizza --help + + Usage: pizza [options] + + Options: + + -V, --version output the version number + -p, --peppers Add peppers + -P, --pineapple Add pineappe + -b, --bbq Add bbq sauce + -c, --cheese Add the specified type of cheese [marble] + -h, --help output usage information + +``` + +## Coercion + +```js +function range(val) { + return val.split('..').map(Number); +} + +function list(val) { + return val.split(','); +} + +program + .version('0.0.1') + .usage('[options] ') + .option('-i, --integer ', 'An integer argument', parseInt) + .option('-f, --float ', 'A float argument', parseFloat) + .option('-r, --range ..', 'A range', range) + .option('-l, --list ', 'A list', list) + .option('-o, --optional [value]', 'An optional value') + .parse(process.argv); + +console.log(' int: %j', program.integer); +console.log(' float: %j', program.float); +console.log(' optional: %j', program.optional); +program.range = program.range || []; +console.log(' range: %j..%j', program.range[0], program.range[1]); +console.log(' list: %j', program.list); +console.log(' args: %j', program.args); +``` + +## Custom help + + You can display arbitrary `-h, --help` information + by listening for "--help". Commander will automatically + exit once you are done so that the remainder of your program + does not execute causing undesired behaviours, for example + in the following executable "stuff" will not output when + `--help` is used. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('../'); + +function list(val) { + return val.split(',').map(Number); +} + +program + .version('0.0.1') + .option('-f, --foo', 'enable some foo') + .option('-b, --bar', 'enable some bar') + .option('-B, --baz', 'enable some baz'); + +// must be before .parse() since +// node's emit() is immediate + +program.on('--help', function(){ + console.log(' Examples:'); + console.log(''); + console.log(' $ custom-help --help'); + console.log(' $ custom-help -h'); + console.log(''); +}); + +program.parse(process.argv); + +console.log('stuff'); +``` + +yielding the following help output: + +``` + +Usage: custom-help [options] + +Options: + + -h, --help output usage information + -V, --version output the version number + -f, --foo enable some foo + -b, --bar enable some bar + -B, --baz enable some baz + +Examples: + + $ custom-help --help + $ custom-help -h + +``` + +## .prompt(msg, fn) + + Single-line prompt: + +```js +program.prompt('name: ', function(name){ + console.log('hi %s', name); +}); +``` + + Multi-line prompt: + +```js +program.prompt('description:', function(name){ + console.log('hi %s', name); +}); +``` + + Coercion: + +```js +program.prompt('Age: ', Number, function(age){ + console.log('age: %j', age); +}); +``` + +```js +program.prompt('Birthdate: ', Date, function(date){ + console.log('date: %s', date); +}); +``` + +## .password(msg[, mask], fn) + +Prompt for password without echoing: + +```js +program.password('Password: ', function(pass){ + console.log('got "%s"', pass); + process.stdin.destroy(); +}); +``` + +Prompt for password with mask char "*": + +```js +program.password('Password: ', '*', function(pass){ + console.log('got "%s"', pass); + process.stdin.destroy(); +}); +``` + +## .confirm(msg, fn) + + Confirm with the given `msg`: + +```js +program.confirm('continue? ', function(ok){ + console.log(' got %j', ok); +}); +``` + +## .choose(list, fn) + + Let the user choose from a `list`: + +```js +var list = ['tobi', 'loki', 'jane', 'manny', 'luna']; + +console.log('Choose the coolest pet:'); +program.choose(list, function(i){ + console.log('you chose %d "%s"', i, list[i]); +}); +``` + +## Links + + - [API documentation](http://visionmedia.github.com/commander.js/) + - [ascii tables](https://github.com/LearnBoost/cli-table) + - [progress bars](https://github.com/visionmedia/node-progress) + - [more progress bars](https://github.com/substack/node-multimeter) + - [examples](https://github.com/visionmedia/commander.js/tree/master/examples) + +## License + +(The MIT License) + +Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/index.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/index.js new file mode 100644 index 0000000..06ec1e4 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/commander'); \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/lib/commander.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/lib/commander.js new file mode 100644 index 0000000..5ba87eb --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/lib/commander.js @@ -0,0 +1,1026 @@ + +/*! + * commander + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , path = require('path') + , tty = require('tty') + , basename = path.basename; + +/** + * Expose the root command. + */ + +exports = module.exports = new Command; + +/** + * Expose `Command`. + */ + +exports.Command = Command; + +/** + * Expose `Option`. + */ + +exports.Option = Option; + +/** + * Initialize a new `Option` with the given `flags` and `description`. + * + * @param {String} flags + * @param {String} description + * @api public + */ + +function Option(flags, description) { + this.flags = flags; + this.required = ~flags.indexOf('<'); + this.optional = ~flags.indexOf('['); + this.bool = !~flags.indexOf('-no-'); + flags = flags.split(/[ ,|]+/); + if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift(); + this.long = flags.shift(); + this.description = description; +} + +/** + * Return option name. + * + * @return {String} + * @api private + */ + +Option.prototype.name = function(){ + return this.long + .replace('--', '') + .replace('no-', ''); +}; + +/** + * Check if `arg` matches the short or long flag. + * + * @param {String} arg + * @return {Boolean} + * @api private + */ + +Option.prototype.is = function(arg){ + return arg == this.short + || arg == this.long; +}; + +/** + * Initialize a new `Command`. + * + * @param {String} name + * @api public + */ + +function Command(name) { + this.commands = []; + this.options = []; + this.args = []; + this.name = name; +} + +/** + * Inherit from `EventEmitter.prototype`. + */ + +Command.prototype.__proto__ = EventEmitter.prototype; + +/** + * Add command `name`. + * + * The `.action()` callback is invoked when the + * command `name` is specified via __ARGV__, + * and the remaining arguments are applied to the + * function for access. + * + * When the `name` is "*" an un-matched command + * will be passed as the first arg, followed by + * the rest of __ARGV__ remaining. + * + * Examples: + * + * program + * .version('0.0.1') + * .option('-C, --chdir ', 'change the working directory') + * .option('-c, --config ', 'set config path. defaults to ./deploy.conf') + * .option('-T, --no-tests', 'ignore test hook') + * + * program + * .command('setup') + * .description('run remote setup commands') + * .action(function(){ + * console.log('setup'); + * }); + * + * program + * .command('exec ') + * .description('run the given remote command') + * .action(function(cmd){ + * console.log('exec "%s"', cmd); + * }); + * + * program + * .command('*') + * .description('deploy the given env') + * .action(function(env){ + * console.log('deploying "%s"', env); + * }); + * + * program.parse(process.argv); + * + * @param {String} name + * @return {Command} the new command + * @api public + */ + +Command.prototype.command = function(name){ + var args = name.split(/ +/); + var cmd = new Command(args.shift()); + this.commands.push(cmd); + cmd.parseExpectedArgs(args); + cmd.parent = this; + return cmd; +}; + +/** + * Parse expected `args`. + * + * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`. + * + * @param {Array} args + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parseExpectedArgs = function(args){ + if (!args.length) return; + var self = this; + args.forEach(function(arg){ + switch (arg[0]) { + case '<': + self.args.push({ required: true, name: arg.slice(1, -1) }); + break; + case '[': + self.args.push({ required: false, name: arg.slice(1, -1) }); + break; + } + }); + return this; +}; + +/** + * Register callback `fn` for the command. + * + * Examples: + * + * program + * .command('help') + * .description('display verbose help') + * .action(function(){ + * // output help here + * }); + * + * @param {Function} fn + * @return {Command} for chaining + * @api public + */ + +Command.prototype.action = function(fn){ + var self = this; + this.parent.on(this.name, function(args, unknown){ + // Parse any so-far unknown options + unknown = unknown || []; + var parsed = self.parseOptions(unknown); + + // Output help if necessary + outputHelpIfNecessary(self, parsed.unknown); + + // If there are still any unknown options, then we simply + // die, unless someone asked for help, in which case we give it + // to them, and then we die. + if (parsed.unknown.length > 0) { + self.unknownOption(parsed.unknown[0]); + } + + self.args.forEach(function(arg, i){ + if (arg.required && null == args[i]) { + self.missingArgument(arg.name); + } + }); + + // Always append ourselves to the end of the arguments, + // to make sure we match the number of arguments the user + // expects + if (self.args.length) { + args[self.args.length] = self; + } else { + args.push(self); + } + + fn.apply(this, args); + }); + return this; +}; + +/** + * Define option with `flags`, `description` and optional + * coercion `fn`. + * + * The `flags` string should contain both the short and long flags, + * separated by comma, a pipe or space. The following are all valid + * all will output this way when `--help` is used. + * + * "-p, --pepper" + * "-p|--pepper" + * "-p --pepper" + * + * Examples: + * + * // simple boolean defaulting to false + * program.option('-p, --pepper', 'add pepper'); + * + * --pepper + * program.pepper + * // => Boolean + * + * // simple boolean defaulting to false + * program.option('-C, --no-cheese', 'remove cheese'); + * + * program.cheese + * // => true + * + * --no-cheese + * program.cheese + * // => true + * + * // required argument + * program.option('-C, --chdir ', 'change the working directory'); + * + * --chdir /tmp + * program.chdir + * // => "/tmp" + * + * // optional argument + * program.option('-c, --cheese [type]', 'add cheese [marble]'); + * + * @param {String} flags + * @param {String} description + * @param {Function|Mixed} fn or default + * @param {Mixed} defaultValue + * @return {Command} for chaining + * @api public + */ + +Command.prototype.option = function(flags, description, fn, defaultValue){ + var self = this + , option = new Option(flags, description) + , oname = option.name() + , name = camelcase(oname); + + // default as 3rd arg + if ('function' != typeof fn) defaultValue = fn, fn = null; + + // preassign default value only for --no-*, [optional], or + if (false == option.bool || option.optional || option.required) { + // when --no-* we make sure default is true + if (false == option.bool) defaultValue = true; + // preassign only if we have a default + if (undefined !== defaultValue) self[name] = defaultValue; + } + + // register the option + this.options.push(option); + + // when it's passed assign the value + // and conditionally invoke the callback + this.on(oname, function(val){ + // coercion + if (null != val && fn) val = fn(val); + + // unassigned or bool + if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) { + // if no value, bool true, and we have a default, then use it! + if (null == val) { + self[name] = option.bool + ? defaultValue || true + : false; + } else { + self[name] = val; + } + } else if (null !== val) { + // reassign + self[name] = val; + } + }); + + return this; +}; + +/** + * Parse `argv`, settings options and invoking commands when defined. + * + * @param {Array} argv + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parse = function(argv){ + // store raw args + this.rawArgs = argv; + + // guess name + if (!this.name) this.name = basename(argv[1]); + + // process argv + var parsed = this.parseOptions(this.normalize(argv.slice(2))); + this.args = parsed.args; + return this.parseArgs(this.args, parsed.unknown); +}; + +/** + * Normalize `args`, splitting joined short flags. For example + * the arg "-abc" is equivalent to "-a -b -c". + * + * @param {Array} args + * @return {Array} + * @api private + */ + +Command.prototype.normalize = function(args){ + var ret = [] + , arg; + + for (var i = 0, len = args.length; i < len; ++i) { + arg = args[i]; + if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) { + arg.slice(1).split('').forEach(function(c){ + ret.push('-' + c); + }); + } else { + ret.push(arg); + } + } + + return ret; +}; + +/** + * Parse command `args`. + * + * When listener(s) are available those + * callbacks are invoked, otherwise the "*" + * event is emitted and those actions are invoked. + * + * @param {Array} args + * @return {Command} for chaining + * @api private + */ + +Command.prototype.parseArgs = function(args, unknown){ + var cmds = this.commands + , len = cmds.length + , name; + + if (args.length) { + name = args[0]; + if (this.listeners(name).length) { + this.emit(args.shift(), args, unknown); + } else { + this.emit('*', args); + } + } else { + outputHelpIfNecessary(this, unknown); + + // If there were no args and we have unknown options, + // then they are extraneous and we need to error. + if (unknown.length > 0) { + this.unknownOption(unknown[0]); + } + } + + return this; +}; + +/** + * Return an option matching `arg` if any. + * + * @param {String} arg + * @return {Option} + * @api private + */ + +Command.prototype.optionFor = function(arg){ + for (var i = 0, len = this.options.length; i < len; ++i) { + if (this.options[i].is(arg)) { + return this.options[i]; + } + } +}; + +/** + * Parse options from `argv` returning `argv` + * void of these options. + * + * @param {Array} argv + * @return {Array} + * @api public + */ + +Command.prototype.parseOptions = function(argv){ + var args = [] + , len = argv.length + , literal + , option + , arg; + + var unknownOptions = []; + + // parse options + for (var i = 0; i < len; ++i) { + arg = argv[i]; + + // literal args after -- + if ('--' == arg) { + literal = true; + continue; + } + + if (literal) { + args.push(arg); + continue; + } + + // find matching Option + option = this.optionFor(arg); + + // option is defined + if (option) { + // requires arg + if (option.required) { + arg = argv[++i]; + if (null == arg) return this.optionMissingArgument(option); + if ('-' == arg[0]) return this.optionMissingArgument(option, arg); + this.emit(option.name(), arg); + // optional arg + } else if (option.optional) { + arg = argv[i+1]; + if (null == arg || '-' == arg[0]) { + arg = null; + } else { + ++i; + } + this.emit(option.name(), arg); + // bool + } else { + this.emit(option.name()); + } + continue; + } + + // looks like an option + if (arg.length > 1 && '-' == arg[0]) { + unknownOptions.push(arg); + + // If the next argument looks like it might be + // an argument for this option, we pass it on. + // If it isn't, then it'll simply be ignored + if (argv[i+1] && '-' != argv[i+1][0]) { + unknownOptions.push(argv[++i]); + } + continue; + } + + // arg + args.push(arg); + } + + return { args: args, unknown: unknownOptions }; +}; + +/** + * Argument `name` is missing. + * + * @param {String} name + * @api private + */ + +Command.prototype.missingArgument = function(name){ + console.error(); + console.error(" error: missing required argument `%s'", name); + console.error(); + process.exit(1); +}; + +/** + * `Option` is missing an argument, but received `flag` or nothing. + * + * @param {String} option + * @param {String} flag + * @api private + */ + +Command.prototype.optionMissingArgument = function(option, flag){ + console.error(); + if (flag) { + console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag); + } else { + console.error(" error: option `%s' argument missing", option.flags); + } + console.error(); + process.exit(1); +}; + +/** + * Unknown option `flag`. + * + * @param {String} flag + * @api private + */ + +Command.prototype.unknownOption = function(flag){ + console.error(); + console.error(" error: unknown option `%s'", flag); + console.error(); + process.exit(1); +}; + +/** + * Set the program version to `str`. + * + * This method auto-registers the "-V, --version" flag + * which will print the version number when passed. + * + * @param {String} str + * @param {String} flags + * @return {Command} for chaining + * @api public + */ + +Command.prototype.version = function(str, flags){ + if (0 == arguments.length) return this._version; + this._version = str; + flags = flags || '-V, --version'; + this.option(flags, 'output the version number'); + this.on('version', function(){ + console.log(str); + process.exit(0); + }); + return this; +}; + +/** + * Set the description `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.description = function(str){ + if (0 == arguments.length) return this._description; + this._description = str; + return this; +}; + +/** + * Set / get the command usage `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.usage = function(str){ + var args = this.args.map(function(arg){ + return arg.required + ? '<' + arg.name + '>' + : '[' + arg.name + ']'; + }); + + var usage = '[options' + + (this.commands.length ? '] [command' : '') + + ']' + + (this.args.length ? ' ' + args : ''); + if (0 == arguments.length) return this._usage || usage; + this._usage = str; + + return this; +}; + +/** + * Return the largest option length. + * + * @return {Number} + * @api private + */ + +Command.prototype.largestOptionLength = function(){ + return this.options.reduce(function(max, option){ + return Math.max(max, option.flags.length); + }, 0); +}; + +/** + * Return help for options. + * + * @return {String} + * @api private + */ + +Command.prototype.optionHelp = function(){ + var width = this.largestOptionLength(); + + // Prepend the help information + return [pad('-h, --help', width) + ' ' + 'output usage information'] + .concat(this.options.map(function(option){ + return pad(option.flags, width) + + ' ' + option.description; + })) + .join('\n'); +}; + +/** + * Return command help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.commandHelp = function(){ + if (!this.commands.length) return ''; + return [ + '' + , ' Commands:' + , '' + , this.commands.map(function(cmd){ + var args = cmd.args.map(function(arg){ + return arg.required + ? '<' + arg.name + '>' + : '[' + arg.name + ']'; + }).join(' '); + + return cmd.name + + (cmd.options.length + ? ' [options]' + : '') + ' ' + args + + (cmd.description() + ? '\n' + cmd.description() + : ''); + }).join('\n\n').replace(/^/gm, ' ') + , '' + ].join('\n'); +}; + +/** + * Return program help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.helpInformation = function(){ + return [ + '' + , ' Usage: ' + this.name + ' ' + this.usage() + , '' + this.commandHelp() + , ' Options:' + , '' + , '' + this.optionHelp().replace(/^/gm, ' ') + , '' + , '' + ].join('\n'); +}; + +/** + * Prompt for a `Number`. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptForNumber = function(str, fn){ + var self = this; + this.promptSingleLine(str, function parseNumber(val){ + val = Number(val); + if (isNaN(val)) return self.promptSingleLine(str + '(must be a number) ', parseNumber); + fn(val); + }); +}; + +/** + * Prompt for a `Date`. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptForDate = function(str, fn){ + var self = this; + this.promptSingleLine(str, function parseDate(val){ + val = new Date(val); + if (isNaN(val.getTime())) return self.promptSingleLine(str + '(must be a date) ', parseDate); + fn(val); + }); +}; + +/** + * Single-line prompt. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptSingleLine = function(str, fn){ + if ('function' == typeof arguments[2]) { + return this['promptFor' + (fn.name || fn)](str, arguments[2]); + } + + process.stdout.write(str); + process.stdin.setEncoding('utf8'); + process.stdin.once('data', function(val){ + fn(val.trim()); + }).resume(); +}; + +/** + * Multi-line prompt. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptMultiLine = function(str, fn){ + var buf = []; + console.log(str); + process.stdin.setEncoding('utf8'); + process.stdin.on('data', function(val){ + if ('\n' == val || '\r\n' == val) { + process.stdin.removeAllListeners('data'); + fn(buf.join('\n')); + } else { + buf.push(val.trimRight()); + } + }).resume(); +}; + +/** + * Prompt `str` and callback `fn(val)` + * + * Commander supports single-line and multi-line prompts. + * To issue a single-line prompt simply add white-space + * to the end of `str`, something like "name: ", whereas + * for a multi-line prompt omit this "description:". + * + * + * Examples: + * + * program.prompt('Username: ', function(name){ + * console.log('hi %s', name); + * }); + * + * program.prompt('Description:', function(desc){ + * console.log('description was "%s"', desc.trim()); + * }); + * + * @param {String|Object} str + * @param {Function} fn + * @api public + */ + +Command.prototype.prompt = function(str, fn){ + var self = this; + + if ('string' == typeof str) { + if (/ $/.test(str)) return this.promptSingleLine.apply(this, arguments); + this.promptMultiLine(str, fn); + } else { + var keys = Object.keys(str) + , obj = {}; + + function next() { + var key = keys.shift() + , label = str[key]; + + if (!key) return fn(obj); + self.prompt(label, function(val){ + obj[key] = val; + next(); + }); + } + + next(); + } +}; + +/** + * Prompt for password with `str`, `mask` char and callback `fn(val)`. + * + * The mask string defaults to '', aka no output is + * written while typing, you may want to use "*" etc. + * + * Examples: + * + * program.password('Password: ', function(pass){ + * console.log('got "%s"', pass); + * process.stdin.destroy(); + * }); + * + * program.password('Password: ', '*', function(pass){ + * console.log('got "%s"', pass); + * process.stdin.destroy(); + * }); + * + * @param {String} str + * @param {String} mask + * @param {Function} fn + * @api public + */ + +Command.prototype.password = function(str, mask, fn){ + var self = this + , buf = ''; + + // default mask + if ('function' == typeof mask) { + fn = mask; + mask = ''; + } + + process.stdin.resume(); + tty.setRawMode(true); + process.stdout.write(str); + + // keypress + process.stdin.on('keypress', function(c, key){ + if (key && 'enter' == key.name) { + console.log(); + process.stdin.removeAllListeners('keypress'); + tty.setRawMode(false); + if (!buf.trim().length) return self.password(str, mask, fn); + fn(buf); + return; + } + + if (key && key.ctrl && 'c' == key.name) { + console.log('%s', buf); + process.exit(); + } + + process.stdout.write(mask); + buf += c; + }).resume(); +}; + +/** + * Confirmation prompt with `str` and callback `fn(bool)` + * + * Examples: + * + * program.confirm('continue? ', function(ok){ + * console.log(' got %j', ok); + * process.stdin.destroy(); + * }); + * + * @param {String} str + * @param {Function} fn + * @api public + */ + + +Command.prototype.confirm = function(str, fn, verbose){ + var self = this; + this.prompt(str, function(ok){ + if (!ok.trim()) { + if (!verbose) str += '(yes or no) '; + return self.confirm(str, fn, true); + } + fn(parseBool(ok)); + }); +}; + +/** + * Choice prompt with `list` of items and callback `fn(index, item)` + * + * Examples: + * + * var list = ['tobi', 'loki', 'jane', 'manny', 'luna']; + * + * console.log('Choose the coolest pet:'); + * program.choose(list, function(i){ + * console.log('you chose %d "%s"', i, list[i]); + * process.stdin.destroy(); + * }); + * + * @param {Array} list + * @param {Number|Function} index or fn + * @param {Function} fn + * @api public + */ + +Command.prototype.choose = function(list, index, fn){ + var self = this + , hasDefault = 'number' == typeof index; + + if (!hasDefault) { + fn = index; + index = null; + } + + list.forEach(function(item, i){ + if (hasDefault && i == index) { + console.log('* %d) %s', i + 1, item); + } else { + console.log(' %d) %s', i + 1, item); + } + }); + + function again() { + self.prompt(' : ', function(val){ + val = parseInt(val, 10) - 1; + if (hasDefault && isNaN(val)) val = index; + + if (null == list[val]) { + again(); + } else { + fn(val, list[val]); + } + }); + } + + again(); +}; + +/** + * Camel-case the given `flag` + * + * @param {String} flag + * @return {String} + * @api private + */ + +function camelcase(flag) { + return flag.split('-').reduce(function(str, word){ + return str + word[0].toUpperCase() + word.slice(1); + }); +} + +/** + * Parse a boolean `str`. + * + * @param {String} str + * @return {Boolean} + * @api private + */ + +function parseBool(str) { + return /^y|yes|ok|true$/i.test(str); +} + +/** + * Pad `str` to `width`. + * + * @param {String} str + * @param {Number} width + * @return {String} + * @api private + */ + +function pad(str, width) { + var len = Math.max(0, width - str.length); + return str + Array(len + 1).join(' '); +} + +/** + * Output help information if necessary + * + * @param {Command} command to output help for + * @param {Array} array of options to search for -h or --help + * @api private + */ + +function outputHelpIfNecessary(cmd, options) { + options = options || []; + for (var i = 0; i < options.length; i++) { + if (options[i] == '--help' || options[i] == '-h') { + process.stdout.write(cmd.helpInformation()); + cmd.emit('--help'); + process.exit(0); + } + } +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/package.json b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/package.json new file mode 100644 index 0000000..bb8a2c2 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/commander/package.json @@ -0,0 +1,38 @@ +{ + "name": "commander", + "version": "0.6.1", + "description": "the complete solution for node.js command-line programs", + "keywords": [ + "command", + "option", + "parser", + "prompt", + "stdin" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "repository": { + "type": "git", + "url": "https://github.com/visionmedia/commander.js.git" + }, + "dependencies": {}, + "devDependencies": { + "should": ">= 0.0.1" + }, + "scripts": { + "test": "make test" + }, + "main": "index", + "engines": { + "node": ">= 0.4.x" + }, + "readme": "# Commander.js\n\n The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander).\n\n [![Build Status](https://secure.travis-ci.org/visionmedia/commander.js.png)](http://travis-ci.org/visionmedia/commander.js)\n\n## Installation\n\n $ npm install commander\n\n## Option parsing\n\n Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options.\n\n```js\n#!/usr/bin/env node\n\n/**\n * Module dependencies.\n */\n\nvar program = require('commander');\n\nprogram\n .version('0.0.1')\n .option('-p, --peppers', 'Add peppers')\n .option('-P, --pineapple', 'Add pineapple')\n .option('-b, --bbq', 'Add bbq sauce')\n .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')\n .parse(process.argv);\n\nconsole.log('you ordered a pizza with:');\nif (program.peppers) console.log(' - peppers');\nif (program.pineapple) console.log(' - pineappe');\nif (program.bbq) console.log(' - bbq');\nconsole.log(' - %s cheese', program.cheese);\n```\n\n Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as \"--template-engine\" are camel-cased, becoming `program.templateEngine` etc.\n\n## Automated --help\n\n The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free:\n\n``` \n $ ./examples/pizza --help\n\n Usage: pizza [options]\n\n Options:\n\n -V, --version output the version number\n -p, --peppers Add peppers\n -P, --pineapple Add pineappe\n -b, --bbq Add bbq sauce\n -c, --cheese Add the specified type of cheese [marble]\n -h, --help output usage information\n\n```\n\n## Coercion\n\n```js\nfunction range(val) {\n return val.split('..').map(Number);\n}\n\nfunction list(val) {\n return val.split(',');\n}\n\nprogram\n .version('0.0.1')\n .usage('[options] ')\n .option('-i, --integer ', 'An integer argument', parseInt)\n .option('-f, --float ', 'A float argument', parseFloat)\n .option('-r, --range ..', 'A range', range)\n .option('-l, --list ', 'A list', list)\n .option('-o, --optional [value]', 'An optional value')\n .parse(process.argv);\n\nconsole.log(' int: %j', program.integer);\nconsole.log(' float: %j', program.float);\nconsole.log(' optional: %j', program.optional);\nprogram.range = program.range || [];\nconsole.log(' range: %j..%j', program.range[0], program.range[1]);\nconsole.log(' list: %j', program.list);\nconsole.log(' args: %j', program.args);\n```\n\n## Custom help\n\n You can display arbitrary `-h, --help` information\n by listening for \"--help\". Commander will automatically\n exit once you are done so that the remainder of your program\n does not execute causing undesired behaviours, for example\n in the following executable \"stuff\" will not output when\n `--help` is used.\n\n```js\n#!/usr/bin/env node\n\n/**\n * Module dependencies.\n */\n\nvar program = require('../');\n\nfunction list(val) {\n return val.split(',').map(Number);\n}\n\nprogram\n .version('0.0.1')\n .option('-f, --foo', 'enable some foo')\n .option('-b, --bar', 'enable some bar')\n .option('-B, --baz', 'enable some baz');\n\n// must be before .parse() since\n// node's emit() is immediate\n\nprogram.on('--help', function(){\n console.log(' Examples:');\n console.log('');\n console.log(' $ custom-help --help');\n console.log(' $ custom-help -h');\n console.log('');\n});\n\nprogram.parse(process.argv);\n\nconsole.log('stuff');\n```\n\nyielding the following help output:\n\n```\n\nUsage: custom-help [options]\n\nOptions:\n\n -h, --help output usage information\n -V, --version output the version number\n -f, --foo enable some foo\n -b, --bar enable some bar\n -B, --baz enable some baz\n\nExamples:\n\n $ custom-help --help\n $ custom-help -h\n\n```\n\n## .prompt(msg, fn)\n\n Single-line prompt:\n\n```js\nprogram.prompt('name: ', function(name){\n console.log('hi %s', name);\n});\n```\n\n Multi-line prompt:\n\n```js\nprogram.prompt('description:', function(name){\n console.log('hi %s', name);\n});\n```\n\n Coercion:\n\n```js\nprogram.prompt('Age: ', Number, function(age){\n console.log('age: %j', age);\n});\n```\n\n```js\nprogram.prompt('Birthdate: ', Date, function(date){\n console.log('date: %s', date);\n});\n```\n\n## .password(msg[, mask], fn)\n\nPrompt for password without echoing:\n\n```js\nprogram.password('Password: ', function(pass){\n console.log('got \"%s\"', pass);\n process.stdin.destroy();\n});\n```\n\nPrompt for password with mask char \"*\":\n\n```js\nprogram.password('Password: ', '*', function(pass){\n console.log('got \"%s\"', pass);\n process.stdin.destroy();\n});\n```\n\n## .confirm(msg, fn)\n\n Confirm with the given `msg`:\n\n```js\nprogram.confirm('continue? ', function(ok){\n console.log(' got %j', ok);\n});\n```\n\n## .choose(list, fn)\n\n Let the user choose from a `list`:\n\n```js\nvar list = ['tobi', 'loki', 'jane', 'manny', 'luna'];\n\nconsole.log('Choose the coolest pet:');\nprogram.choose(list, function(i){\n console.log('you chose %d \"%s\"', i, list[i]);\n});\n```\n\n## Links\n\n - [API documentation](http://visionmedia.github.com/commander.js/)\n - [ascii tables](https://github.com/LearnBoost/cli-table)\n - [progress bars](https://github.com/visionmedia/node-progress)\n - [more progress bars](https://github.com/substack/node-multimeter)\n - [examples](https://github.com/visionmedia/commander.js/tree/master/examples)\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", + "readmeFilename": "Readme.md", + "bugs": { + "url": "https://github.com/visionmedia/commander.js/issues" + }, + "_id": "commander@0.6.1", + "_from": "commander@~0.6.1" +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/.index.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/.index.js new file mode 100644 index 0000000..68da1f3 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/.index.js @@ -0,0 +1 @@ +//noop \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/LICENSE b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/LICENSE new file mode 100644 index 0000000..352c287 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/LICENSE @@ -0,0 +1,43 @@ +Copyright 2013, NAN contributors: + - Rod Vagg + - Benjamin Byholm + - Trevor Norris +(the "Original Author") +All rights reserved. + +MIT +no-false-attribs License + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +Distributions of all or part of the Software intended to be used +by the recipients as they would use the unmodified Software, +containing modifications that substantially alter, remove, or +disable functionality of the Software, outside of the documented +configuration mechanisms provided by the Software, shall be +modified such that the Original Author's bug reporting email +addresses and urls are either replaced with the contact information +of the parties responsible for the changes, or removed entirely. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + + +Except where noted, this license applies to any and all software +programs and associated documentation files created by the +Original Author, when distributed with the Software. diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/README.md b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/README.md new file mode 100644 index 0000000..7959e09 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/README.md @@ -0,0 +1,703 @@ +Native Abstractions for Node.js +=============================== + +**A header file filled with macro and utility goodness for making addon development for Node.js easier across versions 0.8, 0.10 and 0.11, and eventually 0.12.** + +***Current version: 0.3.1*** *(See [nan.h](https://github.com/rvagg/nan/blob/master/nan.h) for changelog)* + +Thanks to the crazy changes in V8 (and some in Node core), keeping native addons compiling happily across versions, particularly 0.10 to 0.11/0.12, is a minor nightmare. The goal of this project is to store all logic necessary to develop native Node.js addons without having to inspect `NODE_MODULE_VERSION` and get yourself into a macro-tangle. + +This project also contains some helper utilities that make addon development a bit more pleasant. + + * **[Usage](#usage)** + * **[Example](#example)** + * **[API](#api)** + + +## Usage + +Simply add **NAN** as a dependency in the *package.json* of your Node addon: + +```js +"dependencies": { + ... + "nan" : "~0.3.1" + ... +} +``` + +Pull in the path to **NAN** in your *binding.gyp* so that you can use `#include "nan.h"` in your *.cpp*: + +```js +"include_dirs" : [ + ... + "` when compiling your addon. + + +## Example + +See **[LevelDOWN](https://github.com/rvagg/node-leveldown/pull/48)** for a full example of **NAN** in use. + +For a simpler example, see the **[async pi estimation example](https://github.com/rvagg/nan/tree/master/examples/async_pi_estimate)** in the examples directory for full code and an explanation of what this Monte Carlo Pi estimation example does. Below are just some parts of the full example that illustrate the use of **NAN**. + +Compare to the current 0.10 version of this example, found in the [node-addon-examples](https://github.com/rvagg/node-addon-examples/tree/master/9_async_work) repository and also a 0.11 version of the same found [here](https://github.com/kkoopa/node-addon-examples/tree/5c01f58fc993377a567812597e54a83af69686d7/9_async_work). + +Note that there is no embedded version sniffing going on here and also the async work is made much simpler, see below for details on the `NanAsyncWorker` class. + +```c++ +// addon.cc +#include +#include "nan.h" +// ... + +using namespace v8; + +void InitAll(Handle exports) { + exports->Set(NanSymbol("calculateSync"), + FunctionTemplate::New(CalculateSync)->GetFunction()); + + exports->Set(NanSymbol("calculateAsync"), + FunctionTemplate::New(CalculateAsync)->GetFunction()); +} + +NODE_MODULE(addon, InitAll) +``` + +```c++ +// sync.h +#include +#include "nan.h" + +NAN_METHOD(CalculateSync); +``` + +```c++ +// sync.cc +#include +#include "nan.h" +#include "sync.h" +// ... + +using namespace v8; + +// Simple synchronous access to the `Estimate()` function +NAN_METHOD(CalculateSync) { + NanScope(); + + // expect a number as the first argument + int points = args[0]->Uint32Value(); + double est = Estimate(points); + + NanReturnValue(Number::New(est)); +} +``` + +```c++ +// async.cc +#include +#include "nan.h" +#include "async.h" + +// ... + +using namespace v8; + +class PiWorker : public NanAsyncWorker { + public: + PiWorker(NanCallback *callback, int points) + : NanAsyncWorker(callback), points(points) {} + ~PiWorker() {} + + // Executed inside the worker-thread. + // It is not safe to access V8, or V8 data structures + // here, so everything we need for input and output + // should go on `this`. + void Execute () { + estimate = Estimate(points); + } + + // Executed when the async work is complete + // this function will be run inside the main event loop + // so it is safe to use V8 again + void HandleOKCallback () { + NanScope(); + + Local argv[] = { + Local::New(Null()) + , Number::New(estimate) + }; + + callback->Call(2, argv); + }; + + private: + int points; + double estimate; +}; + +// Asynchronous access to the `Estimate()` function +NAN_METHOD(CalculateAsync) { + NanScope(); + + int points = args[0]->Uint32Value(); + NanCallback *callback = new NanCallback(args[1].As()); + + NanAsyncQueueWorker(new PiWorker(callback, points)); + NanReturnUndefined(); +} +``` + + +## API + + * NAN_METHOD + * NAN_GETTER + * NAN_SETTER + * NAN_PROPERTY_GETTER + * NAN_PROPERTY_SETTER + * NAN_PROPERTY_ENUMERATOR + * NAN_PROPERTY_DELETER + * NAN_PROPERTY_QUERY + * NAN_WEAK_CALLBACK + * NanReturnValue + * NanReturnUndefined + * NanReturnNull + * NanReturnEmptyString + * NanScope + * NanLocker + * NanUnlocker + * NanGetInternalFieldPointer + * NanSetInternalFieldPointer + * NanObjectWrapHandle + * NanMakeWeak + * NanSymbol + * NanGetPointerSafe + * NanSetPointerSafe + * NanFromV8String + * NanBooleanOptionValue + * NanUInt32OptionValue + * NanThrowError, NanThrowTypeError, NanThrowRangeError, NanThrowError(Handle), NanThrowError(Handle, int) + * NanNewBufferHandle(char *, size_t, FreeCallback, void *), NanNewBufferHandle(char *, uint32_t), NanNewBufferHandle(uint32_t) + * NanBufferUse(char *, uint32_t) + * NanNewContextHandle + * NanHasInstance + * NanPersistentToLocal + * NanDispose + * NanAssignPersistent + * NanInitPersistent + * NanCallback + * NanAsyncWorker + * NanAsyncQueueWorker + + +### NAN_METHOD(methodname) + +Use `NAN_METHOD` to define your V8 accessible methods: + +```c++ +// .h: +class Foo : public node::ObjectWrap { + ... + + static NAN_METHOD(Bar); + static NAN_METHOD(Baz); +} + + +// .cc: +NAN_METHOD(Foo::Bar) { + ... +} + +NAN_METHOD(Foo::Baz) { + ... +} +``` + +The reason for this macro is because of the method signature change in 0.11: + +```c++ +// 0.10 and below: +Handle name(const Arguments& args) + +// 0.11 and above +void name(const FunctionCallbackInfo& args) +``` + +The introduction of `FunctionCallbackInfo` brings additional complications: + + +### NAN_GETTER(methodname) + +Use `NAN_GETTER` to declare your V8 accessible getters. You get a `Local` `property` and an appropriately typed `args` object that can act like the `args` argument to a `NAN_METHOD` call. + +You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_GETTER`. + + +### NAN_SETTER(methodname) + +Use `NAN_SETTER` to declare your V8 accessible setters. Same as `NAN_GETTER` but you also get a `Local` `value` object to work with. + +You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_SETTER`. + + +### NAN_PROPERTY_GETTER(cbname) +Use `NAN_PROPERTY_GETTER` to declare your V8 accessible property getters. You get a `Local` `property` and an appropriately typed `args` object that can act similar to the `args` argument to a `NAN_METHOD` call. + +You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_GETTER`. + + +### NAN_PROPERTY_SETTER(cbname) +Use `NAN_PROPERTY_SETTER` to declare your V8 accessible property setters. Same as `NAN_PROPERTY_GETTER` but you also get a `Local` `value` object to work with. + +You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_SETTER`. + + +### NAN_PROPERTY_ENUMERATOR(cbname) +Use `NAN_PROPERTY_ENUMERATOR` to declare your V8 accessible property enumerators. You get an appropriately typed `args` object like the `args` argument to a `NAN_PROPERTY_GETTER` call. + +You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_ENUMERATOR`. + + +### NAN_PROPERTY_DELETER(cbname) +Use `NAN_PROPERTY_DELETER` to declare your V8 accessible property deleters. Same as `NAN_PROPERTY_GETTER`. + +You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_DELETER`. + + +### NAN_PROPERTY_QUERY(cbname) +Use `NAN_PROPERTY_QUERY` to declare your V8 accessible property queries. Same as `NAN_PROPERTY_GETTER`. + +You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_QUERY`. + + +### NAN_WEAK_CALLBACK(type, cbname) + +Use `NAN_WEAK_CALLBACK` to declare your V8 WeakReference callbacks. There is an object argument accessible through `NAN_WEAK_CALLBACK_OBJECT`. The `type` argument gives the type of the `data` argument, accessible through `NAN_WEAK_CALLBACK_DATA(type)`. + +```c++ +static NAN_WEAK_CALLBACK(BufferReference*, WeakCheck) { + if (NAN_WEAK_CALLBACK_DATA(BufferReference*)->noLongerNeeded_) { + delete NAN_WEAK_CALLBACK_DATA(BufferReference*); + } else { + // Still in use, revive, prevent GC + NanMakeWeak(NAN_WEAK_CALLBACK_OBJECT, NAN_WEAK_CALLBACK_DATA(BufferReference*), &WeakCheck); + } +} + +``` + +### NanReturnValue(Handle<Value>) + +Use `NanReturnValue` when you want to return a value from your V8 accessible method: + +```c++ +NAN_METHOD(Foo::Bar) { + ... + + NanReturnValue(String::New("FooBar!")); +} +``` + +No `return` statement required. + + +### NanReturnUndefined() + +Use `NanReturnUndefined` when you don't want to return anything from your V8 accessible method: + +```c++ +NAN_METHOD(Foo::Baz) { + ... + + NanReturnUndefined(); +} +``` + + +### NanReturnNull() + +Use `NanReturnNull` when you want to return `Null` from your V8 accessible method: + +```c++ +NAN_METHOD(Foo::Baz) { + ... + + NanReturnNull(); +} +``` + + +### NanReturnEmptyString() + +Use `NanReturnEmptyString` when you want to return an empty `String` from your V8 accessible method: + +```c++ +NAN_METHOD(Foo::Baz) { + ... + + NanReturnEmptyString(); +} +``` + + +### NanScope() + +The introduction of `isolate` references for many V8 calls in Node 0.11 makes `NanScope()` necessary, use it in place of `HandleScope scope`: + +```c++ +NAN_METHOD(Foo::Bar) { + NanScope(); + + NanReturnValue(String::New("FooBar!")); +} +``` + + +### NanLocker() + +The introduction of `isolate` references for many V8 calls in Node 0.11 makes `NanLocker()` necessary, use it in place of `Locker locker`: + +```c++ +NAN_METHOD(Foo::Bar) { + NanLocker(); + ... + NanUnlocker(); +} +``` + + +### NanUnlocker() + +The introduction of `isolate` references for many V8 calls in Node 0.11 makes `NanUnlocker()` necessary, use it in place of `Unlocker unlocker`: + +```c++ +NAN_METHOD(Foo::Bar) { + NanLocker(); + ... + NanUnlocker(); +} +``` + + +### void * NanGetInternalFieldPointer(Handle<Object>, int) + +Gets a pointer to the internal field with at `index` from a V8 `Object` handle. + +```c++ +Local obj; +... +NanGetInternalFieldPointer(obj, 0); +``` + +### void NanSetInternalFieldPointer(Handle<Object>, int, void *) + +Sets the value of the internal field at `index` on a V8 `Object` handle. + +```c++ +static Persistent dataWrapperCtor; +... +Local wrapper = NanPersistentToLocal(dataWrapperCtor)->NewInstance(); +NanSetInternalFieldPointer(wrapper, 0, this); +``` + + +### Local<Object> NanObjectWrapHandle(Object) + +When you want to fetch the V8 object handle from a native object you've wrapped with Node's `ObjectWrap`, you should use `NanObjectWrapHandle`: + +```c++ +NanObjectWrapHandle(iterator)->Get(String::NewSymbol("end")) +``` + + +### NanMakeWeak(Persistent<T>, parameter, callback) + +Make a persistent reference weak. + + +### String NanSymbol(char *) + +This isn't strictly about compatibility, it's just an easier way to create string symbol objects (i.e. `String::NewSymbol(x)`), for getting and setting object properties, or names of objects. + +```c++ +bool foo = false; +if (obj->Has(NanSymbol("foo"))) + foo = optionsObj->Get(NanSymbol("foo"))->BooleanValue() +``` + + +### Type NanGetPointerSafe(Type *[, Type]) + +A helper for getting values from optional pointers. If the pointer is `NULL`, the function returns the optional default value, which defaults to `0`. Otherwise, the function returns the value the pointer points to. + +```c++ +char *plugh(uint32_t *optional) { + char res[] = "xyzzy"; + uint32_t param = NanGetPointerSafe(optional, 0x1337); + switch (param) { + ... + } + NanSetPointerSafe(optional, 0xDEADBEEF); +} +``` + + +### bool NanSetPointerSafe(Type *, Type) + +A helper for setting optional argument pointers. If the pointer is `NULL`, the function simply return `false`. Otherwise, the value is assigned to the variable the pointer points to. + +```c++ +const char *plugh(size_t *outputsize) { + char res[] = "xyzzy"; + if !(NanSetPointerSafe(outputsize, strlen(res) + 1)) { + ... + } + + ... +} +``` + + +### char* NanFromV8String(Handle<Value>[, enum Nan::Encoding, size_t *, char *, size_t, int]) + +When you want to convert a V8 `String` to a `char*` use `NanFromV8String`. It is possible to define an encoding that defaults to `Nan::UTF8` as well as a pointer to a variable that will be assigned the number of bytes in the returned string. It is also possible to supply a buffer and its length to the function in order not to have a new buffer allocated. The final argument allows optionally setting `String::WriteOptions`, which default to `String::HINT_MANY_WRITES_EXPECTED | String::NO_NULL_TERMINATION`. +Just remember that you'll end up with an object that you'll need to `delete[]` at some point unless you supply your own buffer: + +```c++ +size_t count; +char* name = NanFromV8String(args[0]); +char* decoded = NanFromV8String(args[1], Nan::BASE64, &count, NULL, 0, String::HINT_MANY_WRITES_EXPECTED); +char param_copy[count]; +memcpy(param_copy, decoded, count); +delete[] decoded; +``` + + +### bool NanBooleanOptionValue(Handle<Value>, Handle<String>[, bool]) + +When you have an "options" object that you need to fetch properties from, boolean options can be fetched with this pair. They check first if the object exists (`IsEmpty`), then if the object has the given property (`Has`) then they get and convert/coerce the property to a `bool`. + +The optional last parameter is the *default* value, which is `false` if left off: + +```c++ +// `foo` is false unless the user supplies a truthy value for it +bool foo = NanBooleanOptionValue(optionsObj, NanSymbol("foo")); +// `bar` is true unless the user supplies a falsy value for it +bool bar = NanBooleanOptionValueDefTrue(optionsObj, NanSymbol("bar"), true); +``` + + +### uint32_t NanUInt32OptionValue(Handle<Value>, Handle<String>, uint32_t) + +Similar to `NanBooleanOptionValue`, use `NanUInt32OptionValue` to fetch an integer option from your options object. Can be any kind of JavaScript `Number` and it will be coerced to an unsigned 32-bit integer. + +Requires all 3 arguments as a default is not optional: + +```c++ +uint32_t count = NanUInt32OptionValue(optionsObj, NanSymbol("count"), 1024); +``` + + +### NanThrowError(message), NanThrowTypeError(message), NanThrowRangeError(message), NanThrowError(Local<Value>), NanThrowError(Local<Value>, int) + +For throwing `Error`, `TypeError` and `RangeError` objects. You should `return` this call: + +```c++ +return NanThrowError("you must supply a callback argument"); +``` + +Can also handle any custom object you may want to throw. If used with the error code argument, it will add the supplied error code to the error object as a property called `code`. + + +### Local<Object> NanNewBufferHandle(char *, uint32_t), Local<Object> NanNewBufferHandle(uint32_t) + +The `Buffer` API has changed a little in Node 0.11, this helper provides consistent access to `Buffer` creation: + +```c++ +NanNewBufferHandle((char*)value.data(), value.size()); +``` + +Can also be used to initialize a `Buffer` with just a `size` argument. + +Can also be supplied with a `NAN_WEAK_CALLBACK` and a hint for the garbage collector, when dealing with weak references. + + +### Local<Object> NanBufferUse(char*, uint32_t) + +`Buffer::New(char*, uint32_t)` prior to 0.11 would make a copy of the data. +While it was possible to get around this, it required a shim by passing a +callback. So the new API `Buffer::Use(char*, uint32_t)` was introduced to remove +needing to use this shim. + +`NanBufferUse` uses the `char*` passed as the backing data, and will free the +memory automatically when the weak callback is called. Keep this in mind, as +careless use can lead to "double free or corruption" and other cryptic failures. + + +### bool NanHasInstance(Persistent<FunctionTemplate>&, Handle<Value>) + +Can be used to check the type of an object to determine it is of a particular class you have already defined and have a `Persistent` handle for. + + +### Local<Type> NanPersistentToLocal(Persistent<Type>&) + +Aside from `FunctionCallbackInfo`, the biggest and most painful change to V8 in Node 0.11 is the many restrictions now placed on `Persistent` handles. They are difficult to assign and difficult to fetch the original value out of. + +Use `NanPersistentToLocal` to convert a `Persistent` handle back to a `Local` handle. + +```c++ +Local handle = NanPersistentToLocal(persistentHandle); +``` + + +### Local<Context> NanNewContextHandle([ExtensionConfiguration*, Handle<ObjectTemplate>, Handle<Value>]) +Creates a new `Local` handle. + +```c++ +Local ftmpl = FunctionTemplate::New(); +Local otmpl = ftmpl->InstanceTemplate(); +Local ctx = NanNewContextHandle(NULL, otmpl); +``` + + +### void NanDispose(Persistent<T> &) + +Use `NanDispose` to dispose a `Persistent` handle. + +```c++ +NanDispose(persistentHandle); +``` + + +### NanAssignPersistent(type, handle, object) + +Use `NanAssignPersistent` to assign a non-`Persistent` handle to a `Persistent` one. You can no longer just declare a `Persistent` handle and assign directly to it later, you have to `Reset` it in Node 0.11, so this makes it easier. + +In general it is now better to place anything you want to protect from V8's garbage collector as properties of a generic `Object` and then assign that to a `Persistent`. This works in older versions of Node also if you use `NanAssignPersistent`: + +```c++ +Persistent persistentHandle; + +... + +Local obj = Object::New(); +obj->Set(NanSymbol("key"), keyHandle); // where keyHandle might be a Local +NanAssignPersistent(Object, persistentHandle, obj) +``` + + +### NanInitPersistent(type, name, object) + +User `NanInitPersistent` to declare and initialize a new `Persistent` with the supplied object. The assignment operator for `Persistent` is no longer public in Node 0.11, so this macro makes it easier to declare and initializing a new `Persistent`. See NanAssignPersistent for more information. + +```c++ +Local obj = Object::New(); +obj->Set(NanSymbol("key"), keyHandle); // where keyHandle might be a Local +NanInitPersistent(Object, persistentHandle, obj); +``` + + +### NanCallback + +Because of the difficulties imposed by the changes to `Persistent` handles in V8 in Node 0.11, creating `Persistent` versions of your `Local` handles is annoyingly tricky. `NanCallback` makes it easier by taking your `Local` handle, making it persistent until the `NanCallback` is deleted and even providing a handy `Call()` method to fetch and execute the callback `Function`. + +```c++ +Local callbackHandle = callback = args[0].As(); +NanCallback *callback = new NanCallback(callbackHandle); +// pass `callback` around and it's safe from GC until you: +delete callback; +``` + +You can execute the callback like so: + +```c++ +// no arguments: +callback->Call(0, NULL); + +// an error argument: +Local argv[] = { + Exception::Error(String::New("fail!")) +}; +callback->Call(1, argv); + +// a success argument: +Local argv[] = { + Local::New(Null()), + String::New("w00t!") +}; +callback->Call(2, argv); +``` + +`NanCallback` also has a `Local GetCallback()` method that you can use to fetch a local handle to the underlying callback function if you need it. + + +### NanAsyncWorker + +`NanAsyncWorker` is an abstract class that you can subclass to have much of the annoying async queuing and handling taken care of for you. It can even store arbitrary V8 objects for you and have them persist while the async work is in progress. + +See a rough outline of the implementation: + +```c++ +class NanAsyncWorker { +public: + NanAsyncWorker (NanCallback *callback); + + // Clean up persistent handles and delete the *callback + virtual ~NanAsyncWorker (); + + // Check the `char *errmsg` property and call HandleOKCallback() + // or HandleErrorCallback depending on whether it has been set or not + virtual void WorkComplete (); + + // You must implement this to do some async work. If there is an + // error then allocate `errmsg` to to a message and the callback will + // be passed that string in an Error object + virtual void Execute (); + +protected: + // Set this if there is an error, otherwise it's NULL + const char *errmsg; + + // Save a V8 object in a Persistent handle to protect it from GC + void SavePersistent(const char *key, Local &obj); + + // Fetch a stored V8 object (don't call from within `Execute()`) + Local GetFromPersistent(const char *key); + + // Default implementation calls the callback function with no arguments. + // Override this to return meaningful data + virtual void HandleOKCallback (); + + // Default implementation calls the callback function with an Error object + // wrapping the `errmsg` string + virtual void HandleErrorCallback (); +}; +``` + + +### NanAsyncQueueWorker(NanAsyncWorker *) + +`NanAsyncQueueWorker` will run a `NanAsyncWorker` asynchronously via libuv. Both the *execute* and *after_work* steps are taken care of for you—most of the logic for this is embedded in `NanAsyncWorker`. + +### Contributors + +NAN is only possible due to the excellent work of the following contributors: + +
'; +html += '
Enter Your Password
'; +html += '
'; +html += '
Password:


'; +html += '
'; +html += '
'; +html += '

'; +html += ''; +html += ''; +html += ''; +html += '
' + large_icon_button('x', 'Cancel', "clear_login()") + ' ' + large_icon_button('check', 'Login', 'do_effect_login()') + '
'; +html += '
'; +html += ''; +session.hooks.keys[ENTER_KEY] = 'do_effect_login'; +session.hooks.keys[ESC_KEY] = 'clear_login'; +safe_focus( 'fe_lp_password' ); +show_popup_dialog(450, 225, html); +} +function clear_login() { +hide_popup_dialog(); +Nav.prev(); +} +function do_login() { +if ($('fe_username').value.match(/^\w+$/)) { +session.username = $('fe_username').value; +session.auto_login = $('fe_auto_login').checked; +do_login_prompt_2(); +return; +} +else { +do_openid_login(); +} +} +function do_openid_login() { +if (!$('fe_username').value) return; +session.openid_win = popup_window(''); +if (!session.openid_win) return; +session.open_id = $('fe_username').value; +session.auto_login = $('fe_auto_login') && $('fe_auto_login').checked; +hide_popup_dialog(); +show_progress_dialog(1, "Logging in..."); +session.hooks.before_error = 'close_openid_window'; +session.hooks.after_error = 'do_login_prompt'; +effect_api_send('openid_login', { +OpenID: session.open_id, +Infinite: session.auto_login ? 1 : 0 +}, 'do_openid_login_2'); +} +function close_openid_window() { +if (session.openid_win) { +session.openid_win.close(); +delete session.openid_win; +} +} +function do_openid_login_2(response) { +if (response.CheckURL) { +Debug.trace('openid', "Redirecting popup window to OpenID Check URL: " + response.CheckURL); +show_progress_dialog(1, "Waiting for popup window...", false, ['x', 'Cancel', 'do_login_prompt()']); +session.openid_win.location = response.CheckURL; +session.openid_win.focus(); +} +} +function receive_openid_response(iframe_response) { +var response = deep_copy_object(iframe_response); +Debug.trace('openid', "Received OpenID Response: " + dumper(response)); +hide_popup_dialog(); +if (response.Code) { +close_openid_window(); +return do_error( response.Description ); +} +delete session.hooks.before_error; +delete session.hooks.after_error; +if (response.SessionID) { +session.cookie.set( 'effect_session_id', response.SessionID ); +session.cookie.save(); +} +switch (response.Action) { +case 'popup': +show_progress_dialog(1, "Waiting for popup window...", false, ['x', 'Cancel', 'do_login_prompt()']); +Debug.trace('openid', "Redirecting popup window to OpenID Setup URL: " + response.SetupURL); +session.openid_win.location = response.SetupURL; +session.openid_win.focus(); +break; +case 'login': +close_openid_window(); +do_login_2(response); +break; +case 'register': +if (!response.Info) response.Info = {}; +close_openid_window(); +Debug.trace('openid', 'Original OpenID: ' + response.OpenID_Login); +Debug.trace('openid', 'Clean OpenID: ' + response.OpenID_Unique); +Debug.trace('openid', 'Registration Info: ' + dumper(response.Info)); +session.prereg = response.Info; +session.prereg.open_id_login = response.OpenID_Login; +session.prereg.open_id = response.OpenID_Unique; +if (session.user) { +if (!session.user.OpenIDs) session.user.OpenIDs = {}; +if (!session.user.OpenIDs.OpenID) session.user.OpenIDs.OpenID = []; +var dupe = find_object( session.user.OpenIDs.OpenID, { Unique: session.prereg.open_id } ); +if (dupe) return do_error("That OpenID is already registered and attached to your account. No need to add it again."); +session.user.OpenIDs.OpenID.push({ +Login: session.prereg.open_id_login, +Unique: session.prereg.open_id +}); +setTimeout( function() { +Nav.go('MyAccount', true); +do_message('success', 'Added new OpenID URL to account.'); +}, 1 ); +} +else { +setTimeout( function() { Nav.go('CreateAccount', true); }, 1 ); +} +break; +} +} +function do_effect_login() { +var password = $('fe_lp_password').value; +session.auto_login = $('fe_auto_login').checked; +hide_popup_dialog(); +show_progress_dialog(1, "Logging in..."); +session.hooks.after_error = 'do_login_prompt'; +effect_api_send('user_login', { +Username: session.username, +Password: password, +Infinite: session.auto_login ? 1 : 0 +}, 'do_login_2'); +} +function do_logout() { +effect_api_send('user_logout', {}, 'do_logout_2'); +} +function do_logout_2(response) { +hide_popup_dialog(); +show_default_login_status(); +delete session.hooks.after_error; +delete session.cookie.tree.effect_session_id; +session.cookie.save(); +session.storage = {}; +session.storage_dirty = false; +delete session.user; +delete session.first_login; +var old_username = session.username; +session.username = ''; +if (Nav.inited) { +Nav.go('Main'); +if (old_username) $GR.growl('success', "Logged out of account: " + old_username); +} +else { +Nav.init(); +} +} +function do_login_2(response, tx) { +if (response.FirstLogin) session.first_login = 1; +if (response.User.UserStorage) { +Debug.trace('Recovering site storage blob: session.storage = ' + response.User.UserStorage + ';'); +try { +eval( 'session.storage = ' + response.User.UserStorage + ';' ); +} +catch (e) { +Debug.trace("SITE STORAGE RECOVERY FAILED: " + e); +session.storage = {}; +} +delete response.User.UserStorage; +session.storage_dirty = false; +} +session.user = response.User; +session.username = session.user.Username; +hide_popup_dialog(); +delete session.hooks.after_error; +update_header(); +if (!tx || !tx._from_recover) $GR.growl('success', "Logged in as: " + session.username); +if (session.nav_after_login) { +Nav.go( session.nav_after_login ); +delete session.nav_after_login; +} +else if (Nav.currentAnchor().match(/^Login/)) { +Nav.go('Home'); +} +else { +Nav.refresh(); +} +Nav.init(); +} +function user_storage_mark() { +Debug.trace("Marking user storage as dirty"); +session.storage_dirty = true; +} +function user_storage_idle() { +if (session.storage_dirty && !session.mouseIsDown) { +user_storage_save(); +session.storage_dirty = false; +} +setTimeout( 'user_storage_idle()', 5000 ); +} +function user_storage_save() { +if (session.user) { +Debug.trace("Committing user storage blob"); +effect_api_send('update_user_storage', { Data: serialize(session.storage) }, 'user_storage_save_finish', { _silent: 1 } ); +} +} +function user_storage_save_finish(response, tx) { +} +function show_default_login_status() { +$('d_sidebar_wrapper_recent_games').hide(); +$('d_login_status').innerHTML = '
' + +'
' + +large_icon_button('key', "Login", '#Home') + '' + spacer(1,1) + '' + +'' + large_icon_button('user_add.png', "Signup", '#CreateAccount') + '
' + +'
'; +$('d_tagline').innerHTML = +'Login' + ' | ' + +'Create Account'; +} +function update_header() { +var html = ''; +html += '
'; +html += ''; +html += ''; +html += ''; +html += ''+spacer(2,2)+''; +html += session.user.FullName + '
'; +html += spacer(1,5) + '
'; +html += 'My Home  |  '; +html += 'Logout'; +html += '
'; +$('d_login_status').innerHTML = html; +$('d_tagline').innerHTML = +'Welcome '+session.user.FirstName+'' + ' | ' + +'My Home' + ' | ' + +'Logout'; +effect_api_get( 'get_user_games', { limit:5, offset:0 }, 'receive_sidebar_recent_games', { } ); +} +function receive_sidebar_recent_games(response, tx) { +var html = ''; +if (response.Rows && response.Rows.Row) { +var games = always_array( response.Rows.Row ); +for (var idx = 0, len = games.length; idx < len; idx++) { +var game = games[idx]; +html += ''; +} +html += ''; +$('d_sidebar_recent_games').innerHTML = html; +$('d_sidebar_wrapper_recent_games').show(); +} +else { +$('d_sidebar_wrapper_recent_games').hide(); +} +} +function check_privilege(key) { +if (!session.user) return false; +if (session.user.Privileges.admin == 1) return true; +if (!key.toString().match(/^\//)) key = '/' + key; +var value = lookup_path(key, session.user.Privileges); +return( value && (value != 0) ); +} +function is_admin() { +return check_privilege('admin'); +} +function upgrade_flash_error() { +return alert("Sorry, file upload requires Adobe Flash Player 9 or higher."); +} +function cancel_user_image_manager() { +upload_destroy(); +hide_popup_dialog(); +delete session.hooks.keys[DELETE_KEY]; +} +function do_user_image_manager(callback) { +if (callback) session.uim_callback = callback; +else session.uim_callback = null; +session.temp_last_user_img = null; +session.temp_last_user_image_filename = ''; +var html = '
'; +html += '
Image Manager
'; +html += '
'; +html += ''; +html += '
'; +html += '
'; +html += ''; +html += ''; +html += ''; +html += ''; +html += ''; +html += '
' + large_icon_button('x', 'Cancel', 'cancel_user_image_manager()') + ' ' + large_icon_button('bullet_upload.png', 'Upload Files...', 'upload_basic()', 'b_upload_user_image') + ' ' + large_icon_button('check', 'Choose', 'do_choose_user_image()', 'btn_choose_user_image', '', 'disabled') + '
'; +html += '
'; +session.hooks.keys[ENTER_KEY] = 'do_choose_user_image'; +session.hooks.keys[ESC_KEY] = 'cancel_user_image_manager'; +session.hooks.keys[DELETE_KEY] = 'do_delete_selected_user_image'; +show_popup_dialog(500, 300, html); +var self = this; +setTimeout( function() { +prep_upload('b_upload_user_image', '/effect/api/upload_user_image', [self, 'do_upload_user_image_2'], ['Image Files', '*.jpg;*.jpe;*.jpeg;*.gif;*.png']); +}, 1 ); +var args = { +limit: 50, +offset: 0, +random: Math.random() +}; +effect_api_get( 'user_images_get', args, 'uim_populate_images', { } ); +} +function do_upload_user_image_2() { +effect_api_mod_touch('user_images_get'); +effect_api_send('user_get', { +Username: session.username +}, [this, 'do_upload_user_image_3']); +} +function do_upload_user_image_3(response) { +if (response.User.LastUploadError) return do_error( "Failed to upload image: " + response.User.LastUploadError ); +do_user_image_manager( session.uim_callback ); +} +function uim_populate_images(response, tx) { +var html = ''; +var base_url = '/effect/api/view/users/' + session.username + '/images'; +if (response.Rows && response.Rows.Row) { +var imgs = always_array( response.Rows.Row ); +for (var idx = 0, len = imgs.length; idx < len; idx++) { +var img = imgs[idx]; +var class_name = ((img.Filename == session.temp_last_user_image_filename) ? 'choose_item_selected' : 'choose_item'); +html += ''; +} +} +else { +html = ''; +} +$('d_user_image_list').innerHTML = html; +} +function do_select_user_image(img, filename) { +if (session.temp_last_user_img) session.temp_last_user_img.className = 'choose_item'; +img.className = 'choose_item_selected'; +$('btn_choose_user_image').removeClass('disabled'); +session.temp_last_user_img = img; +session.temp_last_user_image_filename = filename; +} +function do_delete_selected_user_image() { +if (session.temp_last_user_image_filename) { +effect_api_send('user_image_delete', { Filename: session.temp_last_user_image_filename }, 'do_delete_selected_user_image_finish', {}); +} +} +function do_delete_selected_user_image_finish(response, tx) { +try { $('d_user_image_list').removeChild( session.temp_last_user_img ); } catch(e) {;} +session.temp_last_user_img = null; +session.temp_last_user_image_filename = null; +} +function do_choose_user_image() { +if (!session.temp_last_user_image_filename) return; +if (session.uim_callback) { +fire_callback( session.uim_callback, session.temp_last_user_image_filename ); +} +cancel_user_image_manager(); +} +function user_image_thumbnail(filename, width, height, attribs) { +var username = session.username; +if (filename.match(/^(\w+)\/(.+)$/)) { +username = RegExp.$1; +filename = RegExp.$2; +} +var url = '/effect/api/view/users/' + username + '/images/' + filename.replace(/\.(\w+)$/, '_thumb.jpg'); +return ''; +} +function get_user_display(username, full_name, base_url) { +if (!base_url) base_url = ''; +return icon('user', full_name || username, base_url + '#User/' + username); +} +function get_game_tab_bar(game_id, cur_page_name) { +return tab_bar([ +['#Game/' + game_id, 'Game', 'controller.png'], +['#GameDisplay/' + game_id, 'Display', 'monitor.png'], +['#GameAssets/' + game_id, 'Assets', 'folder_page_white.png'], +['#GameObjects/' + game_id, 'Objects', 'bricks.png'], +['#GameAudio/' + game_id, 'Audio', 'sound.gif'], +['#GameKeys/' + game_id, 'Keyboard', 'keyboard.png'], +['#GameLevels/' + game_id, 'Levels', 'world.png'], +['#GamePublisher/' + game_id, 'Publish', 'cd.png'] +], cur_page_name); +} +function get_user_tab_bar(cur_page_name) { +var tabs = [ +['#Home', 'My Home', 'house.png'] +]; +tabs.push( ['#MyAccount', 'Edit Account', 'user_edit.png'] ); +tabs.push( ['#ArticleEdit', 'Post Article', 'page_white_edit.png'] ); +if (config.ProEnabled) { +tabs.push( ['#UserPayments', 'Payments', 'money.png'] ); +} +tabs.push( ['#UserLog', 'Security Log', 'application_view_detail.png'] ); +return tab_bar(tabs, cur_page_name); +} +function get_admin_tab_bar(cur_page_name) { +var tabs = []; +tabs.push( ['#Admin', 'Admin', 'lock.png'] ); +tabs.push( ['#TicketSearch/bugs', 'Bug Tracker', 'bug.png'] ); +tabs.push( ['#TicketSearch/helpdesk', 'Help Desk', 'telephone.png'] ); +tabs.push( ['#AdminReport', 'Reports', 'chart_pie.png'] ); +return tab_bar(tabs, cur_page_name); +} +function get_string(path, args) { +assert(window.config, "get_string() called before config loaded"); +if (!args) args = {}; +args.config = config; +args.session = session; +args.query = session.query; +var value = lookup_path(path, config.Strings); +return (typeof(value) == 'string') ? substitute(value, args) : value; +} +function normalize_dir_path(path) { +if (!path.match(/^\//)) path = '/' + path; +if (!path.match(/\/$/)) path += '/'; +return path; +} +function textedit_window_save(storage_key, filename, content, callback) { +if (!callback) callback = null; +effect_api_mod_touch('textedit'); +if (storage_key.match(/^\/games\/([a-z0-9][a-z0-9\-]*[a-z0-9])\/assets(.+)$/)) { +var game_id = RegExp.$1; +var path = RegExp.$2; +show_progress_dialog(1, "Saving file..."); +effect_api_send('asset_save_file_contents', { +GameID: game_id, +Path: path, +Filename: filename, +Content: content +}, 'textedit_window_save_finish', { _mode: 'asset', _game_id: game_id, _filename: filename, _callback: callback } ); +} +else { +show_progress_dialog(1, "Saving data..."); +effect_api_send('admin_save_file_contents', { +Path: storage_key, +Filename: filename, +Content: content +}, 'textedit_window_save_finish', { _mode: 'admin', _storage_key: storage_key, _filename: filename, _callback: callback } ); +} +} +function textedit_window_save_finish(response, tx) { +hide_progress_dialog(); +if (tx._mode == 'asset') { +do_message('success', "Saved asset: \""+tx._filename+"\""); +show_glog_widget(); +} +else { +do_message('success', "Saved data: \""+tx._storage_key+'/'+tx._filename+"\""); +} +if (tx._callback) tx._callback(); +} +function do_buy(args) { +$P().hide(); +$('d_page_loading').show(); +effect_api_send('create_order', args, 'do_buy_redirect', { _buy_args: args } ); +} +function do_buy_redirect(response, tx) { +var args = tx._buy_args; +$('fe_gco_title').value = args.Title || ''; +$('fe_gco_desc').value = args.Desc || ''; +$('fe_gco_price').value = args.Price || ''; +$('fe_gco_after').value = args.After || ''; +$('fe_gco_unique_id').value = response.OrderID; +Debug.trace('payment', "Redirecting to Google Checkout"); +setTimeout( function() { $('BB_BuyButtonForm').submit(); }, 1 ); +} +function show_glog_widget(game_id) { +if (!game_id) game_id = session.glog_game_id; +if (!game_id) { +$('glog_widget').hide(); +return; +} +if (game_id != session.glog_game_id) { +$('glog_widget').hide(); +session.glog_game_id = game_id; +update_glog_widget(game_id); +} +else { +$('glog_widget').show(); +setTimeout( function() { update_glog_widget(game_id); }, 500 ); +} +} +function update_glog_widget(game_id) { +effect_api_get('game_get_log', { +id: game_id, +offset: 0, +limit: 1, +rand: Math.random() +}, 'receive_glog_data', { _game_id: game_id }); +} +function receive_glog_data(response, tx) { +var game_id = tx._game_id; +if (response && response.Rows && response.Rows.Row) { +var rows = always_array( response.Rows.Row ); +var row = rows[0]; +var html = ''; +html += '
'; +html += '
Latest Game Activity
'; +html += ''; +html += ''; +html += '
'; +html += '
'; +html += ''; +html += ''; +html += ''; +html += '
' + get_buddy_icon_display(row.Username, 1, 0) + ''; +html += '
' + icon( get_icon_for_glog_type(row.Type), ''+row.Message+'' ) + '
'; +html += '
' + get_relative_date(row.Date, true) + '
'; +html += '
'; +$('glog_widget').innerHTML = html; +$('glog_widget').show(); +} +} +function show_glog_post_dialog(game_id) { +hide_popup_dialog(); +delete session.progress; +var html = ''; +html += '
'; +html += '\n \n \n \n'); + }; + __out.push('\n\n'); + __out.push(require('templates/clients/modules/sub_header').call(this, { + heading: t("Ride Request") + })); + __out.push('\n\n\n
\n
\n
\n
\n \n \n \n \n
\n\n
'; +html += '
Post Game Log Message
'; +html += '
'; +html += ''; +html += '
Enter your log message here. Plain text only please.
'; +html += '
'; +html += '

'; +html += ''; +html += ''; +html += ''; +html += '
' + large_icon_button('x', 'Cancel', "hide_popup_dialog()") + ' ' + large_icon_button('check', 'Post Message', "glog_post('"+game_id+"')") + '
'; +html += '
'; +html += ''; +session.hooks.keys[ESC_KEY] = 'hide_popup_dialog'; +safe_focus( 'fe_glog_body' ); +show_popup_dialog(500, 175, html); +} +function glog_post(game_id) { +var msg = trim( $('fe_glog_body').value ); +if (msg) { +hide_popup_dialog(); +effect_api_send('game_post_log', { +GameID: game_id, +Message: msg +}, [this, 'glog_post_finish'], { _game_id: game_id }); +} +} +function glog_post_finish(response, tx) { +show_glog_widget( tx._game_id ); +} +function hide_glog_widget() { +$('glog_widget').hide(); +} +function get_icon_for_glog_type(type) { +var icon = 'page_white.png'; +switch (type) { +case 'asset': icon = 'folder_page_white.png'; break; +case 'game': icon = 'controller.png'; break; +case 'member': icon = 'user'; break; +case 'comment': icon = 'comment.png'; break; +case 'level': icon = 'world.png'; break; +case 'sprite': icon = 'cog.png'; break; +case 'tile': icon = 'brick.png'; break; +case 'tileset': icon = 'color_swatch.png'; break; +case 'rev': icon = 'cd.png'; break; +case 'revision': icon = 'cd.png'; break; +case 'font': icon = 'style.png'; break; +case 'key': icon = 'keyboard.png'; break; +case 'audio': icon = 'sound'; break; +case 'payment': icon = 'money.png'; break; +case 'env': icon = 'weather.png'; break; +case 'environment': icon = 'weather.png'; break; +} +return icon; +} +function effect_load_script(url) { +Debug.trace('api', 'Loading script: ' + url); +load_script(url); +} +function effect_api_get_ie(cmd, params, userData) { +if (!session.api_state_ie) session.api_state_ie = {}; +var unique_id = get_unique_id(); +session.api_state_ie[unique_id] = userData; +params.format = 'js'; +params.onafter = 'effect_api_response_ie(' + unique_id + ', response);'; +var url = '/effect/api/' + cmd + composeQueryString(params); +Debug.trace('api', "Sending MSIE HTTP GET: " + url); +load_script(url); +} +function effect_api_response_ie(unique_id, tree) { +Debug.trace('api', "Got response from MSIE HTTP GET"); +var tx = session.api_state_ie[unique_id]; +delete session.api_state_ie[unique_id]; +if (tree.Code == 'session') { +do_logout_2(); +return; +} +if (tree.Code == 'access') { +do_notice("Access Denied", tree.Description, 'do_not_pass_go'); +return; +} +if (tree.Code != 0) { +if (tx._on_error) return fire_callback( tx._on_error, tree, tx ); +return do_error( tree.Description ); +} +if (tree.SessionID) { +if (tree.SessionID == '_DELETE_') { +delete session.cookie.tree.effect_session_id; +} +else { +session.cookie.set( 'effect_session_id', tree.SessionID ); +} +session.cookie.save(); +} +if (tx._api_callback) { +fire_callback( tx._api_callback, tree, tx ); +} +} +function effect_api_get(cmd, params, callback, userData) { +if (!userData) userData = {}; +userData._api_callback = callback; +if (!session.api_mod_cache[cmd] && session.username) session.api_mod_cache[cmd] = hires_time_now(); +if (!params.mod && session.api_mod_cache[cmd]) params.mod = session.api_mod_cache[cmd]; +if (ie) return effect_api_get_ie(cmd, params, userData); +var url = '/effect/api/' + cmd + composeQueryString(params); +Debug.trace('api', "Sending HTTP GET: " + url); +ajax.get( url, 'effect_api_response', userData ); +} +function effect_api_send(cmd, xml, callback, userData) { +if (!userData) userData = {}; +userData._api_callback = callback; +var data = compose_xml('EffectRequest', xml); +Debug.trace('api', "Sending API Command: " + cmd + ": " + data); +ajax.send({ +method: 'POST', +url: '/effect/api/' + cmd, +data: data, +headers: { 'Content-Type': 'text/xml' } +}, 'effect_api_response', userData); +} +function effect_api_response(tx) { +Debug.trace('api', "HTTP " + tx.response.code + ": " + tx.response.data); +if (tx.response.code == 999) { +if (tx.request._auto_retry) { +session.net_error = false; +show_progress_dialog(1, "Trying to reestablish connection..."); +session.net_error = true; +setTimeout( function() { ajax.send(tx.request); }, 1000 ); +return; +} +else return do_error( "HTTP ERROR: " + tx.response.code + ": " + tx.response.data + ' (URL: ' + tx.request.url + ')' ); +} +if (session.net_error) { +hide_progress_dialog(); +session.net_error = false; +} +if (tx.response.code != 200) { +if (tx._silent) return; +else return do_error( "HTTP ERROR: " + tx.response.code + ": " + tx.response.data + ' (URL: ' + tx.request.url + ')' ); +} +var tree = null; +if (!tx._raw) { +var parser = new XML({ +preserveAttributes: true, +text: tx.response.data +}); +if (parser.getLastError()) return do_error("XML PARSE ERROR: " + parser.getLastError()); +tree = parser.getTree(); +if (tree.Code == 'session') { +do_logout_2(); +return; +} +if (tree.Code == 'access') { +do_notice("Access Denied", tree.Description, 'do_not_pass_go'); +return; +} +if (tree.Code != 0) { +if (tx._on_error) return fire_callback( tx._on_error, tree, tx ); +return do_error( tree.Description ); +} +if (tree.SessionID) { +if (tree.SessionID == '_DELETE_') { +delete session.cookie.tree.effect_session_id; +} +else { +session.cookie.set( 'effect_session_id', tree.SessionID ); +} +session.cookie.save(); +} +} +if (tx._api_callback) { +fire_callback( tx._api_callback, tree, tx ); +} +} +function effect_api_mod_touch() { +for (var idx = 0, len = arguments.length; idx < len; idx++) { +session.api_mod_cache[ arguments[idx] ] = hires_time_now(); +} +} +function do_not_pass_go() { +Nav.go('Main'); +} +var Nav = { +loc: '', +old_loc: '', +inited: false, +nodes: [], +init: function() { +if (!this.inited) { +this.inited = true; +this.loc = 'init'; +this.monitor(); +} +}, +monitor: function() { +var parts = window.location.href.split(/\#/); +var anchor = parts[1]; +if (!anchor) anchor = 'Main'; +var full_anchor = '' + anchor; +var sub_anchor = ''; +anchor = anchor.replace(/\%7C/, '|'); +if (anchor.match(/\|(\w+)$/)) { +sub_anchor = RegExp.$1.toLowerCase(); +anchor = anchor.replace(/\|(\w+)$/, ''); +} +if ((anchor != this.loc) && !anchor.match(/^_/)) { +Debug.trace('nav', "Caught navigation anchor: " + full_anchor); +var page_name = ''; +var page_args = null; +if (full_anchor.match(/^\w+\?.+/)) { +parts = full_anchor.split(/\?/); +page_name = parts[0]; +page_args = parseQueryString( parts[1] ); +} +else if (full_anchor.match(/^(\w+)\/(.*)$/)) { +page_name = RegExp.$1; +page_args = RegExp.$2; +} +else { +parts = full_anchor.split(/\//); +page_name = parts[0]; +page_args = parts.slice(1); +} +Debug.trace('nav', "Calling page: " + page_name + ": " + serialize(page_args)); +hide_popup_dialog(); +var result = page_manager.click( page_name, page_args ); +if (result) { +if (window.pageTracker && (this.loc != 'init')) { +setTimeout( function() { pageTracker._trackPageview('/effect/' + anchor); }, 1000 ); +} +this.old_loc = this.loc; +if (this.old_loc == 'init') this.old_loc = 'Main'; +this.loc = anchor; +} +else { +this.go( this.loc ); +} +} +else if (sub_anchor != this.sub_anchor) { +Debug.trace('nav', "Caught sub-anchor: " + sub_anchor); +$P().gosub( sub_anchor ); +} +this.sub_anchor = sub_anchor; +setTimeout( 'Nav.monitor()', 100 ); +}, +go: function(anchor, force) { +anchor = anchor.replace(/^\#/, ''); +if (force) this.loc = 'init'; +window.location.href = '#' + anchor; +}, +prev: function() { +this.go( this.old_loc || 'Main' ); +}, +refresh: function() { +this.loc = 'refresh'; +}, +bar: function() { +var nodes = arguments; +var html = ''; +for (var idx = 0, len = nodes.length; idx < len; idx++) { +var node = nodes[idx]; +if (node) this.nodes[idx] = node; +else node = this.nodes[idx]; +if (node != '_ignore_') { +html += ''; +} +} +html += '
'; +$('d_nav_bar').innerHTML = html; +}, +title: function(name) { +if (name) document.title = name + ' | EffectGames.com'; +else document.title = 'EffectGames.com'; +}, +currentAnchor: function() { +var parts = window.location.href.split(/\#/); +var anchor = parts[1] || ''; +var sub_anchor = ''; +anchor = anchor.replace(/\%7C/, '|'); +if (anchor.match(/\|(\w+)$/)) { +sub_anchor = RegExp.$1.toLowerCase(); +anchor = anchor.replace(/\|(\w+)$/, ''); +} +return anchor; +} +}; +var Blog = { +edit_caption: '
*Bold*  |Italic|  {monospace}  [http://link]  Formatting Guide...
', +search: function(args) { +if (!args.mode) args.mode = 'and'; +if (!args.offset) args.offset = 0; +if (!args.limit) args.limit = 10; +if (!args.format) args.format = 'xml'; +var query_args = copy_object( args ); +delete query_args.callback; +effect_api_get( 'article_search', query_args, [this, 'search_response'], { _search_args: args } ); +}, +get_article_preview: function(row, args) { +var html = ''; +Debug.trace('blog', 'Row: ' + dumper(row)); +html += '
'; +var ext_article_url = 'http://' + location.hostname + '/effect/article.psp.html' + row.Path + '/' + row.ArticleID; +var article_url = '#Article' + row.Path + '/' + row.ArticleID; +html += ''; +if (!args.title_only) { +html += '
'; +html += row.Preview; +html += '  ' + (args.link_title || 'Read Full Story...') + ''; +html += '
'; +html += ''; +html += '
'; +var elem_class = args.footer_element_class || 'blog_preview_footer_element'; +if ((session.username == row.Username) || is_admin()) { +html += '
' + +icon('page_white_edit.png', "Edit", '#ArticleEdit?path=' + row.Path + '&id=' + row.ArticleID) + '
'; +} +html += '
' + get_user_display(row.Username) + '
'; +html += '
' + icon('calendar', get_short_date_time(row.Published)) + '
'; +html += '
' + icon('talk', row.Comments) + '
'; +if (0 && row.Tags) html += '
' + icon('note.png', make_tag_links(row.Tags, 3)) + '
'; +html += '
' + icon('facebook.png', 'Facebook', "window.open('http://www.facebook.com/sharer.php?u="+encodeURIComponent(ext_article_url)+'&t='+encodeURIComponent(row.Title)+"','sharer','toolbar=0,status=0,width=626,height=436')", "Share on Facebook") + '
'; +html += '
' + icon('twitter.png', 'Twitter', "window.open('http://twitter.com/home?status=Reading%20" + encodeURIComponent(row.Title) + "%3A%20" + encodeURIComponent(ext_article_url)+"')", "Share on Twitter") + '
'; +html += '
'; +html += '
'; +html += '
'; +} +html += '
'; +return html; +}, +search_response: function(response, tx) { +var args = tx._search_args; +if (args.callback) return fire_callback(args.callback, response, args); +var div = $(args.target); +assert(div, "Could not find target DIV: " + args.target); +var html = ''; +if (response.Rows && response.Rows.Row) { +var rows = always_array( response.Rows.Row ); +for (var idx = 0, len = rows.length; idx < len; idx++) { +var row = rows[idx]; +html += this.get_article_preview( row, args ); +} +if (args.more && (rows.length == args.limit)) { +html += large_icon_button('page_white_put.png', 'More...', "Blog.more(this, "+encode_object(args)+")") + '
'; +html += spacer(1,15) + '
'; +} +if (args.after) html += args.after; +} +else if (response.Code != 0) { +html = 'Search Error: ' . response.Code + ': ' + response.Description; +} +else { +html = args.none_found_msg || 'No articles found.'; +} +div.innerHTML = html; +}, +more: function(div, args) { +args.offset += args.limit; +Debug.trace('blog', "More Args: " + dumper(args)); +div.innerHTML = ''; +effect_api_get( 'article_search', args, [this, 'more_response'], { _search_args: args, _div: div } ); +}, +more_response: function(response, tx) { +var args = tx._search_args; +var button = tx._div; +var html = ''; +if (response.Rows && response.Rows.Row) { +var rows = always_array( response.Rows.Row ); +for (var idx = 0, len = rows.length; idx < len; idx++) { +var row = rows[idx]; +html += this.get_article_preview( row, args ); +} +if (args.more && (rows.length == args.limit)) { +html += large_icon_button('page_white_put.png', 'More...', "Blog.more(this, "+encode_object(args)+")") + '
'; +html += spacer(1,15) + '
'; +} +} +else if (response.Code != 0) { +html = 'Search Error: ' . response.Code + ': ' + response.Description; +} +else { +html = args.none_found_msg || 'No more articles found.'; +} +var div = document.createElement('div'); +div.innerHTML = html; +button.parentNode.replaceChild( div, button ); +} +}; +function make_tag_links(csv, max, base_url) { +if (!base_url) base_url = ''; +var tags = csv.split(/\,\s*/); +var append = ''; +if (max && (tags.length > max)) { +tags.length = max; +append = '...'; +} +var html = ''; +for (var idx = 0, len = tags.length; idx < len; idx++) { +html += ''+tags[idx]+''; +if (idx < len - 1) html += ', '; +} +html += append; +return html; +} +function get_url_friendly_title(title) { +title = title.toString().replace(/\W+/g, '_'); +if (title.length > 40) title = title.substring(0, 40); +title = title.replace(/^_+/, ''); +title = title.replace(/_+$/, ''); +return title; +} +function get_full_url(url) { +if (url.match(/^\#/)) { +var parts = window.location.href.split(/\#/); +url = parts[0] + url; +} +return url; +} +var Comments = { +comments_per_page: 10, +get: function(page_id) { +var html = ''; +html += '
'; +html += '
Comments'; +html += '
'; +html += '
'; +html += '
'; +setTimeout( function() { Comments.search({ page_id: page_id }); }, 1 ); +return html; +}, +search: function(args) { +if (!args.limit) args.limit = this.comments_per_page; +if (!args.offset) args.offset = 0; +assert(args.page_id, "Comments.search: No page_id specified"); +args.format = 'xml'; +this.last_search = args; +effect_api_get( 'comments_get', args, [this, 'search_response'], { _search_args: args } ); +}, +research: function(offset) { +var args = this.last_search; +if (!args) return; +args.offset = offset; +effect_api_get( 'comments_get', args, [this, 'search_response'], { _search_args: args } ); +}, +search_response: function(response, tx) { +this.comments = []; +var args = tx._search_args; +if (args.callback) return fire_callback(args.callback, response, args); +var html = ''; +html += '
' + +large_icon_button( 'comment_edit.png', 'Post Comment...', "Comments.add('"+args.page_id+"')" ) + '
'; +if (args.page_id.match(/^Article\//)) { +html += '
' + icon('feed.png', 'RSS', '/effect/api/comment_feed/' + args.page_id + '.rss', 'Comments RSS Feed') + '
'; +} +if (response.Items && response.Items.Item && response.List && response.List.length) { +html += ''; +html += '
'; +var items = this.comments = always_array( response.Items.Item ); +for (var idx = 0, len = items.length; idx < len; idx++) { +var item = items[idx]; +var extra_classes = (args.highlight && (args.highlight == item.ID)) ? ' highlight' : ''; +html += '
'; +html += '
'; +if (item.Username) html += ''; +html += '' + item.Name.toString().toUpperCase() + ''; +if (item.Username) html += ''; +html += ', ' + get_short_date_time(item.Date) + '
'; +html += '
'; +html += this.get_comment_controls( args.page_id, item ); +html += '
'; +html += '
'; +html += '
' + item.Comment + '
'; +html += '
'; +html += ''; +if (item.LastReply && ((item.LastReply >= time_now() - (86400 * 7)) || (session.username && (session.username == item.Username)))) { +setTimeout( "Comments.show_replies('"+args.page_id+"','"+item.ID+"')", 1 ); +} +} +} +else { +} +$( 'd_comments_' + args.page_id ).innerHTML = html; +}, +get_control: function(icon, code, text, status_text) { +if (!icon.match(/\.\w+$/)) icon += '.gif'; +return '' + code_link(code, text, status_text) + ''; +}, +get_comment_controls: function(page_id, comment) { +var html = ''; +var spacer_txt = '  |  '; +if (session.user) { +html += this.get_control('comment', "Comments.reply('"+page_id+"','"+comment.ID+"')", 'Reply') + spacer_txt; +} +if (comment.Replies) { +if (comment._replies_visible) html += this.get_control('magnify_minus', "Comments.hide_replies('"+page_id+"','"+comment.ID+"')", 'Hide Replies'); +else html += this.get_control('magnify_plus', "Comments.show_replies('"+page_id+"','"+comment.ID+"')", 'Show Replies ('+comment.Replies+')'); +if (session.user) html += spacer_txt; +} +if (session.user) { +html += this.get_control( +'star', +"Comments.like('"+page_id+"','"+comment.ID+"')", +'Like' + (comment.Like ? (' ('+comment.Like+')') : ''), +comment.Like ? (comment.Like + ' ' + ((comment.Like == 1) ? 'person likes this' : 'people like this')) : 'I like this comment' +) + spacer_txt; +if (is_admin()) html += this.get_control('trash', "Comments._delete('"+page_id+"','"+comment.ID+"')", 'Delete') + spacer_txt; +html += this.get_control('warning', "Comments.report('"+page_id+"','"+comment.ID+"')", 'Report Abuse'); +} +return html; +}, +reply: function(page_id, comment_id) { +hide_popup_dialog(); +delete session.progress; +var comment = find_object( this.comments, { ID: comment_id } ); +var html = ''; +html += '
'; +html += '\n \n \n \n \n \n \n \n \n \n '); + }, this); + __out.push('\n\n
\n
'; +html += '
Reply to Comment by "'+comment.Name+'"
'; +html += '
'; +var name = this.get_name(); +html += '

Posted by: ' + name; +if (!session.user) html += ' → Create Account'; +html += '


'; +html += ''; +html += Blog.edit_caption; +html += '
'; +html += '

'; +html += ''; +html += ''; +html += ''; +html += '
' + large_icon_button('x', 'Cancel', "hide_popup_dialog()") + ' ' + large_icon_button('check', 'Post Reply', "Comments.post_reply('"+page_id+"','"+comment_id+"')") + '
'; +html += '
'; +html += ''; +session.hooks.keys[ESC_KEY] = 'hide_popup_dialog'; +safe_focus( 'fe_comment_body' ); +show_popup_dialog(600, 300, html); +}, +post_reply: function(page_id, comment_id) { +var value = $('fe_comment_body').value; +if (!value) return; +hide_popup_dialog(); +show_progress_dialog(1, "Posting reply..."); +var name = this.get_name(); +effect_api_mod_touch('comment_replies_get'); +effect_api_send('comment_post_reply', { +PageID: page_id, +CommentID: comment_id, +Username: session.username || '', +Name: name, +Comment: value, +PageURL: location.href +}, [this, 'post_reply_finish'], { _page_id: page_id, _comment_id: comment_id } ); +}, +post_reply_finish: function(response, tx) { +hide_popup_dialog(); +var page_id = tx._page_id; +var comment_id = tx._comment_id; +var comment = find_object( this.comments, { ID: comment_id } ); +do_message('success', "Comment reply posted successfully."); +this.show_replies(page_id, comment_id); +if (!comment.Replies) comment.Replies = 1; else comment.Replies++; +$('d_comment_controls_'+comment_id).innerHTML = this.get_comment_controls( page_id, comment ); +}, +show_replies: function(page_id, comment_id) { +var comment = find_object( this.comments, { ID: comment_id } ); +if (!comment._replies_visible) { +$('d_comment_replies_' + comment_id).show().innerHTML = ''; +} +var args = { page_id: page_id, comment_id: comment_id, offset: 0, limit: 100 }; +effect_api_get( 'comment_replies_get', args, [this, 'receive_replies_response'], { _search_args: args } ); +}, +receive_replies_response: function(response, tx) { +var page_id = tx._search_args.page_id; +var comment_id = tx._search_args.comment_id; +var comment = find_object( this.comments, { ID: comment_id } ); +var html = ''; +var replies = always_array( response.Items.Item ); +for (var idx = 0, len = replies.length; idx < len; idx++) { +var reply = replies[idx]; +html += get_chat_balloon( +(reply.Username == session.username) ? 'blue' : 'grey', +reply.Username, +reply.Comment.replace(/^]*?>(.+)<\/div>$/i, '$1') +); +} +$('d_comment_replies_' + comment_id).innerHTML = html; +if (!comment._replies_visible) { +$('d_comment_replies_' + comment_id).hide(); +animate_div_visibility( 'd_comment_replies_' + comment_id, true ); +} +comment._replies_visible = true; +$('d_comment_controls_'+comment_id).innerHTML = this.get_comment_controls( page_id, comment ); +}, +hide_replies: function(page_id, comment_id) { +var comment = find_object( this.comments, { ID: comment_id } ); +if (comment._replies_visible) { +animate_div_visibility( 'd_comment_replies_' + comment_id, false ); +comment._replies_visible = false; +$('d_comment_controls_'+comment_id).innerHTML = this.get_comment_controls( page_id, comment ); +} +}, +like: function(page_id, comment_id) { +effect_api_mod_touch('comments_get'); +effect_api_send('comment_like', { +PageID: page_id, +CommentID: comment_id +}, [this, 'like_finish'], { _page_id: page_id, _comment_id: comment_id, _on_error: [this, 'like_error'] } ); +}, +like_error: function(response, tx) { +if (response.Code == 'comment_already_like') do_message('error', "You already like this comment."); +else do_error( response.Description ); +}, +like_finish: function(resopnse, tx) { +var page_id = tx._page_id; +var comment_id = tx._comment_id; +var comment = find_object( this.comments, { ID: comment_id } ); +do_message('success', "You now like this comment."); +if (!comment.Like) comment.Like = 1; else comment.Like++; +$('d_comment_controls_'+comment_id).innerHTML = this.get_comment_controls( page_id, comment ); +}, +add: function(page_id) { +hide_popup_dialog(); +delete session.progress; +var html = ''; +html += '
'; +html += '", "" ], + legend: [ 1, "
", "
" ], + thead: [ 1, "
'; +html += '
Post New Comment
'; +html += '
'; +var name = this.get_name(); +html += '

Posted by: ' + name; +if (!session.user) html += ' → Create Account'; +html += '


'; +html += ''; +html += Blog.edit_caption; +html += '
'; +html += '

'; +html += ''; +html += ''; +html += ''; +html += '
' + large_icon_button('x', 'Cancel', "hide_popup_dialog()") + ' ' + large_icon_button('check', 'Post Comment', "Comments.post('"+page_id+"')") + '
'; +html += '
'; +html += ''; +session.hooks.keys[ESC_KEY] = 'hide_popup_dialog'; +safe_focus( 'fe_comment_body' ); +show_popup_dialog(600, 300, html); +}, +report: function(page_id, comment_id) { +if (confirm('Are you sure you want to report this comment to the site administrators as abusive and/or spam?')) { +effect_api_send('comment_report_abuse', { +PageID: page_id, +CommentID: comment_id +}, [this, 'report_finish'], { _page_id: page_id, _comment_id: comment_id } ); +} +}, +report_finish: function(response, tx) { +do_message('success', 'Your abuse report has been received, and will be evaluated by the site administrators.'); +}, +_delete: function(page_id, comment_id) { +if (confirm('Are you sure you want to permanently delete this comment?')) { +effect_api_mod_touch('comments_get'); +effect_api_send('comment_delete', { +PageID: page_id, +CommentID: comment_id +}, [this, 'delete_finish'], { _page_id: page_id, _comment_id: comment_id } ); +} +}, +delete_finish: function(response, tx) { +do_message('success', 'The comment was deleted successfully.'); +var page_id = tx._page_id; +this.search({ page_id: page_id }); +}, +get_name: function() { +var name = '(Anonymous)'; +if (session.user) { +if (get_bool_pref('public_profile')) name = session.user.FullName; +else name = session.username; +} +return name; +}, +post: function(page_id) { +var value = $('fe_comment_body').value; +if (!value) return; +hide_popup_dialog(); +show_progress_dialog(1, "Posting comment..."); +var name = this.get_name(); +effect_api_mod_touch('comments_get'); +effect_api_send('comment_post', { +PageID: page_id, +Username: session.username || '', +Name: name, +Comment: value +}, [this, 'post_finish'], { _page_id: page_id } ); +}, +post_finish: function(response, tx) { +hide_popup_dialog(); +var comment_id = response.CommentID; +var page_id = tx._page_id; +this.search({ page_id: page_id, highlight: comment_id }); +} +}; +Class.create( 'Menu', { +id: '', +menu: null, +__construct: function(id) { +this.id = id; +}, +load: function() { +if (!this.menu) { +this.menu = $(this.id); +assert( !!this.menu, "Could not locate DOM element: " + this.id ); +} +}, +get_value: function() { +this.load(); +return this.menu.options[this.menu.selectedIndex].value; +}, +set_value: function(value, auto_add) { +value = str_value(value); +this.load(); +for (var idx = 0, len = this.menu.options.length; idx < len; idx++) { +if (this.menu.options[idx].value == value) { +this.menu.selectedIndex = idx; +return true; +} +} +if (auto_add) { +this.menu.options[this.menu.options.length] = new Option(value, value); +this.menu.selectedIndex = this.menu.options.length - 1; +return true; +} +return false; +}, +disable: function() { +this.load(); +this.menu.disabled = true; +this.menu.setAttribute( 'disabled', 'disabled' ); +}, +enable: function() { +this.load(); +this.menu.setAttribute( 'disabled', '' ); +this.menu.disabled = false; +}, +populate: function(items, sel_value) { +this.load(); +this.menu.options.length = 0; +for (var idx = 0, len = items.length; idx < len; idx++) { +var item = items[idx]; +var item_name = ''; +var item_value = ''; +if (isa_hash(item)) { +item_name = item.label; +item_value = item.data; +} +else if (isa_array(item)) { +item_name = item[0]; +item_value = item[1]; +} +else { +item_name = item_value = item; +} +this.menu.options[ this.menu.options.length ] = new Option( item_name, item_value ); +if (item_value == sel_value) this.menu.selectedIndex = idx; +} +} +} ); +Class.subclass( Menu, 'MultiMenu', { +__static: { +toggle_type: function(id) { +var menu = $(id); +assert(menu, "Could not find menu in DOM: " + id); +if (menu.disabled) return; +var obj = MenuManager.find(id); +assert(obj, "Could not find menu in MenuManager: " + id); +var div = $( 'd_inner_' + id ); +var ic = $( 'ic_' + id ); +var is_multiple = (ic.src.indexOf('contract') > -1); +obj.multi = !is_multiple; +var multiple_tag = !is_multiple ? +' multiple="multiple" size=5' : ''; +var items = []; +for (var idx = 0; idx < menu.options.length; idx++) { +var option = menu.options[idx]; +array_push( items, { +value: option.value, +text: option.text, +selected: option.selected +}); +} +var html = ''; +html += ''; +div.innerHTML = html; +ic.src = images_uri + '/menu_' + (is_multiple ? 'expand' : 'contract') + '.gif'; +obj.menu = null; +} +}, +attribs: null, +multi: false, +toggle: true, +__construct: function(id, attribs) { +this.id = id; +if (attribs) this.attribs = attribs; +}, +get_html: function(items, selected_csv, attribs) { +if (!items) items = []; +if (!selected_csv) selected_csv = ''; +if (attribs) this.attribs = attribs; +var selected = csv_to_hash(selected_csv); +this.menu = null; +if (num_keys(selected) > 1) this.multi = true; +var html = '
'; +html += ''; +html += ''; +html += ''; +if (this.toggle) html += ''; +html += '
' + spacer(1,1) + '
'+spacer(1,2)+'
'; +html += '
'; +return html; +}, +get_value: function() { +this.load(); +var value = ''; +for (var idx = 0; idx < this.menu.options.length; idx++) { +var option = this.menu.options[idx]; +if (option.selected && option.value.length) { +if (value.length > 0) value += ','; +value += option.value; +} +} +return value; +}, +set_value: function(value, auto_add) { +value = '' + value; +this.load(); +if (!value) { +value = ''; +for (var idx = 0; idx < this.menu.options.length; idx++) { +var option = this.menu.options[idx]; +option.selected = (option.value == value); +} +return; +} +var selected = csv_to_hash(value); +if ((num_keys(selected) > 1) && !this.multi) { +MultiMenu.toggle_type(this.id); +var self = this; +setTimeout( function() { +self.set_value(value, auto_add); +}, 1 ); +return; +} +for (var idx = 0; idx < this.menu.options.length; idx++) { +var option = this.menu.options[idx]; +option.selected = selected[option.value] ? true : false; +} +}, +populate: function(items, value) { +this.load(); +this.menu.options.length = 0; +if (!value) value = ''; +var selected = csv_to_hash(value); +for (var idx = 0, len = items.length; idx < len; idx++) { +var item = items[idx]; +var item_name = ''; +var item_value = ''; +if (isa_hash(item)) { +item_name = item.label; +item_value = item.data; +} +else if (isa_array(item)) { +item_name = item[0]; +item_value = item[1]; +} +else { +item_name = item_value = item; +} +var opt = new Option( item_name, item_value ); +this.menu.options[ this.menu.options.length ] = opt; +opt.selected = selected[item_value] ? true : false; +} +}, +collapse: function() { +if (this.multi) MultiMenu.toggle_type(this.id); +}, +expand: function() { +if (!this.multi) MultiMenu.toggle_type(this.id); +} +} ); +Class.create( 'MenuManager', { +__static: { +menus: {}, +register: function(menu) { +this.menus[ menu.id ] = menu; +return menu; +}, +find: function(id) { +return this.menus[id]; +} +} +} ); +Class.create( 'GrowlManager', { +lifetime: 10, +marginRight: 0, +marginTop: 0, +__construct: function() { +this.growls = []; +}, +growl: function(type, msg) { +if (find_object(this.growls, { type: type, msg: msg })) return; +var div = $(document.createElement('div')); +div.className = 'growl_message ' + type; +div.setOpacity(0.0); +div.innerHTML = '
' + msg + '
' + spacer(1,5) + '
'; +$('d_growl_wrapper').insertBefore( div, $('d_growl_top').nextSibling ); +var growl = { id:get_unique_id(), type: type, msg: msg, opacity:0.0, start:hires_time_now(), div:div }; +this.growls.push(growl); +this.handle_resize(); +this.animate(growl); +var self = this; +div.onclick = function() { +delete_object(self.growls, { id: growl.id }); +$('d_growl_wrapper').removeChild( div ); +}; +}, +animate: function(growl) { +if (growl.deleted) return; +var now = hires_time_now(); +var div = growl.div; +if (now - growl.start <= 0.5) { +div.setOpacity( tweenFrame(0.0, 1.0, (now - growl.start) * 2, 'EaseOut', 'Quadratic') ); +} +else if (now - growl.start <= this.lifetime) { +if (!growl._fully_opaque) { +div.setOpacity( 1.0 ); +growl._fully_opaque = true; +} +} +else if (now - growl.start <= this.lifetime + 1.0) { +div.setOpacity( tweenFrame(1.0, 0.0, (now - growl.start) - this.lifetime, 'EaseOut', 'Quadratic') ); +} +else { +delete_object(this.growls, { id: growl.id }); +$('d_growl_wrapper').removeChild( div ); +return; +} +var self = this; +setTimeout( function() { self.animate(growl); }, 33 ); +}, +handle_resize: function() { +var div = $('d_growl_wrapper'); +if (this.growls.length) { +var size = getInnerWindowSize(); +div.style.top = '' + (10 + this.marginTop) + 'px'; +div.style.left = '' + Math.floor((size.width - 310) - this.marginRight) + 'px'; +} +else { +div.style.left = '-2000px'; +} +} +} ); +window.$GR = new GrowlManager(); +if (window.addEventListener) { +window.addEventListener( "resize", function() { +$GR.handle_resize(); +}, false ); +} +else if (window.attachEvent && !ie6) { +window.attachEvent("onresize", function() { +$GR.handle_resize(); +}); +} +Class.create( 'Effect.Page', { +ID: '', +data: null, +active: false, +__construct: function(config) { +if (!config) return; +this.data = {}; +if (!config) config = {}; +for (var key in config) this[key] = config[key]; +this.div = $('page_' + this.ID); +assert(this.div, "Cannot find page div: page_" + this.ID); +}, +onInit: function() { +}, +onActivate: function() { +return true; +}, +onDeactivate: function() { +return true; +}, +show: function() { +this.div.show(); +}, +hide: function() { +this.div.hide(); +}, +gosub: function(anchor) { +} +} ); +Class.require( 'Effect.Page' ); +Class.create( 'Effect.PageManager', { +pages: null, +current_page_id: '', +on_demand: {}, +__construct: function(page_list) { +this.pages = []; +this.page_list = page_list; +for (var idx = 0, len = page_list.length; idx < len; idx++) { +Debug.trace( 'page', "Initializing page: " + page_list[idx].ID ); +if (Effect.Page[ page_list[idx].ID ]) { +var page = new Effect.Page[ page_list[idx].ID ]( page_list[idx] ); +page.onInit(); +this.pages.push(page); +} +else { +Debug.trace( 'page', 'Page ' + page_list[idx].ID + ' will be loaded on-demand' ); +} +} +}, +find: function(id) { +var page = find_object( this.pages, { ID: id } ); +if (!page) Debug.trace('PageManager', "Could not find page: " + id); +return page; +}, +notify_load: function(file, id) { +for (var idx = 0, len = this.page_list.length; idx < len; idx++) { +var page_config = this.page_list[idx]; +if (page_config.File == file) { +Debug.trace( 'page', "Initializing page on-demand: " + page_config.ID ); +var page = new Effect.Page[ page_config.ID ]( page_config ); +page.onInit(); +this.pages.push(page); +} +} +var self = this; +setTimeout( function() { +var result = self.activate(id, self.temp_args); +delete self.temp_args; +$('d_page_loading').hide(); +if (!result) { +$('page_'+id).hide(); +self.current_page_id = ''; +} +}, 1 ); +}, +activate: function(id, args) { +if (!find_object( this.pages, { ID: id } )) { +var page_config = find_object( this.page_list, { ID: id } ); +assert(!!page_config, "Page config not found: " + id ); +Debug.trace('page', "Loading file on-demand: " + page_config.File + " for page: " + id); +var url = '/effect/api/load_page/' + page_config.File + '?onafter=' + escape('page_manager.notify_load(\''+page_config.File+'\',\''+id+'\')'); +if (page_config.Requires) { +var files = page_config.Requires.split(/\,\s*/); +for (var idx = 0, len = files.length; idx < len; idx++) { +var filename = files[idx]; +if (!this.on_demand[filename]) { +Debug.trace('page', "Also loading file: " + filename); +url += '&file=' + filename; +this.on_demand[filename] = 1; +} +} +} +$('d_page_loading').show(); +this.temp_args = args; +load_script( url ); +return true; +} +$('page_'+id).show(); +var page = this.find(id); +page.active = true; +if (!args) args = []; +if (!isa_array(args)) args = [ args ]; +var result = page.onActivate.apply(page, args); +if (typeof(result) == 'boolean') return result; +else return alert("Page " + id + " onActivate did not return a boolean!"); +}, +deactivate: function(id, new_id) { +var page = this.find(id); +var result = page.onDeactivate(new_id); +if (result) { +$('page_'+id).hide(); +page.active = false; +} +return result; +}, +click: function(id, args) { +Debug.trace('page', "Switching pages to: " + id); +var old_id = this.current_page_id; +if (this.current_page_id) { +var result = this.deactivate( this.current_page_id, id ); +if (!result) return false; +} +this.current_page_id = id; +this.old_page_id = old_id; +window.scrollTo( 0, 0 ); +var result = this.activate(id, args); +if (!result) { +$('page_'+id).hide(); +this.current_page_id = ''; +} +return true; +} +} ); +Class.subclass( Effect.Page, "Effect.Page.Main", { +inited: false, +onActivate: function() { +Nav.bar( ['Main', 'EffectGames.com'] ); +Nav.title(''); +$('d_blog_news').innerHTML = loading_image(); +$('d_blog_community').innerHTML = loading_image(); +$('d_blog_featured').innerHTML = loading_image(); +Blog.search({ +stag: 'featured_game', +limit: 4, +full: 1, +callback: [this, 'receive_featured_games'] +}); +effect_api_get( 'get_site_info', { cat: 'pop_pub_games' }, [this, 'receive_pop_pub_games'], { } ); +Blog.search({ +stag: 'front_page', +limit: 5, +target: 'd_blog_news', +more: 1 +}); +Blog.search({ +path: '/community', +limit: 5, +target: 'd_blog_community', +more: 1 +}); +if (!this.inited) { +this.inited = true; +config.Strings.MainSlideshow.Slide = always_array( config.Strings.MainSlideshow.Slide ); +this.slide_idx = 0; +this.num_slides = config.Strings.MainSlideshow.Slide.length; +this.slide_div_num = 0; +this.slide_dir = 1; +this.bk_pos = -340; +this.bk_pos_target = -340; +this.slide_images = []; +for (var idx = 0, len = this.num_slides; idx < len; idx++) { +var url = images_uri + '/' + config.Strings.MainSlideshow.Slide[idx].Photo; +this.slide_images[idx] = new Image(); +this.slide_images[idx].src = png(url, true); +} +} +this.height_target = 470; +this.height_start = $('d_header').offsetHeight; +this.time_start = hires_time_now(); +this.duration = 0.75; +if (!this.timer) this.timer = setTimeout( '$P("Main").animate_mhs()', 33 ); +if (session.user) $('d_blurb_main').hide(); +else { +$('d_blurb_main').innerHTML = get_string('/Main/Blurb'); +$('d_blurb_main').show(); +} +return true; +}, +receive_pop_pub_games: function(response, tx) { +var html = ''; +if (response.Data && response.Data.Games && response.Data.Games.Game) { +var games = always_array( response.Data.Games.Game ); +for (var idx = 0, len = Math.min(games.length, 16); idx < len; idx++) { +var game = games[idx]; +html += '
' + +(game.Logo ? +user_image_thumbnail(game.Logo, 80, 60) : +'' +) + '
' + ww_fit_box(game.Title, 80, 2, session.em_width, 1) + '
'; +} +html += '
'; +} +else { +html += 'No active public games found! Why not create a new one?'; +} +$('d_main_pop_pub_games').innerHTML = html; +}, +receive_featured_games: function(response, tx) { +var html = ''; +if (response.Rows && response.Rows.Row) { +html += ''; +var rows = always_array( response.Rows.Row ); +for (var idx = 0, len = rows.length; idx < len; idx++) { +var row = rows[idx]; +var image_url = row.Params.featured_image; +if (image_url && image_url.match(/^(\w+)\/(\w+\.\w+)$/)) { +image_url = '/effect/api/view/users/' + RegExp.$1 + '/images/' + RegExp.$2; +} +if (idx % 2 == 0) html += ''; +html += ''; +if (idx % 2 == 1) html += ''; +} +if (rows.length % 2 == 1) { +html += ''; +html += ''; +} +html += '
'; +html += ''; +html += ''; +html += ''; +html += ''; +html += ''; +html += '
'; +html += ''; +html += '' + spacer(10,1) + ''; +html += ''; +html += ''; +html += '' + spacer(15,1) + '
'; +html += spacer(1,20); +html += '
'; +} +$('d_blog_featured').innerHTML = html; +}, +animate_mhs: function() { +var now = hires_time_now(); +if (now - this.time_start >= this.duration) { +$('d_header').style.height = '' + this.height_target + 'px'; +$('d_shadow').style.height = '' + this.height_target + 'px'; +delete this.timer; +} +else { +var height = tweenFrame(this.height_start, this.height_target, (now - this.time_start) / this.duration, 'EaseOut', 'Circular'); +$('d_header').style.height = '' + height + 'px'; +$('d_shadow').style.height = '' + height + 'px'; +this.timer = setTimeout( '$P("Main").animate_mhs()', 33 ); +} +}, +onDeactivate: function() { +$('d_blog_news').innerHTML = ''; +$('d_blog_community').innerHTML = ''; +this.height_target = 75; +this.height_start = $('d_header').offsetHeight; +this.time_start = hires_time_now(); +if (!this.timer) this.timer = setTimeout( '$P("Main").animate_mhs()', 33 ); +return true; +}, +draw_slide: function() { +if (this.slide_timer) return; +var slide = config.Strings.MainSlideshow.Slide[ this.slide_idx ]; +this.old_photo = $('d_header_slideshow_photo_' + this.slide_div_num); +this.old_text = $('d_header_slideshow_text_' + this.slide_div_num); +this.slide_div_num = 1 - this.slide_div_num; +this.new_photo = $('d_header_slideshow_photo_' + this.slide_div_num); +this.new_text = $('d_header_slideshow_text_' + this.slide_div_num); +this.new_photo.style.backgroundImage = 'url('+png(images_uri+'/'+slide.Photo, true)+')'; +this.new_photo.setOpacity(0.0); +var html = ''; +html += slide.Text; +this.slide_width = this.new_text.offsetWidth; +this.new_text.innerHTML = html; +if (this.slide_dir == 1) this.new_text.style.left = '' + this.slide_width + 'px'; +else this.new_text.style.left = '-' + this.slide_width + 'px'; +this.slide_time_start = hires_time_now(); +this.slide_timer = setTimeout( '$P("Main").animate_mhs_slide()', 33 ); +}, +animate_mhs_slide: function() { +var now = hires_time_now(); +if (now - this.slide_time_start >= this.duration) { +this.new_text.style.left = '0px'; +this.old_text.style.left = '-' + this.slide_width + 'px'; +this.new_photo.setOpacity( 1.0 ); +this.old_photo.setOpacity( 0.0 ); +delete this.slide_timer; +this.bk_pos = this.bk_pos_target; +} +else { +var value = tweenFrame(0.0, 1.0, (now - this.slide_time_start) / this.duration, 'EaseOut', 'Circular'); +if (this.slide_dir == 1) { +this.new_text.style.left = '' + Math.floor( this.slide_width - (this.slide_width * value) ) + 'px'; +this.old_text.style.left = '-' + Math.floor( this.slide_width * value ) + 'px'; +} +else { +this.new_text.style.left = '-' + Math.floor( this.slide_width - (this.slide_width * value) ) + 'px'; +this.old_text.style.left = '' + Math.floor( this.slide_width * value ) + 'px'; +} +this.new_photo.setOpacity( value ); +this.old_photo.setOpacity( 1.0 - value ); +var bkp = Math.floor( this.bk_pos + ((this.bk_pos_target - this.bk_pos) * value) ); +$('d_header').style.backgroundPosition = '' + bkp + 'px 0px'; +this.slide_timer = setTimeout( '$P("Main").animate_mhs_slide()', 33 ); +} +}, +prev_slide: function() { +this.bk_pos_target += 200; +this.slide_idx--; +if (this.slide_idx < 0) this.slide_idx += this.num_slides; +this.slide_dir = -1; +this.draw_slide(); +}, +next_slide: function() { +this.bk_pos_target -= 200; +this.slide_idx++; +if (this.slide_idx >= this.num_slides) this.slide_idx -= this.num_slides; +this.slide_dir = 1; +this.draw_slide(); +} +} ); +Class.subclass( Effect.Page, "Effect.Page.PublicGameList", { +onActivate: function() { +Nav.bar( +['Main', 'EffectGames.com'], +['PublicGameList', "All Public Games"] +); +Nav.title( "List of All Public Game Projects" ); +effect_api_get( 'get_site_info', { cat: 'all_pub_games' }, [this, 'receive_all_pub_games'], { } ); +this.div.innerHTML = loading_image(); +return true; +}, +onDeactivate: function() { +this.div.innerHTML = ''; +return true; +}, +receive_all_pub_games: function(response, tx) { +var html = ''; +html += '

List of All Public Game Projects

'; +html += '
This is the complete list of public games currently being built by our users, presented in alphabetical order. Maybe they could use some help! Check out the game project pages and see (requires user account).
'; +if (response.Data && response.Data.Games && response.Data.Games.Game) { +var games = always_array( response.Data.Games.Game ); +for (var idx = 0, len = games.length; idx < len; idx++) { +var game = games[idx]; +html += '
' + +(game.Logo ? +user_image_thumbnail(game.Logo, 80, 60) : +'' +) + '
' + ww_fit_box(game.Title, 80, 2, session.em_width, 1) + '
'; +} +html += '
'; +} +else { +html += 'No public games found! Why not create a new one?'; +} +this.div.innerHTML = html; +} +} ); +Class.subclass( Effect.Page, "Effect.Page.Search", { +onActivate: function(args) { +if (!args) args = {}; +var search_text = args.q; +var start = args.s || 0; +if (!start) start = 0; +var title = 'Search results for "'+search_text+'"'; +Nav.bar( +['Main', 'EffectGames.com'], +['Search?q=' + escape(search_text), "Search Results"] +); +Nav.title( title ); +this.last_search_text = search_text; +$('d_article_search').innerHTML = loading_image(); +load_script( 'http://www.google.com/uds/GwebSearch?callback=receive_google_search_results&context=0&lstkp=0&rsz=large&hl=en&source=gsc&gss=.com&sig=&q='+escape(search_text)+'%20site%3Ahttp%3A%2F%2Fwww.effectgames.com%2F&key=notsupplied&v=1.0&start='+start+'&nocache=' + (new Date()).getTime() ); +$('h_article_search').innerHTML = title; +return true; +}, +onDeactivate: function(new_page) { +$('fe_search_bar').value = ''; +$('d_article_search').innerHTML = ''; +return true; +} +} ); +function do_search_bar() { +var search_text = $('fe_search_bar').value; +if (search_text.length) { +Nav.go('Search?q=' + escape(search_text)); +} +} +function receive_google_search_results(context, response) { +var html = ''; +html += '
Powered by
'; +if (response.results.length) { +for (var idx = 0, len = response.results.length; idx < len; idx++) { +var row = response.results[idx]; +var url = row.unescapedUrl.replace(/^.+article\.psp\.html/, '#Article'); +html += '
'; +html += ''; +html += '
' + row.content + '
'; +html += '
'; +} +} +else { +html += 'No results found.'; +} +if (response.cursor.pages) { +html += '
Page: '; +for (var idx = 0, len = response.cursor.pages.length; idx < len; idx++) { +html += ''; +var page = response.cursor.pages[idx]; +var url = '#Search?q=' + escape($P('Search').last_search_text) + '&s=' + page.start; +if (response.cursor.currentPageIndex != idx) html += ''; +else html += ''; +html += page.label; +if (response.cursor.currentPageIndex != idx) html += ''; +else html += ''; +html += ''; +} +html += '
'; +} +$('d_article_search').innerHTML = html; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/index.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/index.js new file mode 100644 index 0000000..8b164a4 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/index.js @@ -0,0 +1 @@ +exports.ZeParser = require('./ZeParser').ZeParser; diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/package.json b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/package.json new file mode 100644 index 0000000..1aad0e2 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/package.json @@ -0,0 +1,31 @@ +{ + "author": { + "name": "Peter van der Zee", + "url": "http://qfox.nl/" + }, + "name": "zeparser", + "description": "My JavaScript parser", + "version": "0.0.5", + "homepage": "https://github.com/qfox/ZeParser/", + "repository": { + "type": "git", + "url": "git://github.com/qfox/ZeParser.git" + }, + "main": "./index", + "engines": { + "node": "*" + }, + "dependencies": {}, + "devDependencies": {}, + "readme": "This is a JavaScript parser.\nhttp://github.com/qfox/ZeParser\n(c) Peter van der Zee\nhttp://qfox.nl\n\n\nBenchmark\nhttp://qfox.github.com/ZeParser/benchmark.html\n\nThe Tokenizer is used by the parser. The parser tells the tokenizer whether the next token may be a regular expression or not. Without the parser, the tokenizer will fail if regular expression literals are used in the input.\n\nUsage:\nZeParser.parse(input);\n\nReturns a \"parse tree\" which is a tree of an array of arrays with tokens (regular objects) as leafs. Meta information embedded as properties (of the arrays and the tokens).\n\nZeParser.createParser(input);\n\nReturns a new ZeParser instance which has already parsed the input. Amongst others, the ZeParser instance will have the properties .tree, .wtree and .btree.\n\n.tree is the parse tree mentioned above.\n.wtree (\"white\" tree) is a regular array with all the tokens encountered (including whitespace, line terminators and comments)\n.btree (\"black\" tree) is just like .wtree but without the whitespace, line terminators and comments. This is what the specification would call the \"token stream\".\n\nI'm aware that the naming convention is a bit awkward. It's a tradeoff between short and descriptive. The streams are used quite often in the analysis.\n\nTokens are regular objects with several properties. Amongst them are .tokposw and .tokposw, they correspond with their own position in the .wtree and .btree.\n\nThe parser has two modes for parsing: simple and extended. Simple mode is mainly for just parsing and returning the streams and a simple parse tree. There's not so much meta information here and this mode is mainly built for speed. The other mode has everything required for Zeon to do its job. This mode is toggled by the instance property .ast, which is true by default :)\n\nNon-factory example:\n\nvar input = \"foo\";\nvar tree = []; // this should probably be refactored away some day\nvar tokenizer = new Tokenizer(input); // dito\nvar parser = new ZeParser(input, tokenizer, tree);\nparser.parse(); // returns tree..., should never throw errors\n", + "readmeFilename": "README", + "bugs": { + "url": "https://github.com/qfox/ZeParser/issues" + }, + "_id": "zeparser@0.0.5", + "dist": { + "shasum": "b6932cb5a7e6391618033bf7da107187d9b825c4" + }, + "_from": "zeparser@0.0.5", + "_resolved": "https://registry.npmjs.org/zeparser/-/zeparser-0.0.5.tgz" +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/test-parser.html b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/test-parser.html new file mode 100755 index 0000000..1ff5ff4 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/test-parser.html @@ -0,0 +1,26 @@ + + + + Parser Test Suite Page + + + + (c) qfox.nl
+ Parser test suite
+
Running...
+ + + + + + + \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/test-tokenizer.html b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/test-tokenizer.html new file mode 100755 index 0000000..0e0d1b1 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/test-tokenizer.html @@ -0,0 +1,23 @@ + + + + Tokenizer Test Suite Page + + + + (c) qfox.nl
+ + + + + + \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/tests.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/tests.js new file mode 100644 index 0000000..8a4138b --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/tests.js @@ -0,0 +1,478 @@ +// tests for both the tokenizer and parser. Parser test results could be checked tighter. +// api: [input, token-output-count, ?regex-hints, desc] +// regex-hints are for tokenizer, will tell for each token whether it might parse regex or not (parser's job) +var Tests = [ + +["var abc;", 4, "Variable Declaration"], +["var abc = 5;", 8, "Variable Declaration, Assignment"], +["/* */", 1, "Block Comment"], +["/** **/", 1, "JSDoc-style Comment"], +["var f = function(){;};", 13, "Assignment, Function Expression"], +["hi; // moo", 4, "Trailing Line Comment"], +["hi; // moo\n;", 6, "Trailing Line Comment, Linefeed, `;`"], +["var varwithfunction;", 4, "Variable Declaration, Identifier Containing Reserved Words, `;`"], +["a + b;", 6, "Addition/Concatenation"], + +["'a'", 1, "Single-Quoted String"], +["'a';", 2, "Single-Quoted String, `;`"], // Taken from the parser test suite. + +["'a\\n'", 1, "Single-Quoted String With Escaped Linefeed"], +["'a\\n';", 2, "Single-Quoted String With Escaped Linefeed, `;`"], // Taken from the parser test suite. + +["\"a\"", 1, "Double-Quoted String"], +["\"a\";", 2, "Double-Quoted String, `;`"], // Taken from the parser test suite. + +["\"a\\n\"", 1, "Double-Quoted String With Escaped Linefeed"], +["\"a\\n\";", 2, "Double-Quoted String With Escaped Linefeed, `;`"], // Taken from the parser test suite. + +["500", 1, "Integer"], +["500;", 2, "Integer, `;`"], // Taken from the parser test suite. + +["500.", 1, "Double With Trailing Decimal Point"], +["500.;", 2, "Double With Trailing Decimal Point"], // Taken from the parser test suite. + +["500.432", 1, "Double With Decimal Component"], +["500.432;", 2, "Double With Decimal Component, `;`"], // Taken from the parser test suite. + +[".432432", 1, "Number, 0 < Double < 1"], +[".432432;", 2, "Number, 0 < Double < 1, `;`"], // Taken from the parser test suite. + +["(a,b,c)", 7, "Parentheses, Comma-separated identifiers"], +["(a,b,c);", 8, "Parentheses, Comma-separated identifiers, `;`"], // Taken from the parser test suite. + +["[1,2,abc]", 7, "Array literal"], +["[1,2,abc];", 8, "Array literal, `;`"], // Taken from the parser test suite. + +["{a:1,\"b\":2,c:c}", 13, "Object literal"], +["var o = {a:1,\"b\":2,c:c};", 20, "Assignment, Object Literal, `;`"], // Taken from the parser test suite. + +["var x;\nvar y;", 9, "2 Variable Declarations, Multiple lines"], +["var x;\nfunction n(){ }", 13, "Variable, Linefeed, Function Declaration"], +["var x;\nfunction n(abc){ }", 14, "Variable, Linefeed, Function Declaration With One Argument"], +["var x;\nfunction n(abc, def){ }", 17, "Variable, Linefeed, Function Declaration With Multiple Arguments"], +["function n(){ \"hello\"; }", 11, "Function Declaration, Body"], + +["/a/;", 2, [true, false], "RegExp Literal, `;`"], +["/a/b;", 2, [true, true], "RegExp Literal, Flags, `;`"], +["++x;", 3, "Unary Increment, Prefix, `;`"], +[" / /;", 3, [true, true, false], "RegExp, Leading Whitespace, `;`"], +["/ / / / /", 5, [true, false, false, false, true], "RegExp Containing One Space, Space, Division, Space, RegExp Containing One Space"], + +// Taken from the parser test suite. + +["\"var\";", 2, "Keyword String, `;`"], +["\"variable\";", 2, "String Beginning With Keyword, `;`"], +["\"somevariable\";", 2, "String Containing Keyword, `;`"], +["\"somevar\";", 2, "String Ending With Keyword, `;`"], + +["var varwithfunction;", 4, "Keywords should not be matched in identifiers"], + +["var o = {a:1};", 12, "Object Literal With Unquoted Property"], +["var o = {\"b\":2};", 12, "Object Literal With Quoted Property"], +["var o = {c:c};", 12, "Object Literal With Equivalent Property Name and Identifier"], + +["/a/ / /b/;", 6, [true, true, false, false, true, false], "RegExp, Division, RegExp, `;`"], +["a/b/c;", 6, "Triple Division (Identifier / Identifier / Identifier)"], + +["+function(){/regex/;};", 9, [false, false, false, false, false, true, false, false, false], "Unary `+` Operator, Function Expression Containing RegExp and Semicolon, `;`"], + +// Line Terminators. +["\r\n", 1, "CRLF Line Ending = 1 Linefeed"], +["\r", 1, "CR Line Ending = 1 Linefeed"], +["\n", 1, "LF Line Ending = 1 Linefeed"], +["\r\n\n\u2028\u2029\r", 5, "Various Line Terminators"], + +// Whitespace. +["a \t\u000b\u000c\u00a0\uFFFFb", 8, "Whitespace"], + +// Comments. +["//foo!@#^&$1234\nbar;", 4, "Line Comment, Linefeed, Identifier, `;`"], +["/* abcd!@#@$* { } && null*/;", 2, "Single-Line Block Comment, `;`"], +["/*foo\nbar*/;", 2, "Multi-Line Block Comment, `;`"], +["/*x*x*/;", 2, "Block Comment With Asterisks, `;`"], +["/**/;", 2, "Empty Comment, `;`"], + +// Identifiers. +["x;", 2, "Single-Character Identifier, `;`"], +["_x;", 2, "Identifier With Leading `_`, `;`"], +["xyz;", 2, "Identifier With Letters Only, `;`"], +["$x;", 2, "Identifier With Leading `$`, `;`"], +["x5;", 2, "Identifier With Number As Second Character, `;`"], +["x_y;", 2, "Identifier Containing `_`, `;`"], +["x+5;", 4, "Identifier, Binary `+` Operator, Identifier, `;`"], +["xyz123;", 2, "Alphanumeric Identifier, `;`"], +["x1y1z1;", 2, "Alternating Alphanumeric Identifier, `;`"], +["foo\\u00d8bar;", 2, "Identifier With Unicode Escape Sequence (`\\uXXXX`), `;`"], +["f\u00d8\u00d8bar;", 2, "Identifier With Embedded Unicode Character"], + +// Numbers. +["5;", 2, "Integer, `;`"], +["5.5;", 2, "Double, `;`"], +["0;", 2, "Integer Zero, `;`"], +["0.0;", 2, "Double Zero, `;`"], +["0.001;", 2, "0 < Decimalized Double < 1, `;`"], +["1.e2;", 2, "Integer With Decimal and Exponential Component (`e`), `;`"], +["1.e-2;", 2, "Integer With Decimal and Negative Exponential Component, `;`"], +["1.E2;", 2, "Integer With Decimal and Uppercase Exponential Component (`E`), `;`"], +["1.E-2;", 2, "Integer With Decimal and Uppercase Negative Exponential Component, `;`"], +[".5;", 2, "0 < Double < 1, `;`"], +[".5e3;", 2, "(0 < Double < 1) With Exponential Component"], +[".5e-3;", 2, "(0 < Double < 1) With Negative Exponential Component"], +["0.5e3;", 2, "(0 < Decimalized Double < 1) With Exponential Component"], +["55;", 2, "Two-Digit Integer, `;`"], +["123;", 2, "Three-Digit Integer, `;`"], +["55.55;", 2, "Two-Digit Double, `;`"], +["55.55e10;", 2, "Two-Digit Double With Exponential Component, `;`"], +["123.456;", 2, "Three-Digit Double, `;`"], +["1+e;", 4, "Additive Expression, `;`"], +["0x01;", 2, "Hexadecimal `1` With 1 Leading Zero, `;`"], +["0xcafe;", 2, "Hexadecimal `51966`, `;`"], +["0x12345678;", 2, "Hexadecimal `305419896`, `;`"], +["0x1234ABCD;", 2, "Hexadecimal `305441741` With Uppercase Letters, `;`"], +["0x0001;", 2, "Hexadecimal `1` with 3 Leading Zeros, `;`"], + +// Strings. +["\"foo\";", 2, "Multi-Character Double-Quoted String, `;`"], +["\"a\\n\";", 2, "Double-Quoted String Containing Linefeed, `;`"], +["\'foo\';", 2, "Single-Quoted String, `;`"], +["'a\\n';", 2, "Single-Quoted String Containing Linefeed, `;`"], +["\"x\";", 2, "Single-Character Double-Quoted String, `;`"], +["'';", 2, "Empty Single-Quoted String, `;`"], +["\"foo\\tbar\";", 2, "Double-Quoted String With Tab Character, `;`"], +["\"!@#$%^&*()_+{}[]\";", 2, "Double-Quoted String Containing Punctuators, `;`"], +["\"/*test*/\";", 2, "Double-Quoted String Containing Block Comment, `;`"], +["\"//test\";", 2, "Double-Quoted String Containing Line Comment, `;`"], +["\"\\\\\";", 2, "Double-Quoted String Containing Reverse Solidus, `;`"], +["\"\\u0001\";", 2, "Double-Quoted String Containing Numeric Unicode Escape Sequence, `;`"], +["\"\\uFEFF\";", 2, "Double-Quoted String Containing Alphanumeric Unicode Escape Sequence, `;`"], +["\"\\u10002\";", 2, "Double-Quoted String Containing 5-Digit Unicode Escape Sequence, `;`"], +["\"\\x55\";", 2, "Double-Quoted String Containing Hex Escape Sequence, `;`"], +["\"\\x55a\";", 2, "Double-Quoted String Containing Hex Escape Sequence and Additional Character, `;`"], +["\"a\\\\nb\";", 2, "Double-Quoted String Containing Escaped Linefeed, `;`"], +["\";\"", 1, "Double-Quoted String Containing `;`"], +["\"a\\\nb\";", 2, "Double-Quoted String Containing Reverse Solidus and Linefeed, `;`"], +["'\\\\'+ ''", 4, "Single-Quoted String Containing Reverse Solidus, `+`, Empty Single-Quoted String"], + +// `null`, `true`, and `false`. +["null;", 2, "`null`, `;`"], +["true;", 2, "`true`, `;`"], +["false;", 2, "`false`, `;`"], + +// RegExps +["/a/;", 2, [true, true], "Single-Character RegExp, `;`"], +["/abc/;", 2, [true, true], "Multi-Character RegExp, `;`"], +["/abc[a-z]*def/g;", 2, [true, true], "RegExp Containing Character Range and Quantifier, `;`"], +["/\\b/;", 2, [true, true], "RegExp Containing Control Character, `;`"], +["/[a-zA-Z]/;", 2, [true, true], "RegExp Containing Extended Character Range, `;`"], +["/foo(.*)/g;", 2, [true, false], "RegExp Containing Capturing Group and Quantifier, `;`"], + +// Array Literals. +["[];", 3, "Empty Array, `;`"], +["[\b\n\f\r\t\x20];", 9, "Array Containing Whitespace, `;`"], +["[1];", 4, "Array Containing 1 Element, `;`"], +["[1,2];", 6, "Array Containing 2 Elements, `;`"], +["[1,2,,];", 8, "Array Containing 2 Elisions, `;`"], +["[1,2,3];", 8, "Array Containing 3 Elements, `;`"], +["[1,2,3,,,];", 11, "Array Containing 3 Elisions, `;`"], + +// Object Literals. +["({x:5});", 8, "Object Literal Containing 1 Member; `;`"], +["({x:5,y:6});", 12, "Object Literal Containing 2 Members, `;`"], +["({x:5,});", 9, "Object Literal Containing 1 Member and Trailing Comma, `;`"], +["({if:5});", 8, "Object Literal Containing Reserved Word Property Name, `;`"], +["({ get x() {42;} });", 17, "Object Literal Containing Getter, `;`"], +["({ set y(a) {1;} });", 18, "Object Literal Containing Setter, `;`"], + +// Member Expressions. +["o.m;", 4, "Dot Member Accessor, `;`"], +["o['m'];", 5, "Square Bracket Member Accessor, `;`"], +["o['n']['m'];", 8, "Nested Square Bracket Member Accessor, `;`"], +["o.n.m;", 6, "Nested Dot Member Accessor, `;`"], +["o.if;", 4, "Dot Reserved Property Name Accessor, `;`"], + +// Function Calls. +["f();", 4, "Function Call Operator, `;`"], +["f(x);", 5, "Function Call Operator With 1 Argument, `;`"], +["f(x,y);", 7, "Function Call Operator With Multiple Arguments, `;`"], +["o.m();", 6, "Dot Member Accessor, Function Call, `;`"], +["o['m']();", 7, "Square Bracket Member Accessor, Function Call, `;`"], +["o.m(x);", 7, "Dot Member Accessor, Function Call With 1 Argument, `;`"], +["o['m'](x);", 8, "Square Bracket Member Accessor, Function Call With 1 Argument, `;`"], +["o.m(x,y);", 9, "Dot Member Accessor, Function Call With 2 Arguments, `;`"], +["o['m'](x,y);", 10, "Square Bracket Member Accessor, Function Call With 2 Arguments, `;`"], +["f(x)(y);", 8, "Nested Function Call With 1 Argument Each, `;`"], +["f().x;", 6, "Function Call, Dot Member Accessor, `;`"], + +// `eval` Function. +["eval('x');", 5, "`eval` Invocation With 1 Argument, `;`"], +["(eval)('x');", 7, "Direct `eval` Call Example, `;`"], +["(1,eval)('x');", 9, "Indirect `eval` Call Example, `;`"], +["eval(x,y);", 7, "`eval` Invocation With 2 Arguments, `;`"], + +// `new` Operator. +["new f();", 6, "`new` Operator, Function Call, `;`"], +["new o;", 4, "`new` Operator, Identifier, `;`"], +["new o.m;", 6, "`new` Operator, Dot Member Accessor, `;`"], +["new o.m(x);", 9, "`new` Operator, Dot Member Accessor, Function Call With 1 Argument, `;`"], +["new o.m(x,y);", 11, "``new` Operator, Dot Member Accessor, Function Call With 2 Arguments , `;`"], + +// Prefix and Postfix Increment. +["++x;", 3, "Prefix Increment, Identifier, `;`"], +["x++;", 3, "Identifier, Postfix Increment, `;`"], +["--x;", 3, "Prefix Decrement, Identifier, `;`"], +["x--;", 3, "Postfix Decrement, Identifier, `;`"], +["x ++;", 4, "Identifier, Space, Postfix Increment, `;`"], +["x /* comment */ ++;", 6, "Identifier, Block Comment, Postfix Increment, `;`"], +["++ /* comment */ x;", 6, "Prefix Increment, Block Comment, Identifier, `;`"], + +// Unary Operators. +["delete x;", 4, "`delete` Operator, Space, Identifier, `;`"], +["void x;", 4, "`void` Operator, Space, Identifier, `;`"], +["typeof x;", 4, "`typeof` Operator, Space, Identifier, `;`"], +["+x;", 3, "Unary `+` Operator, Identifier, `;`"], +["-x;", 3, "Unary Negation Operator, Identifier, `;`"], +["~x;", 3, "Bitwise NOT Operator, Identifier, `;`"], +["!x;", 3, "Logical NOT Operator, Identifier, `;`"], + +// Comma Operator. +["x, y;", 5, "Comma Operator"], + +// Miscellaneous. +["new Date++;", 5, "`new` Operator, Identifier, Postfix Increment, `;`"], +["+x++;", 4, "Unary `+`, Identifier, Postfix Increment, `;`"], + +// Expressions. +["1 * 2;", 6, "Integer, Multiplication, Integer, `;`"], +["1 / 2;", 6, "Integer, Division, Integer, `;`"], +["1 % 2;", 6, "Integer, Modulus, Integer, `;`"], +["1 + 2;", 6, "Integer, Addition, Integer, `;`"], +["1 - 2;", 6, "Integer, Subtraction, Integer, `;`"], +["1 << 2;", 6, "Integer, Bitwise Left Shift, Integer, `;`"], +["1 >>> 2;", 6, "Integer, Bitwise Zero-fill Right Shift, Integer, `;`"], +["1 >> 2;", 6, "Integer, Bitwise Sign-Propagating Right Shift, Integer, `;`"], +["1 * 2 + 3;", 10, "Order-of-Operations Expression, `;`"], +["(1+2)*3;", 8, "Parenthesized Additive Expression, Multiplication, `;`"], +["1*(2+3);", 8, "Multiplication, Parenthesized Additive Expression, `;`"], +["xy;", 4, "Greater-Than Relational Operator, `;`"], +["x<=y;", 4, "Less-Than-or-Equal-To Relational Operator, `;`"], +["x>=y;", 4, "Greater-Than-or-Equal-To Relational Operator, `;`"], +["x instanceof y;", 6, "`instanceof` Operator, `;`"], +["x in y;", 6, "`in` Operator, `;`"], +["x&y;", 4, "Bitwise AND Operator, `;`"], +["x^y;", 4, "Bitwise XOR Operator, `;`"], +["x|y;", 4, "Bitwise OR Operator, `;`"], +["x+y>>= y;", 6, "Bitwise Zero-Fill Right Shift Assignment, `;`"], +["x <<= y;", 6, "Bitwise Left Shift Assignment, `;`"], +["x += y;", 6, "Additive Assignment, `;`"], +["x -= y;", 6, "Subtractive Assignment, `;`"], +["x *= y;", 6, "Multiplicative Assignment, `;`"], +["x /= y;", 6, "Divisive Assignment, `;`"], +["x %= y;", 6, "Modulus Assignment, `;`"], +["x >>= y;", 6, "Bitwise Sign-Propagating Right Shift Assignment, `;`"], +["x &= y;", 6, "Bitwise AND Assignment, `;`"], +["x ^= y;", 6, "Bitwise XOR Assignment, `;`"], +["x |= y;", 6, "Bitwise OR Assignment, `;`"], + +// Blocks. +["{};", 3, "Empty Block, `;`"], +["{x;};", 5, "Block Containing 1 Identifier, `;`"], +["{x;y;};", 7, "Block Containing 2 Identifiers, `;`"], + +// Variable Declarations. +["var abc;", 4, "Variable Declaration"], +["var x,y;", 6, "Comma-Separated Variable Declarations, `;`"], +["var x=1,y=2;", 10, "Comma-Separated Variable Initializations, `;`"], +["var x,y=2;", 8, "Variable Declaration, Variable Initialization, `;`"], + +// Empty Statements. +[";", 1, "Empty Statement"], +["\n;", 2, "Linefeed, `;`"], + +// Expression Statements. +["x;", 2, "Identifier, `;`"], +["5;", 2, "Integer, `;`"], +["1+2;", 4, "Additive Statement, `;`"], + +// `if...else` Statements. +["if (c) x; else y;", 13, "Space-Delimited `if...else` Statement"], +["if (c) x;", 8, "Space-Delimited `if` Statement, `;`"], +["if (c) {} else {};", 14, "Empty Block-Delimited `if...else` Statement"], +["if (c1) if (c2) s1; else s2;", 19, "Nested `if...else` Statement Without Dangling `else`"], + +// `while` and `do...while` Loops. +["do s; while (e);", 11, "Space-Delimited `do...while` Loop"], +["do { s; } while (e);", 15, "Block-Delimited `do...while` Loop"], +["while (e) s;", 8, "Space-Delimited `while` Loop"], +["while (e) { s; };", 13, "Block-Delimited `while` Loop"], + +// `for` and `for...in` Loops. +["for (;;) ;", 8, "Infinite Space-Delimited `for` Loop"], +["for (;c;x++) x;", 12, "`for` Loop: Empty Initialization Condition; Space-Delimited Body"], +["for (i;i foo(new window[(['Active'].concat('Object').join('X'))])\n```\n\n## License\n\nLicensed under the MIT license.\n\n[socket.io]: http://socket.io/\n", + "readmeFilename": "Readme.md", + "bugs": { + "url": "https://github.com/felixge/node-active-x-obfuscator/issues" + }, + "_id": "active-x-obfuscator@0.0.1", + "dist": { + "shasum": "e570359f5a624d15ca7ede4213398b6219c6b5f8" + }, + "_from": "active-x-obfuscator@0.0.1", + "_resolved": "https://registry.npmjs.org/active-x-obfuscator/-/active-x-obfuscator-0.0.1.tgz" +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/test.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/test.js new file mode 100644 index 0000000..e8fc807 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/test.js @@ -0,0 +1,53 @@ +var activeXObfuscator = require('./index'); +var assert = require('assert'); + +var OBFUSCATED_ACTIVE_X_OBJECT = activeXObfuscator.OBFUSCATED_ACTIVE_X_OBJECT; +var OBFUSCATED_ACTIVE_X = activeXObfuscator.OBFUSCATED_ACTIVE_X; + +var input = + "foo(new ActiveXObject('Microsoft.XMLHTTP'))"; +var expected = + "foo(new window[" + OBFUSCATED_ACTIVE_X_OBJECT + "]('Microsoft.XMLHTTP'))"; +assert.equal(activeXObfuscator(input), expected); + +var input = + "var foo = 'ActiveXObject';"; +var expected = + "var foo = " + OBFUSCATED_ACTIVE_X_OBJECT + ";"; +assert.equal(activeXObfuscator(input), expected); + +var input = + 'var foo = "ActiveXObject";'; +var expected = + "var foo = " + OBFUSCATED_ACTIVE_X_OBJECT + ";"; +assert.equal(activeXObfuscator(input), expected); + +var input = + 'var foo = o.ActiveXObject;'; +var expected = + "var foo = o[" + OBFUSCATED_ACTIVE_X_OBJECT + "];"; +assert.equal(activeXObfuscator(input), expected); + +var input = + 'var foo = "ActiveX";'; +var expected = + "var foo = " + OBFUSCATED_ACTIVE_X + ";"; +assert.equal(activeXObfuscator(input), expected); + +var input = + "var foo = 'ActiveX';"; +var expected = + "var foo = " + OBFUSCATED_ACTIVE_X + ";"; +assert.equal(activeXObfuscator(input), expected); + +var input = + "var foo; // ActiveX is cool"; +var expected = + "var foo; // Ac...eX is cool"; +assert.equal(activeXObfuscator(input), expected); + +var input = + "var foo = 'ActiveX is cool';"; +assert.throws(function() { + activeXObfuscator(input); +}, /Unknown ActiveX occurence/); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/.npmignore b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/.npmignore new file mode 100644 index 0000000..d97eaa0 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/.npmignore @@ -0,0 +1,4 @@ +.DS_Store +.tmp*~ +*.local.* +.pinf-* \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/README.html b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/README.html new file mode 100644 index 0000000..5f37ac0 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/README.html @@ -0,0 +1,981 @@ + + + + +UglifyJS – a JavaScript parser/compressor/beautifier + + + + + + + + + + + + + +
+ +
+ +
+

UglifyJS – a JavaScript parser/compressor/beautifier

+ + + + +
+

1 UglifyJS — a JavaScript parser/compressor/beautifier

+
+ + +

+This package implements a general-purpose JavaScript +parser/compressor/beautifier toolkit. It is developed on NodeJS, but it +should work on any JavaScript platform supporting the CommonJS module system +(and if your platform of choice doesn't support CommonJS, you can easily +implement it, or discard the exports.* lines from UglifyJS sources). +

+

+The tokenizer/parser generates an abstract syntax tree from JS code. You +can then traverse the AST to learn more about the code, or do various +manipulations on it. This part is implemented in parse-js.js and it's a +port to JavaScript of the excellent parse-js Common Lisp library from Marijn Haverbeke. +

+

+( See cl-uglify-js if you're looking for the Common Lisp version of +UglifyJS. ) +

+

+The second part of this package, implemented in process.js, inspects and +manipulates the AST generated by the parser to provide the following: +

+
    +
  • ability to re-generate JavaScript code from the AST. Optionally + indented—you can use this if you want to “beautify” a program that has + been compressed, so that you can inspect the source. But you can also run + our code generator to print out an AST without any whitespace, so you + achieve compression as well. + +
  • +
  • shorten variable names (usually to single characters). Our mangler will + analyze the code and generate proper variable names, depending on scope + and usage, and is smart enough to deal with globals defined elsewhere, or + with eval() calls or with{} statements. In short, if eval() or + with{} are used in some scope, then all variables in that scope and any + variables in the parent scopes will remain unmangled, and any references + to such variables remain unmangled as well. + +
  • +
  • various small optimizations that may lead to faster code but certainly + lead to smaller code. Where possible, we do the following: + +
      +
    • foo["bar"] ==> foo.bar + +
    • +
    • remove block brackets {} + +
    • +
    • join consecutive var declarations: + var a = 10; var b = 20; ==> var a=10,b=20; + +
    • +
    • resolve simple constant expressions: 1 +2 * 3 ==> 7. We only do the + replacement if the result occupies less bytes; for example 1/3 would + translate to 0.333333333333, so in this case we don't replace it. + +
    • +
    • consecutive statements in blocks are merged into a sequence; in many + cases, this leaves blocks with a single statement, so then we can remove + the block brackets. + +
    • +
    • various optimizations for IF statements: + +
        +
      • if (foo) bar(); else baz(); ==> foo?bar():baz(); +
      • +
      • if (!foo) bar(); else baz(); ==> foo?baz():bar(); +
      • +
      • if (foo) bar(); ==> foo&&bar(); +
      • +
      • if (!foo) bar(); ==> foo||bar(); +
      • +
      • if (foo) return bar(); else return baz(); ==> return foo?bar():baz(); +
      • +
      • if (foo) return bar(); else something(); ==> {if(foo)return bar();something()} + +
      • +
      + +
    • +
    • remove some unreachable code and warn about it (code that follows a + return, throw, break or continue statement, except + function/variable declarations). + +
    • +
    • act a limited version of a pre-processor (c.f. the pre-processor of + C/C++) to allow you to safely replace selected global symbols with + specified values. When combined with the optimisations above this can + make UglifyJS operate slightly more like a compilation process, in + that when certain symbols are replaced by constant values, entire code + blocks may be optimised away as unreachable. +
    • +
    + +
  • +
+ + + +
+ +
+

1.1 Unsafe transformations

+
+ + +

+The following transformations can in theory break code, although they're +probably safe in most practical cases. To enable them you need to pass the +--unsafe flag. +

+ +
+ +
+

1.1.1 Calls involving the global Array constructor

+
+ + +

+The following transformations occur: +

+ + + +
new Array(1, 2, 3, 4)  => [1,2,3,4]
+Array(a, b, c)         => [a,b,c]
+new Array(5)           => Array(5)
+new Array(a)           => Array(a)
+
+ + +

+These are all safe if the Array name isn't redefined. JavaScript does allow +one to globally redefine Array (and pretty much everything, in fact) but I +personally don't see why would anyone do that. +

+

+UglifyJS does handle the case where Array is redefined locally, or even +globally but with a function or var declaration. Therefore, in the +following cases UglifyJS doesn't touch calls or instantiations of Array: +

+ + + +
// case 1.  globally declared variable
+  var Array;
+  new Array(1, 2, 3);
+  Array(a, b);
+
+  // or (can be declared later)
+  new Array(1, 2, 3);
+  var Array;
+
+  // or (can be a function)
+  new Array(1, 2, 3);
+  function Array() { ... }
+
+// case 2.  declared in a function
+  (function(){
+    a = new Array(1, 2, 3);
+    b = Array(5, 6);
+    var Array;
+  })();
+
+  // or
+  (function(Array){
+    return Array(5, 6, 7);
+  })();
+
+  // or
+  (function(){
+    return new Array(1, 2, 3, 4);
+    function Array() { ... }
+  })();
+
+  // etc.
+
+ + +
+ +
+ +
+

1.1.2 obj.toString() ==> obj+“”

+
+ + +
+
+ +
+ +
+

1.2 Install (NPM)

+
+ + +

+UglifyJS is now available through NPM — npm install uglify-js should do +the job. +

+
+ +
+ +
+

1.3 Install latest code from GitHub

+
+ + + + + +
## clone the repository
+mkdir -p /where/you/wanna/put/it
+cd /where/you/wanna/put/it
+git clone git://github.com/mishoo/UglifyJS.git
+
+## make the module available to Node
+mkdir -p ~/.node_libraries/
+cd ~/.node_libraries/
+ln -s /where/you/wanna/put/it/UglifyJS/uglify-js.js
+
+## and if you want the CLI script too:
+mkdir -p ~/bin
+cd ~/bin
+ln -s /where/you/wanna/put/it/UglifyJS/bin/uglifyjs
+  # (then add ~/bin to your $PATH if it's not there already)
+
+ + +
+ +
+ +
+

1.4 Usage

+
+ + +

+There is a command-line tool that exposes the functionality of this library +for your shell-scripting needs: +

+ + + +
uglifyjs [ options... ] [ filename ]
+
+ + +

+filename should be the last argument and should name the file from which +to read the JavaScript code. If you don't specify it, it will read code +from STDIN. +

+

+Supported options: +

+
    +
  • -b or --beautify — output indented code; when passed, additional + options control the beautifier: + +
      +
    • -i N or --indent N — indentation level (number of spaces) + +
    • +
    • -q or --quote-keys — quote keys in literal objects (by default, + only keys that cannot be identifier names will be quotes). + +
    • +
    + +
  • +
  • --ascii — pass this argument to encode non-ASCII characters as + \uXXXX sequences. By default UglifyJS won't bother to do it and will + output Unicode characters instead. (the output is always encoded in UTF8, + but if you pass this option you'll only get ASCII). + +
  • +
  • -nm or --no-mangle — don't mangle names. + +
  • +
  • -nmf or --no-mangle-functions – in case you want to mangle variable + names, but not touch function names. + +
  • +
  • -ns or --no-squeeze — don't call ast_squeeze() (which does various + optimizations that result in smaller, less readable code). + +
  • +
  • -mt or --mangle-toplevel — mangle names in the toplevel scope too + (by default we don't do this). + +
  • +
  • --no-seqs — when ast_squeeze() is called (thus, unless you pass + --no-squeeze) it will reduce consecutive statements in blocks into a + sequence. For example, "a = 10; b = 20; foo();" will be written as + "a=10,b=20,foo();". In various occasions, this allows us to discard the + block brackets (since the block becomes a single statement). This is ON + by default because it seems safe and saves a few hundred bytes on some + libs that I tested it on, but pass --no-seqs to disable it. + +
  • +
  • --no-dead-code — by default, UglifyJS will remove code that is + obviously unreachable (code that follows a return, throw, break or + continue statement and is not a function/variable declaration). Pass + this option to disable this optimization. + +
  • +
  • -nc or --no-copyright — by default, uglifyjs will keep the initial + comment tokens in the generated code (assumed to be copyright information + etc.). If you pass this it will discard it. + +
  • +
  • -o filename or --output filename — put the result in filename. If + this isn't given, the result goes to standard output (or see next one). + +
  • +
  • --overwrite — if the code is read from a file (not from STDIN) and you + pass --overwrite then the output will be written in the same file. + +
  • +
  • --ast — pass this if you want to get the Abstract Syntax Tree instead + of JavaScript as output. Useful for debugging or learning more about the + internals. + +
  • +
  • -v or --verbose — output some notes on STDERR (for now just how long + each operation takes). + +
  • +
  • -d SYMBOL[=VALUE] or --define SYMBOL[=VALUE] — will replace + all instances of the specified symbol where used as an identifier + (except where symbol has properly declared by a var declaration or + use as function parameter or similar) with the specified value. This + argument may be specified multiple times to define multiple + symbols - if no value is specified the symbol will be replaced with + the value true, or you can specify a numeric value (such as + 1024), a quoted string value (such as ="object"= or + ='https://github.com'), or the name of another symbol or keyword (such as =null or document). + This allows you, for example, to assign meaningful names to key + constant values but discard the symbolic names in the uglified + version for brevity/efficiency, or when used wth care, allows + UglifyJS to operate as a form of conditional compilation + whereby defining appropriate values may, by dint of the constant + folding and dead code removal features above, remove entire + superfluous code blocks (e.g. completely remove instrumentation or + trace code for production use). + Where string values are being defined, the handling of quotes are + likely to be subject to the specifics of your command shell + environment, so you may need to experiment with quoting styles + depending on your platform, or you may find the option + --define-from-module more suitable for use. + +
  • +
  • -define-from-module SOMEMODULE — will load the named module (as + per the NodeJS require() function) and iterate all the exported + properties of the module defining them as symbol names to be defined + (as if by the --define option) per the name of each property + (i.e. without the module name prefix) and given the value of the + property. This is a much easier way to handle and document groups of + symbols to be defined rather than a large number of --define + options. + +
  • +
  • --unsafe — enable other additional optimizations that are known to be + unsafe in some contrived situations, but could still be generally useful. + For now only these: + +
      +
    • foo.toString() ==> foo+"" +
    • +
    • new Array(x,…) ==> [x,…] +
    • +
    • new Array(x) ==> Array(x) + +
    • +
    + +
  • +
  • --max-line-len (default 32K characters) — add a newline after around + 32K characters. I've seen both FF and Chrome croak when all the code was + on a single line of around 670K. Pass –max-line-len 0 to disable this + safety feature. + +
  • +
  • --reserved-names — some libraries rely on certain names to be used, as + pointed out in issue #92 and #81, so this option allow you to exclude such + names from the mangler. For example, to keep names require and $super + intact you'd specify –reserved-names "require,$super". + +
  • +
  • --inline-script – when you want to include the output literally in an + HTML <script> tag you can use this option to prevent </script from + showing up in the output. + +
  • +
  • --lift-vars – when you pass this, UglifyJS will apply the following + transformations (see the notes in API, ast_lift_variables): + +
      +
    • put all var declarations at the start of the scope +
    • +
    • make sure a variable is declared only once +
    • +
    • discard unused function arguments +
    • +
    • discard unused inner (named) functions +
    • +
    • finally, try to merge assignments into that one var declaration, if + possible. +
    • +
    + +
  • +
+ + + +
+ +
+

1.4.1 API

+
+ + +

+To use the library from JavaScript, you'd do the following (example for +NodeJS): +

+ + + +
var jsp = require("uglify-js").parser;
+var pro = require("uglify-js").uglify;
+
+var orig_code = "... JS code here";
+var ast = jsp.parse(orig_code); // parse code and get the initial AST
+ast = pro.ast_mangle(ast); // get a new AST with mangled names
+ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
+var final_code = pro.gen_code(ast); // compressed code here
+
+ + +

+The above performs the full compression that is possible right now. As you +can see, there are a sequence of steps which you can apply. For example if +you want compressed output but for some reason you don't want to mangle +variable names, you would simply skip the line that calls +pro.ast_mangle(ast). +

+

+Some of these functions take optional arguments. Here's a description: +

+
    +
  • jsp.parse(code, strict_semicolons) – parses JS code and returns an AST. + strict_semicolons is optional and defaults to false. If you pass + true then the parser will throw an error when it expects a semicolon and + it doesn't find it. For most JS code you don't want that, but it's useful + if you want to strictly sanitize your code. + +
  • +
  • pro.ast_lift_variables(ast) – merge and move var declarations to the + scop of the scope; discard unused function arguments or variables; discard + unused (named) inner functions. It also tries to merge assignments + following the var declaration into it. + +

    + If your code is very hand-optimized concerning var declarations, this + lifting variable declarations might actually increase size. For me it + helps out. On jQuery it adds 865 bytes (243 after gzip). YMMV. Also + note that (since it's not enabled by default) this operation isn't yet + heavily tested (please report if you find issues!). +

    +

    + Note that although it might increase the image size (on jQuery it gains + 865 bytes, 243 after gzip) it's technically more correct: in certain + situations, dead code removal might drop variable declarations, which + would not happen if the variables are lifted in advance. +

    +

    + Here's an example of what it does: +

  • +
+ + + + + +
function f(a, b, c, d, e) {
+    var q;
+    var w;
+    w = 10;
+    q = 20;
+    for (var i = 1; i < 10; ++i) {
+        var boo = foo(a);
+    }
+    for (var i = 0; i < 1; ++i) {
+        var boo = bar(c);
+    }
+    function foo(){ ... }
+    function bar(){ ... }
+    function baz(){ ... }
+}
+
+// transforms into ==>
+
+function f(a, b, c) {
+    var i, boo, w = 10, q = 20;
+    for (i = 1; i < 10; ++i) {
+        boo = foo(a);
+    }
+    for (i = 0; i < 1; ++i) {
+        boo = bar(c);
+    }
+    function foo() { ... }
+    function bar() { ... }
+}
+
+ + +
    +
  • pro.ast_mangle(ast, options) – generates a new AST containing mangled + (compressed) variable and function names. It supports the following + options: + +
      +
    • toplevel – mangle toplevel names (by default we don't touch them). +
    • +
    • except – an array of names to exclude from compression. +
    • +
    • defines – an object with properties named after symbols to + replace (see the --define option for the script) and the values + representing the AST replacement value. + +
    • +
    + +
  • +
  • pro.ast_squeeze(ast, options) – employs further optimizations designed + to reduce the size of the code that gen_code would generate from the + AST. Returns a new AST. options can be a hash; the supported options + are: + +
      +
    • make_seqs (default true) which will cause consecutive statements in a + block to be merged using the "sequence" (comma) operator + +
    • +
    • dead_code (default true) which will remove unreachable code. + +
    • +
    + +
  • +
  • pro.gen_code(ast, options) – generates JS code from the AST. By + default it's minified, but using the options argument you can get nicely + formatted output. options is, well, optional :-) and if you pass it it + must be an object and supports the following properties (below you can see + the default values): + +
      +
    • beautify: false – pass true if you want indented output +
    • +
    • indent_start: 0 (only applies when beautify is true) – initial + indentation in spaces +
    • +
    • indent_level: 4 (only applies when beautify is true) -- + indentation level, in spaces (pass an even number) +
    • +
    • quote_keys: false – if you pass true it will quote all keys in + literal objects +
    • +
    • space_colon: false (only applies when beautify is true) – wether + to put a space before the colon in object literals +
    • +
    • ascii_only: false – pass true if you want to encode non-ASCII + characters as \uXXXX. +
    • +
    • inline_script: false – pass true to escape occurrences of + </script in strings +
    • +
    + +
  • +
+ + +
+ +
+ +
+

1.4.2 Beautifier shortcoming – no more comments

+
+ + +

+The beautifier can be used as a general purpose indentation tool. It's +useful when you want to make a minified file readable. One limitation, +though, is that it discards all comments, so you don't really want to use it +to reformat your code, unless you don't have, or don't care about, comments. +

+

+In fact it's not the beautifier who discards comments — they are dumped at +the parsing stage, when we build the initial AST. Comments don't really +make sense in the AST, and while we could add nodes for them, it would be +inconvenient because we'd have to add special rules to ignore them at all +the processing stages. +

+
+ +
+ +
+

1.4.3 Use as a code pre-processor

+
+ + +

+The --define option can be used, particularly when combined with the +constant folding logic, as a form of pre-processor to enable or remove +particular constructions, such as might be used for instrumenting +development code, or to produce variations aimed at a specific +platform. +

+

+The code below illustrates the way this can be done, and how the +symbol replacement is performed. +

+ + + +
CLAUSE1: if (typeof DEVMODE === 'undefined') {
+    DEVMODE = true;
+}
+
+CLAUSE2: function init() {
+    if (DEVMODE) {
+        console.log("init() called");
+    }
+    ....
+    DEVMODE &amp;&amp; console.log("init() complete");
+}
+
+CLAUSE3: function reportDeviceStatus(device) {
+    var DEVMODE = device.mode, DEVNAME = device.name;
+    if (DEVMODE === 'open') {
+        ....
+    }
+}
+
+ + +

+When the above code is normally executed, the undeclared global +variable DEVMODE will be assigned the value true (see CLAUSE1) +and so the init() function (CLAUSE2) will write messages to the +console log when executed, but in CLAUSE3 a locally declared +variable will mask access to the DEVMODE global symbol. +

+

+If the above code is processed by UglifyJS with an argument of +--define DEVMODE=false then UglifyJS will replace DEVMODE with the +boolean constant value false within CLAUSE1 and CLAUSE2, but it +will leave CLAUSE3 as it stands because there DEVMODE resolves to +a validly declared variable. +

+

+And more so, the constant-folding features of UglifyJS will recognise +that the if condition of CLAUSE1 is thus always false, and so will +remove the test and body of CLAUSE1 altogether (including the +otherwise slightly problematical statement false = true; which it +will have formed by replacing DEVMODE in the body). Similarly, +within CLAUSE2 both calls to console.log() will be removed +altogether. +

+

+In this way you can mimic, to a limited degree, the functionality of +the C/C++ pre-processor to enable or completely remove blocks +depending on how certain symbols are defined - perhaps using UglifyJS +to generate different versions of source aimed at different +environments +

+

+It is recommmended (but not made mandatory) that symbols designed for +this purpose are given names consisting of UPPER_CASE_LETTERS to +distinguish them from other (normal) symbols and avoid the sort of +clash that CLAUSE3 above illustrates. +

+
+
+ +
+ +
+

1.5 Compression – how good is it?

+
+ + +

+Here are updated statistics. (I also updated my Google Closure and YUI +installations). +

+

+We're still a lot better than YUI in terms of compression, though slightly +slower. We're still a lot faster than Closure, and compression after gzip +is comparable. +

+ + ++ + + + + + + + + + +
FileUglifyJSUglifyJS+gzipClosureClosure+gzipYUIYUI+gzip
jquery-1.6.2.js91001 (0:01.59)3189690678 (0:07.40)31979101527 (0:01.82)34646
paper.js142023 (0:01.65)43334134301 (0:07.42)42495173383 (0:01.58)48785
prototype.js88544 (0:01.09)2668086955 (0:06.97)2632692130 (0:00.79)28624
thelib-full.js (DynarchLIB)251939 (0:02.55)72535249911 (0:09.05)72696258869 (0:01.94)76584
+ + +
+ +
+ +
+

1.6 Bugs?

+
+ + +

+Unfortunately, for the time being there is no automated test suite. But I +ran the compressor manually on non-trivial code, and then I tested that the +generated code works as expected. A few hundred times. +

+

+DynarchLIB was started in times when there was no good JS minifier. +Therefore I was quite religious about trying to write short code manually, +and as such DL contains a lot of syntactic hacks1 such as “foo == bar ? a += 10 : b = 20”, though the more readable version would clearly be to use +“if/else”. +

+

+Since the parser/compressor runs fine on DL and jQuery, I'm quite confident +that it's solid enough for production use. If you can identify any bugs, +I'd love to hear about them (use the Google Group or email me directly). +

+
+ +
+ +
+

1.7 Links

+
+ + + + + +
+ +
+ +
+

1.8 License

+
+ + +

+UglifyJS is released under the BSD license: +

+ + + +
Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com>
+Based on parse-js (http://marijn.haverbeke.nl/parse-js/).
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+    * Redistributions of source code must retain the above
+      copyright notice, this list of conditions and the following
+      disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials
+      provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+ + +
+

Footnotes:

+
+

1 I even reported a few bugs and suggested some fixes in the original + parse-js library, and Marijn pushed fixes literally in minutes. +

+
+
+ +
+
+
+ +
+

Date: 2011-12-09 14:59:08 EET

+

Author: Mihai Bazon

+

Org version 7.7 with Emacs version 23

+Validate XHTML 1.0 + +
+ + diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/README.org b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/README.org new file mode 100644 index 0000000..4d01fdf --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/README.org @@ -0,0 +1,574 @@ +#+TITLE: UglifyJS -- a JavaScript parser/compressor/beautifier +#+KEYWORDS: javascript, js, parser, compiler, compressor, mangle, minify, minifier +#+DESCRIPTION: a JavaScript parser/compressor/beautifier in JavaScript +#+STYLE: +#+AUTHOR: Mihai Bazon +#+EMAIL: mihai.bazon@gmail.com + +* UglifyJS --- a JavaScript parser/compressor/beautifier + +This package implements a general-purpose JavaScript +parser/compressor/beautifier toolkit. It is developed on [[http://nodejs.org/][NodeJS]], but it +should work on any JavaScript platform supporting the CommonJS module system +(and if your platform of choice doesn't support CommonJS, you can easily +implement it, or discard the =exports.*= lines from UglifyJS sources). + +The tokenizer/parser generates an abstract syntax tree from JS code. You +can then traverse the AST to learn more about the code, or do various +manipulations on it. This part is implemented in [[../lib/parse-js.js][parse-js.js]] and it's a +port to JavaScript of the excellent [[http://marijn.haverbeke.nl/parse-js/][parse-js]] Common Lisp library from [[http://marijn.haverbeke.nl/][Marijn +Haverbeke]]. + +( See [[http://github.com/mishoo/cl-uglify-js][cl-uglify-js]] if you're looking for the Common Lisp version of +UglifyJS. ) + +The second part of this package, implemented in [[../lib/process.js][process.js]], inspects and +manipulates the AST generated by the parser to provide the following: + +- ability to re-generate JavaScript code from the AST. Optionally + indented---you can use this if you want to “beautify” a program that has + been compressed, so that you can inspect the source. But you can also run + our code generator to print out an AST without any whitespace, so you + achieve compression as well. + +- shorten variable names (usually to single characters). Our mangler will + analyze the code and generate proper variable names, depending on scope + and usage, and is smart enough to deal with globals defined elsewhere, or + with =eval()= calls or =with{}= statements. In short, if =eval()= or + =with{}= are used in some scope, then all variables in that scope and any + variables in the parent scopes will remain unmangled, and any references + to such variables remain unmangled as well. + +- various small optimizations that may lead to faster code but certainly + lead to smaller code. Where possible, we do the following: + + - foo["bar"] ==> foo.bar + + - remove block brackets ={}= + + - join consecutive var declarations: + var a = 10; var b = 20; ==> var a=10,b=20; + + - resolve simple constant expressions: 1 +2 * 3 ==> 7. We only do the + replacement if the result occupies less bytes; for example 1/3 would + translate to 0.333333333333, so in this case we don't replace it. + + - consecutive statements in blocks are merged into a sequence; in many + cases, this leaves blocks with a single statement, so then we can remove + the block brackets. + + - various optimizations for IF statements: + + - if (foo) bar(); else baz(); ==> foo?bar():baz(); + - if (!foo) bar(); else baz(); ==> foo?baz():bar(); + - if (foo) bar(); ==> foo&&bar(); + - if (!foo) bar(); ==> foo||bar(); + - if (foo) return bar(); else return baz(); ==> return foo?bar():baz(); + - if (foo) return bar(); else something(); ==> {if(foo)return bar();something()} + + - remove some unreachable code and warn about it (code that follows a + =return=, =throw=, =break= or =continue= statement, except + function/variable declarations). + + - act a limited version of a pre-processor (c.f. the pre-processor of + C/C++) to allow you to safely replace selected global symbols with + specified values. When combined with the optimisations above this can + make UglifyJS operate slightly more like a compilation process, in + that when certain symbols are replaced by constant values, entire code + blocks may be optimised away as unreachable. + +** <> + +The following transformations can in theory break code, although they're +probably safe in most practical cases. To enable them you need to pass the +=--unsafe= flag. + +*** Calls involving the global Array constructor + +The following transformations occur: + +#+BEGIN_SRC js +new Array(1, 2, 3, 4) => [1,2,3,4] +Array(a, b, c) => [a,b,c] +new Array(5) => Array(5) +new Array(a) => Array(a) +#+END_SRC + +These are all safe if the Array name isn't redefined. JavaScript does allow +one to globally redefine Array (and pretty much everything, in fact) but I +personally don't see why would anyone do that. + +UglifyJS does handle the case where Array is redefined locally, or even +globally but with a =function= or =var= declaration. Therefore, in the +following cases UglifyJS *doesn't touch* calls or instantiations of Array: + +#+BEGIN_SRC js +// case 1. globally declared variable + var Array; + new Array(1, 2, 3); + Array(a, b); + + // or (can be declared later) + new Array(1, 2, 3); + var Array; + + // or (can be a function) + new Array(1, 2, 3); + function Array() { ... } + +// case 2. declared in a function + (function(){ + a = new Array(1, 2, 3); + b = Array(5, 6); + var Array; + })(); + + // or + (function(Array){ + return Array(5, 6, 7); + })(); + + // or + (function(){ + return new Array(1, 2, 3, 4); + function Array() { ... } + })(); + + // etc. +#+END_SRC + +*** =obj.toString()= ==> =obj+“”= + +** Install (NPM) + +UglifyJS is now available through NPM --- =npm install uglify-js= should do +the job. + +** Install latest code from GitHub + +#+BEGIN_SRC sh +## clone the repository +mkdir -p /where/you/wanna/put/it +cd /where/you/wanna/put/it +git clone git://github.com/mishoo/UglifyJS.git + +## make the module available to Node +mkdir -p ~/.node_libraries/ +cd ~/.node_libraries/ +ln -s /where/you/wanna/put/it/UglifyJS/uglify-js.js + +## and if you want the CLI script too: +mkdir -p ~/bin +cd ~/bin +ln -s /where/you/wanna/put/it/UglifyJS/bin/uglifyjs + # (then add ~/bin to your $PATH if it's not there already) +#+END_SRC + +** Usage + +There is a command-line tool that exposes the functionality of this library +for your shell-scripting needs: + +#+BEGIN_SRC sh +uglifyjs [ options... ] [ filename ] +#+END_SRC + +=filename= should be the last argument and should name the file from which +to read the JavaScript code. If you don't specify it, it will read code +from STDIN. + +Supported options: + +- =-b= or =--beautify= --- output indented code; when passed, additional + options control the beautifier: + + - =-i N= or =--indent N= --- indentation level (number of spaces) + + - =-q= or =--quote-keys= --- quote keys in literal objects (by default, + only keys that cannot be identifier names will be quotes). + +- =--ascii= --- pass this argument to encode non-ASCII characters as + =\uXXXX= sequences. By default UglifyJS won't bother to do it and will + output Unicode characters instead. (the output is always encoded in UTF8, + but if you pass this option you'll only get ASCII). + +- =-nm= or =--no-mangle= --- don't mangle names. + +- =-nmf= or =--no-mangle-functions= -- in case you want to mangle variable + names, but not touch function names. + +- =-ns= or =--no-squeeze= --- don't call =ast_squeeze()= (which does various + optimizations that result in smaller, less readable code). + +- =-mt= or =--mangle-toplevel= --- mangle names in the toplevel scope too + (by default we don't do this). + +- =--no-seqs= --- when =ast_squeeze()= is called (thus, unless you pass + =--no-squeeze=) it will reduce consecutive statements in blocks into a + sequence. For example, "a = 10; b = 20; foo();" will be written as + "a=10,b=20,foo();". In various occasions, this allows us to discard the + block brackets (since the block becomes a single statement). This is ON + by default because it seems safe and saves a few hundred bytes on some + libs that I tested it on, but pass =--no-seqs= to disable it. + +- =--no-dead-code= --- by default, UglifyJS will remove code that is + obviously unreachable (code that follows a =return=, =throw=, =break= or + =continue= statement and is not a function/variable declaration). Pass + this option to disable this optimization. + +- =-nc= or =--no-copyright= --- by default, =uglifyjs= will keep the initial + comment tokens in the generated code (assumed to be copyright information + etc.). If you pass this it will discard it. + +- =-o filename= or =--output filename= --- put the result in =filename=. If + this isn't given, the result goes to standard output (or see next one). + +- =--overwrite= --- if the code is read from a file (not from STDIN) and you + pass =--overwrite= then the output will be written in the same file. + +- =--ast= --- pass this if you want to get the Abstract Syntax Tree instead + of JavaScript as output. Useful for debugging or learning more about the + internals. + +- =-v= or =--verbose= --- output some notes on STDERR (for now just how long + each operation takes). + +- =-d SYMBOL[=VALUE]= or =--define SYMBOL[=VALUE]= --- will replace + all instances of the specified symbol where used as an identifier + (except where symbol has properly declared by a var declaration or + use as function parameter or similar) with the specified value. This + argument may be specified multiple times to define multiple + symbols - if no value is specified the symbol will be replaced with + the value =true=, or you can specify a numeric value (such as + =1024=), a quoted string value (such as ="object"= or + ='https://github.com'=), or the name of another symbol or keyword + (such as =null= or =document=). + This allows you, for example, to assign meaningful names to key + constant values but discard the symbolic names in the uglified + version for brevity/efficiency, or when used wth care, allows + UglifyJS to operate as a form of *conditional compilation* + whereby defining appropriate values may, by dint of the constant + folding and dead code removal features above, remove entire + superfluous code blocks (e.g. completely remove instrumentation or + trace code for production use). + Where string values are being defined, the handling of quotes are + likely to be subject to the specifics of your command shell + environment, so you may need to experiment with quoting styles + depending on your platform, or you may find the option + =--define-from-module= more suitable for use. + +- =-define-from-module SOMEMODULE= --- will load the named module (as + per the NodeJS =require()= function) and iterate all the exported + properties of the module defining them as symbol names to be defined + (as if by the =--define= option) per the name of each property + (i.e. without the module name prefix) and given the value of the + property. This is a much easier way to handle and document groups of + symbols to be defined rather than a large number of =--define= + options. + +- =--unsafe= --- enable other additional optimizations that are known to be + unsafe in some contrived situations, but could still be generally useful. + For now only these: + + - foo.toString() ==> foo+"" + - new Array(x,...) ==> [x,...] + - new Array(x) ==> Array(x) + +- =--max-line-len= (default 32K characters) --- add a newline after around + 32K characters. I've seen both FF and Chrome croak when all the code was + on a single line of around 670K. Pass --max-line-len 0 to disable this + safety feature. + +- =--reserved-names= --- some libraries rely on certain names to be used, as + pointed out in issue #92 and #81, so this option allow you to exclude such + names from the mangler. For example, to keep names =require= and =$super= + intact you'd specify --reserved-names "require,$super". + +- =--inline-script= -- when you want to include the output literally in an + HTML =\n\n\n\n\n
\n\n
\n\n
\n

UglifyJS – a JavaScript parser/compressor/beautifier

\n\n\n\n\n
\n

1 UglifyJS — a JavaScript parser/compressor/beautifier

\n
\n\n\n

\nThis package implements a general-purpose JavaScript\nparser/compressor/beautifier toolkit. It is developed on NodeJS, but it\nshould work on any JavaScript platform supporting the CommonJS module system\n(and if your platform of choice doesn't support CommonJS, you can easily\nimplement it, or discard the exports.* lines from UglifyJS sources).\n

\n

\nThe tokenizer/parser generates an abstract syntax tree from JS code. You\ncan then traverse the AST to learn more about the code, or do various\nmanipulations on it. This part is implemented in parse-js.js and it's a\nport to JavaScript of the excellent parse-js Common Lisp library from Marijn Haverbeke.\n

\n

\n( See cl-uglify-js if you're looking for the Common Lisp version of\nUglifyJS. )\n

\n

\nThe second part of this package, implemented in process.js, inspects and\nmanipulates the AST generated by the parser to provide the following:\n

\n
    \n
  • ability to re-generate JavaScript code from the AST. Optionally\n indented—you can use this if you want to “beautify” a program that has\n been compressed, so that you can inspect the source. But you can also run\n our code generator to print out an AST without any whitespace, so you\n achieve compression as well.\n\n
  • \n
  • shorten variable names (usually to single characters). Our mangler will\n analyze the code and generate proper variable names, depending on scope\n and usage, and is smart enough to deal with globals defined elsewhere, or\n with eval() calls or with{} statements. In short, if eval() or\n with{} are used in some scope, then all variables in that scope and any\n variables in the parent scopes will remain unmangled, and any references\n to such variables remain unmangled as well.\n\n
  • \n
  • various small optimizations that may lead to faster code but certainly\n lead to smaller code. Where possible, we do the following:\n\n
      \n
    • foo[\"bar\"] ==> foo.bar\n\n
    • \n
    • remove block brackets {}\n\n
    • \n
    • join consecutive var declarations:\n var a = 10; var b = 20; ==> var a=10,b=20;\n\n
    • \n
    • resolve simple constant expressions: 1 +2 * 3 ==> 7. We only do the\n replacement if the result occupies less bytes; for example 1/3 would\n translate to 0.333333333333, so in this case we don't replace it.\n\n
    • \n
    • consecutive statements in blocks are merged into a sequence; in many\n cases, this leaves blocks with a single statement, so then we can remove\n the block brackets.\n\n
    • \n
    • various optimizations for IF statements:\n\n
        \n
      • if (foo) bar(); else baz(); ==> foo?bar():baz();\n
      • \n
      • if (!foo) bar(); else baz(); ==> foo?baz():bar();\n
      • \n
      • if (foo) bar(); ==> foo&&bar();\n
      • \n
      • if (!foo) bar(); ==> foo||bar();\n
      • \n
      • if (foo) return bar(); else return baz(); ==> return foo?bar():baz();\n
      • \n
      • if (foo) return bar(); else something(); ==> {if(foo)return bar();something()}\n\n
      • \n
      \n\n
    • \n
    • remove some unreachable code and warn about it (code that follows a\n return, throw, break or continue statement, except\n function/variable declarations).\n\n
    • \n
    • act a limited version of a pre-processor (c.f. the pre-processor of\n C/C++) to allow you to safely replace selected global symbols with\n specified values. When combined with the optimisations above this can\n make UglifyJS operate slightly more like a compilation process, in\n that when certain symbols are replaced by constant values, entire code\n blocks may be optimised away as unreachable.\n
    • \n
    \n\n
  • \n
\n\n\n\n
\n\n
\n

1.1 Unsafe transformations

\n
\n\n\n

\nThe following transformations can in theory break code, although they're\nprobably safe in most practical cases. To enable them you need to pass the\n--unsafe flag.\n

\n\n
\n\n
\n

1.1.1 Calls involving the global Array constructor

\n
\n\n\n

\nThe following transformations occur:\n

\n\n\n\n
new Array(1, 2, 3, 4)  => [1,2,3,4]\nArray(a, b, c)         => [a,b,c]\nnew Array(5)           => Array(5)\nnew Array(a)           => Array(a)\n
\n\n\n

\nThese are all safe if the Array name isn't redefined. JavaScript does allow\none to globally redefine Array (and pretty much everything, in fact) but I\npersonally don't see why would anyone do that.\n

\n

\nUglifyJS does handle the case where Array is redefined locally, or even\nglobally but with a function or var declaration. Therefore, in the\nfollowing cases UglifyJS doesn't touch calls or instantiations of Array:\n

\n\n\n\n
// case 1.  globally declared variable\n  var Array;\n  new Array(1, 2, 3);\n  Array(a, b);\n\n  // or (can be declared later)\n  new Array(1, 2, 3);\n  var Array;\n\n  // or (can be a function)\n  new Array(1, 2, 3);\n  function Array() { ... }\n\n// case 2.  declared in a function\n  (function(){\n    a = new Array(1, 2, 3);\n    b = Array(5, 6);\n    var Array;\n  })();\n\n  // or\n  (function(Array){\n    return Array(5, 6, 7);\n  })();\n\n  // or\n  (function(){\n    return new Array(1, 2, 3, 4);\n    function Array() { ... }\n  })();\n\n  // etc.\n
\n\n\n
\n\n
\n\n
\n

1.1.2 obj.toString() ==> obj+“”

\n
\n\n\n
\n
\n\n
\n\n
\n

1.2 Install (NPM)

\n
\n\n\n

\nUglifyJS is now available through NPM — npm install uglify-js should do\nthe job.\n

\n
\n\n
\n\n
\n

1.3 Install latest code from GitHub

\n
\n\n\n\n\n\n
## clone the repository\nmkdir -p /where/you/wanna/put/it\ncd /where/you/wanna/put/it\ngit clone git://github.com/mishoo/UglifyJS.git\n\n## make the module available to Node\nmkdir -p ~/.node_libraries/\ncd ~/.node_libraries/\nln -s /where/you/wanna/put/it/UglifyJS/uglify-js.js\n\n## and if you want the CLI script too:\nmkdir -p ~/bin\ncd ~/bin\nln -s /where/you/wanna/put/it/UglifyJS/bin/uglifyjs\n  # (then add ~/bin to your $PATH if it's not there already)\n
\n\n\n
\n\n
\n\n
\n

1.4 Usage

\n
\n\n\n

\nThere is a command-line tool that exposes the functionality of this library\nfor your shell-scripting needs:\n

\n\n\n\n
uglifyjs [ options... ] [ filename ]\n
\n\n\n

\nfilename should be the last argument and should name the file from which\nto read the JavaScript code. If you don't specify it, it will read code\nfrom STDIN.\n

\n

\nSupported options:\n

\n
    \n
  • -b or --beautify — output indented code; when passed, additional\n options control the beautifier:\n\n
      \n
    • -i N or --indent N — indentation level (number of spaces)\n\n
    • \n
    • -q or --quote-keys — quote keys in literal objects (by default,\n only keys that cannot be identifier names will be quotes).\n\n
    • \n
    \n\n
  • \n
  • --ascii — pass this argument to encode non-ASCII characters as\n \\uXXXX sequences. By default UglifyJS won't bother to do it and will\n output Unicode characters instead. (the output is always encoded in UTF8,\n but if you pass this option you'll only get ASCII).\n\n
  • \n
  • -nm or --no-mangle — don't mangle names.\n\n
  • \n
  • -nmf or --no-mangle-functions – in case you want to mangle variable\n names, but not touch function names.\n\n
  • \n
  • -ns or --no-squeeze — don't call ast_squeeze() (which does various\n optimizations that result in smaller, less readable code).\n\n
  • \n
  • -mt or --mangle-toplevel — mangle names in the toplevel scope too\n (by default we don't do this).\n\n
  • \n
  • --no-seqs — when ast_squeeze() is called (thus, unless you pass\n --no-squeeze) it will reduce consecutive statements in blocks into a\n sequence. For example, \"a = 10; b = 20; foo();\" will be written as\n \"a=10,b=20,foo();\". In various occasions, this allows us to discard the\n block brackets (since the block becomes a single statement). This is ON\n by default because it seems safe and saves a few hundred bytes on some\n libs that I tested it on, but pass --no-seqs to disable it.\n\n
  • \n
  • --no-dead-code — by default, UglifyJS will remove code that is\n obviously unreachable (code that follows a return, throw, break or\n continue statement and is not a function/variable declaration). Pass\n this option to disable this optimization.\n\n
  • \n
  • -nc or --no-copyright — by default, uglifyjs will keep the initial\n comment tokens in the generated code (assumed to be copyright information\n etc.). If you pass this it will discard it.\n\n
  • \n
  • -o filename or --output filename — put the result in filename. If\n this isn't given, the result goes to standard output (or see next one).\n\n
  • \n
  • --overwrite — if the code is read from a file (not from STDIN) and you\n pass --overwrite then the output will be written in the same file.\n\n
  • \n
  • --ast — pass this if you want to get the Abstract Syntax Tree instead\n of JavaScript as output. Useful for debugging or learning more about the\n internals.\n\n
  • \n
  • -v or --verbose — output some notes on STDERR (for now just how long\n each operation takes).\n\n
  • \n
  • -d SYMBOL[=VALUE] or --define SYMBOL[=VALUE] — will replace\n all instances of the specified symbol where used as an identifier\n (except where symbol has properly declared by a var declaration or\n use as function parameter or similar) with the specified value. This\n argument may be specified multiple times to define multiple\n symbols - if no value is specified the symbol will be replaced with\n the value true, or you can specify a numeric value (such as\n 1024), a quoted string value (such as =\"object\"= or\n ='https://github.com'), or the name of another symbol or keyword (such as =null or document).\n This allows you, for example, to assign meaningful names to key\n constant values but discard the symbolic names in the uglified\n version for brevity/efficiency, or when used wth care, allows\n UglifyJS to operate as a form of conditional compilation\n whereby defining appropriate values may, by dint of the constant\n folding and dead code removal features above, remove entire\n superfluous code blocks (e.g. completely remove instrumentation or\n trace code for production use).\n Where string values are being defined, the handling of quotes are\n likely to be subject to the specifics of your command shell\n environment, so you may need to experiment with quoting styles\n depending on your platform, or you may find the option\n --define-from-module more suitable for use.\n\n
  • \n
  • -define-from-module SOMEMODULE — will load the named module (as\n per the NodeJS require() function) and iterate all the exported\n properties of the module defining them as symbol names to be defined\n (as if by the --define option) per the name of each property\n (i.e. without the module name prefix) and given the value of the\n property. This is a much easier way to handle and document groups of\n symbols to be defined rather than a large number of --define\n options.\n\n
  • \n
  • --unsafe — enable other additional optimizations that are known to be\n unsafe in some contrived situations, but could still be generally useful.\n For now only these:\n\n
      \n
    • foo.toString() ==> foo+\"\"\n
    • \n
    • new Array(x,…) ==> [x,…]\n
    • \n
    • new Array(x) ==> Array(x)\n\n
    • \n
    \n\n
  • \n
  • --max-line-len (default 32K characters) — add a newline after around\n 32K characters. I've seen both FF and Chrome croak when all the code was\n on a single line of around 670K. Pass –max-line-len 0 to disable this\n safety feature.\n\n
  • \n
  • --reserved-names — some libraries rely on certain names to be used, as\n pointed out in issue #92 and #81, so this option allow you to exclude such\n names from the mangler. For example, to keep names require and $super\n intact you'd specify –reserved-names \"require,$super\".\n\n
  • \n
  • --inline-script – when you want to include the output literally in an\n HTML <script> tag you can use this option to prevent </script from\n showing up in the output.\n\n
  • \n
  • --lift-vars – when you pass this, UglifyJS will apply the following\n transformations (see the notes in API, ast_lift_variables):\n\n
      \n
    • put all var declarations at the start of the scope\n
    • \n
    • make sure a variable is declared only once\n
    • \n
    • discard unused function arguments\n
    • \n
    • discard unused inner (named) functions\n
    • \n
    • finally, try to merge assignments into that one var declaration, if\n possible.\n
    • \n
    \n\n
  • \n
\n\n\n\n
\n\n
\n

1.4.1 API

\n
\n\n\n

\nTo use the library from JavaScript, you'd do the following (example for\nNodeJS):\n

\n\n\n\n
var jsp = require(\"uglify-js\").parser;\nvar pro = require(\"uglify-js\").uglify;\n\nvar orig_code = \"... JS code here\";\nvar ast = jsp.parse(orig_code); // parse code and get the initial AST\nast = pro.ast_mangle(ast); // get a new AST with mangled names\nast = pro.ast_squeeze(ast); // get an AST with compression optimizations\nvar final_code = pro.gen_code(ast); // compressed code here\n
\n\n\n

\nThe above performs the full compression that is possible right now. As you\ncan see, there are a sequence of steps which you can apply. For example if\nyou want compressed output but for some reason you don't want to mangle\nvariable names, you would simply skip the line that calls\npro.ast_mangle(ast).\n

\n

\nSome of these functions take optional arguments. Here's a description:\n

\n
    \n
  • jsp.parse(code, strict_semicolons) – parses JS code and returns an AST.\n strict_semicolons is optional and defaults to false. If you pass\n true then the parser will throw an error when it expects a semicolon and\n it doesn't find it. For most JS code you don't want that, but it's useful\n if you want to strictly sanitize your code.\n\n
  • \n
  • pro.ast_lift_variables(ast) – merge and move var declarations to the\n scop of the scope; discard unused function arguments or variables; discard\n unused (named) inner functions. It also tries to merge assignments\n following the var declaration into it.\n\n

    \n If your code is very hand-optimized concerning var declarations, this\n lifting variable declarations might actually increase size. For me it\n helps out. On jQuery it adds 865 bytes (243 after gzip). YMMV. Also\n note that (since it's not enabled by default) this operation isn't yet\n heavily tested (please report if you find issues!).\n

    \n

    \n Note that although it might increase the image size (on jQuery it gains\n 865 bytes, 243 after gzip) it's technically more correct: in certain\n situations, dead code removal might drop variable declarations, which\n would not happen if the variables are lifted in advance.\n

    \n

    \n Here's an example of what it does:\n

  • \n
\n\n\n\n\n\n
function f(a, b, c, d, e) {\n    var q;\n    var w;\n    w = 10;\n    q = 20;\n    for (var i = 1; i < 10; ++i) {\n        var boo = foo(a);\n    }\n    for (var i = 0; i < 1; ++i) {\n        var boo = bar(c);\n    }\n    function foo(){ ... }\n    function bar(){ ... }\n    function baz(){ ... }\n}\n\n// transforms into ==>\n\nfunction f(a, b, c) {\n    var i, boo, w = 10, q = 20;\n    for (i = 1; i < 10; ++i) {\n        boo = foo(a);\n    }\n    for (i = 0; i < 1; ++i) {\n        boo = bar(c);\n    }\n    function foo() { ... }\n    function bar() { ... }\n}\n
\n\n\n
    \n
  • pro.ast_mangle(ast, options) – generates a new AST containing mangled\n (compressed) variable and function names. It supports the following\n options:\n\n
      \n
    • toplevel – mangle toplevel names (by default we don't touch them).\n
    • \n
    • except – an array of names to exclude from compression.\n
    • \n
    • defines – an object with properties named after symbols to\n replace (see the --define option for the script) and the values\n representing the AST replacement value.\n\n
    • \n
    \n\n
  • \n
  • pro.ast_squeeze(ast, options) – employs further optimizations designed\n to reduce the size of the code that gen_code would generate from the\n AST. Returns a new AST. options can be a hash; the supported options\n are:\n\n
      \n
    • make_seqs (default true) which will cause consecutive statements in a\n block to be merged using the \"sequence\" (comma) operator\n\n
    • \n
    • dead_code (default true) which will remove unreachable code.\n\n
    • \n
    \n\n
  • \n
  • pro.gen_code(ast, options) – generates JS code from the AST. By\n default it's minified, but using the options argument you can get nicely\n formatted output. options is, well, optional :-) and if you pass it it\n must be an object and supports the following properties (below you can see\n the default values):\n\n
      \n
    • beautify: false – pass true if you want indented output\n
    • \n
    • indent_start: 0 (only applies when beautify is true) – initial\n indentation in spaces\n
    • \n
    • indent_level: 4 (only applies when beautify is true) --\n indentation level, in spaces (pass an even number)\n
    • \n
    • quote_keys: false – if you pass true it will quote all keys in\n literal objects\n
    • \n
    • space_colon: false (only applies when beautify is true) – wether\n to put a space before the colon in object literals\n
    • \n
    • ascii_only: false – pass true if you want to encode non-ASCII\n characters as \\uXXXX.\n
    • \n
    • inline_script: false – pass true to escape occurrences of\n </script in strings\n
    • \n
    \n\n
  • \n
\n\n\n
\n\n
\n\n
\n

1.4.2 Beautifier shortcoming – no more comments

\n
\n\n\n

\nThe beautifier can be used as a general purpose indentation tool. It's\nuseful when you want to make a minified file readable. One limitation,\nthough, is that it discards all comments, so you don't really want to use it\nto reformat your code, unless you don't have, or don't care about, comments.\n

\n

\nIn fact it's not the beautifier who discards comments — they are dumped at\nthe parsing stage, when we build the initial AST. Comments don't really\nmake sense in the AST, and while we could add nodes for them, it would be\ninconvenient because we'd have to add special rules to ignore them at all\nthe processing stages.\n

\n
\n\n
\n\n
\n

1.4.3 Use as a code pre-processor

\n
\n\n\n

\nThe --define option can be used, particularly when combined with the\nconstant folding logic, as a form of pre-processor to enable or remove\nparticular constructions, such as might be used for instrumenting\ndevelopment code, or to produce variations aimed at a specific\nplatform.\n

\n

\nThe code below illustrates the way this can be done, and how the\nsymbol replacement is performed.\n

\n\n\n\n
CLAUSE1: if (typeof DEVMODE === 'undefined') {\n    DEVMODE = true;\n}\n\nCLAUSE2: function init() {\n    if (DEVMODE) {\n        console.log(\"init() called\");\n    }\n    ....\n    DEVMODE &amp;&amp; console.log(\"init() complete\");\n}\n\nCLAUSE3: function reportDeviceStatus(device) {\n    var DEVMODE = device.mode, DEVNAME = device.name;\n    if (DEVMODE === 'open') {\n        ....\n    }\n}\n
\n\n\n

\nWhen the above code is normally executed, the undeclared global\nvariable DEVMODE will be assigned the value true (see CLAUSE1)\nand so the init() function (CLAUSE2) will write messages to the\nconsole log when executed, but in CLAUSE3 a locally declared\nvariable will mask access to the DEVMODE global symbol.\n

\n

\nIf the above code is processed by UglifyJS with an argument of\n--define DEVMODE=false then UglifyJS will replace DEVMODE with the\nboolean constant value false within CLAUSE1 and CLAUSE2, but it\nwill leave CLAUSE3 as it stands because there DEVMODE resolves to\na validly declared variable.\n

\n

\nAnd more so, the constant-folding features of UglifyJS will recognise\nthat the if condition of CLAUSE1 is thus always false, and so will\nremove the test and body of CLAUSE1 altogether (including the\notherwise slightly problematical statement false = true; which it\nwill have formed by replacing DEVMODE in the body). Similarly,\nwithin CLAUSE2 both calls to console.log() will be removed\naltogether.\n

\n

\nIn this way you can mimic, to a limited degree, the functionality of\nthe C/C++ pre-processor to enable or completely remove blocks\ndepending on how certain symbols are defined - perhaps using UglifyJS\nto generate different versions of source aimed at different\nenvironments\n

\n

\nIt is recommmended (but not made mandatory) that symbols designed for\nthis purpose are given names consisting of UPPER_CASE_LETTERS to\ndistinguish them from other (normal) symbols and avoid the sort of\nclash that CLAUSE3 above illustrates.\n

\n
\n
\n\n
\n\n
\n

1.5 Compression – how good is it?

\n
\n\n\n

\nHere are updated statistics. (I also updated my Google Closure and YUI\ninstallations).\n

\n

\nWe're still a lot better than YUI in terms of compression, though slightly\nslower. We're still a lot faster than Closure, and compression after gzip\nis comparable.\n

\n\n\n\n\n\n\n\n\n\n\n\n\n\n
FileUglifyJSUglifyJS+gzipClosureClosure+gzipYUIYUI+gzip
jquery-1.6.2.js91001 (0:01.59)3189690678 (0:07.40)31979101527 (0:01.82)34646
paper.js142023 (0:01.65)43334134301 (0:07.42)42495173383 (0:01.58)48785
prototype.js88544 (0:01.09)2668086955 (0:06.97)2632692130 (0:00.79)28624
thelib-full.js (DynarchLIB)251939 (0:02.55)72535249911 (0:09.05)72696258869 (0:01.94)76584
\n\n\n
\n\n
\n\n
\n

1.6 Bugs?

\n
\n\n\n

\nUnfortunately, for the time being there is no automated test suite. But I\nran the compressor manually on non-trivial code, and then I tested that the\ngenerated code works as expected. A few hundred times.\n

\n

\nDynarchLIB was started in times when there was no good JS minifier.\nTherefore I was quite religious about trying to write short code manually,\nand as such DL contains a lot of syntactic hacks1 such as “foo == bar ? a\n= 10 : b = 20”, though the more readable version would clearly be to use\n“if/else”.\n

\n

\nSince the parser/compressor runs fine on DL and jQuery, I'm quite confident\nthat it's solid enough for production use. If you can identify any bugs,\nI'd love to hear about them (use the Google Group or email me directly).\n

\n
\n\n
\n\n
\n

1.7 Links

\n
\n\n\n\n\n\n
\n\n
\n\n
\n

1.8 License

\n
\n\n\n

\nUglifyJS is released under the BSD license:\n

\n\n\n\n
Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com>\nBased on parse-js (http://marijn.haverbeke.nl/parse-js/).\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n    * Redistributions of source code must retain the above\n      copyright notice, this list of conditions and the following\n      disclaimer.\n\n    * Redistributions in binary form must reproduce the above\n      copyright notice, this list of conditions and the following\n      disclaimer in the documentation and/or other materials\n      provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY\nEXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,\nOR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\nPROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR\nTORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF\nTHE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGE.\n
\n\n\n
\n

Footnotes:

\n
\n

1 I even reported a few bugs and suggested some fixes in the original\n parse-js library, and Marijn pushed fixes literally in minutes.\n

\n
\n
\n\n
\n
\n
\n\n
\n

Date: 2011-12-09 14:59:08 EET

\n

Author: Mihai Bazon

\n

Org version 7.7 with Emacs version 23

\nValidate XHTML 1.0\n\n
\n\n\n", + "readmeFilename": "README.html", + "bugs": { + "url": "https://github.com/mishoo/UglifyJS/issues" + }, + "_id": "uglify-js@1.2.5", + "dist": { + "shasum": "1665070412612370d493d996c649f9e7fd35861a" + }, + "_from": "uglify-js@1.2.5", + "_resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-1.2.5.tgz" +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/beautify.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/beautify.js new file mode 100755 index 0000000..f19369e --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/beautify.js @@ -0,0 +1,28 @@ +#! /usr/bin/env node + +global.sys = require("sys"); +var fs = require("fs"); + +var jsp = require("../lib/parse-js"); +var pro = require("../lib/process"); + +var filename = process.argv[2]; +fs.readFile(filename, "utf8", function(err, text){ + try { + var ast = time_it("parse", function(){ return jsp.parse(text); }); + ast = time_it("mangle", function(){ return pro.ast_mangle(ast); }); + ast = time_it("squeeze", function(){ return pro.ast_squeeze(ast); }); + var gen = time_it("generate", function(){ return pro.gen_code(ast, false); }); + sys.puts(gen); + } catch(ex) { + sys.debug(ex.stack); + sys.debug(sys.inspect(ex)); + sys.debug(JSON.stringify(ex)); + } +}); + +function time_it(name, cont) { + var t1 = new Date().getTime(); + try { return cont(); } + finally { sys.debug("// " + name + ": " + ((new Date().getTime() - t1) / 1000).toFixed(3) + " sec."); } +}; diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/testparser.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/testparser.js new file mode 100755 index 0000000..02c19a9 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/testparser.js @@ -0,0 +1,403 @@ +#! /usr/bin/env node + +var parseJS = require("../lib/parse-js"); +var sys = require("sys"); + +// write debug in a very straightforward manner +var debug = function(){ + sys.log(Array.prototype.slice.call(arguments).join(', ')); +}; + +ParserTestSuite(function(i, input, desc){ + try { + parseJS.parse(input); + debug("ok " + i + ": " + desc); + } catch(e){ + debug("FAIL " + i + " " + desc + " (" + e + ")"); + } +}); + +function ParserTestSuite(callback){ + var inps = [ + ["var abc;", "Regular variable statement w/o assignment"], + ["var abc = 5;", "Regular variable statement with assignment"], + ["/* */;", "Multiline comment"], + ['/** **/;', 'Double star multiline comment'], + ["var f = function(){;};", "Function expression in var assignment"], + ['hi; // moo\n;', 'single line comment'], + ['var varwithfunction;', 'Dont match keywords as substrings'], // difference between `var withsomevar` and `"str"` (local search and lits) + ['a + b;', 'addition'], + ["'a';", 'single string literal'], + ["'a\\n';", 'single string literal with escaped return'], + ['"a";', 'double string literal'], + ['"a\\n";', 'double string literal with escaped return'], + ['"var";', 'string is a keyword'], + ['"variable";', 'string starts with a keyword'], + ['"somevariable";', 'string contains a keyword'], + ['"somevar";', 'string ends with a keyword'], + ['500;', 'int literal'], + ['500.;', 'float literal w/o decimals'], + ['500.432;', 'float literal with decimals'], + ['.432432;', 'float literal w/o int'], + ['(a,b,c);', 'parens and comma'], + ['[1,2,abc];', 'array literal'], + ['var o = {a:1};', 'object literal unquoted key'], + ['var o = {"b":2};', 'object literal quoted key'], // opening curly may not be at the start of a statement... + ['var o = {c:c};', 'object literal keyname is identifier'], + ['var o = {a:1,"b":2,c:c};', 'object literal combinations'], + ['var x;\nvar y;', 'two lines'], + ['var x;\nfunction n(){; }', 'function def'], + ['var x;\nfunction n(abc){; }', 'function def with arg'], + ['var x;\nfunction n(abc, def){ ;}', 'function def with args'], + ['function n(){ "hello"; }', 'function def with body'], + ['/a/;', 'regex literal'], + ['/a/b;', 'regex literal with flag'], + ['/a/ / /b/;', 'regex div regex'], + ['a/b/c;', 'triple division looks like regex'], + ['+function(){/regex/;};', 'regex at start of function body'], + // http://code.google.com/p/es-lab/source/browse/trunk/tests/parser/parsertests.js?r=86 + // http://code.google.com/p/es-lab/source/browse/trunk/tests/parser/parsertests.js?r=430 + + // first tests for the lexer, should also parse as program (when you append a semi) + + // comments + ['//foo!@#^&$1234\nbar;', 'single line comment'], + ['/* abcd!@#@$* { } && null*/;', 'single line multi line comment'], + ['/*foo\nbar*/;','multi line comment'], + ['/*x*x*/;','multi line comment with *'], + ['/**/;','empty comment'], + // identifiers + ["x;",'1 identifier'], + ["_x;",'2 identifier'], + ["xyz;",'3 identifier'], + ["$x;",'4 identifier'], + ["x$;",'5 identifier'], + ["_;",'6 identifier'], + ["x5;",'7 identifier'], + ["x_y;",'8 identifier'], + ["x+5;",'9 identifier'], + ["xyz123;",'10 identifier'], + ["x1y1z1;",'11 identifier'], + ["foo\\u00D8bar;",'12 identifier unicode escape'], + //["foo�bar;",'13 identifier unicode embedded (might fail)'], + // numbers + ["5;", '1 number'], + ["5.5;", '2 number'], + ["0;", '3 number'], + ["0.0;", '4 number'], + ["0.001;", '5 number'], + ["1.e2;", '6 number'], + ["1.e-2;", '7 number'], + ["1.E2;", '8 number'], + ["1.E-2;", '9 number'], + [".5;", '10 number'], + [".5e3;", '11 number'], + [".5e-3;", '12 number'], + ["0.5e3;", '13 number'], + ["55;", '14 number'], + ["123;", '15 number'], + ["55.55;", '16 number'], + ["55.55e10;", '17 number'], + ["123.456;", '18 number'], + ["1+e;", '20 number'], + ["0x01;", '22 number'], + ["0XCAFE;", '23 number'], + ["0x12345678;", '24 number'], + ["0x1234ABCD;", '25 number'], + ["0x0001;", '26 number'], + // strings + ["\"foo\";", '1 string'], + ["\'foo\';", '2 string'], + ["\"x\";", '3 string'], + ["\'\';", '4 string'], + ["\"foo\\tbar\";", '5 string'], + ["\"!@#$%^&*()_+{}[]\";", '6 string'], + ["\"/*test*/\";", '7 string'], + ["\"//test\";", '8 string'], + ["\"\\\\\";", '9 string'], + ["\"\\u0001\";", '10 string'], + ["\"\\uFEFF\";", '11 string'], + ["\"\\u10002\";", '12 string'], + ["\"\\x55\";", '13 string'], + ["\"\\x55a\";", '14 string'], + ["\"a\\\\nb\";", '15 string'], + ['";"', '16 string: semi in a string'], + ['"a\\\nb";', '17 string: line terminator escape'], + // literals + ["null;", "null"], + ["true;", "true"], + ["false;", "false"], + // regex + ["/a/;", "1 regex"], + ["/abc/;", "2 regex"], + ["/abc[a-z]*def/g;", "3 regex"], + ["/\\b/;", "4 regex"], + ["/[a-zA-Z]/;", "5 regex"], + + // program tests (for as far as they havent been covered above) + + // regexp + ["/foo(.*)/g;", "another regexp"], + // arrays + ["[];", "1 array"], + ["[ ];", "2 array"], + ["[1];", "3 array"], + ["[1,2];", "4 array"], + ["[1,2,,];", "5 array"], + ["[1,2,3];", "6 array"], + ["[1,2,3,,,];", "7 array"], + // objects + ["{};", "1 object"], + ["({x:5});", "2 object"], + ["({x:5,y:6});", "3 object"], + ["({x:5,});", "4 object"], + ["({if:5});", "5 object"], + ["({ get x() {42;} });", "6 object"], + ["({ set y(a) {1;} });", "7 object"], + // member expression + ["o.m;", "1 member expression"], + ["o['m'];", "2 member expression"], + ["o['n']['m'];", "3 member expression"], + ["o.n.m;", "4 member expression"], + ["o.if;", "5 member expression"], + // call and invoke expressions + ["f();", "1 call/invoke expression"], + ["f(x);", "2 call/invoke expression"], + ["f(x,y);", "3 call/invoke expression"], + ["o.m();", "4 call/invoke expression"], + ["o['m'];", "5 call/invoke expression"], + ["o.m(x);", "6 call/invoke expression"], + ["o['m'](x);", "7 call/invoke expression"], + ["o.m(x,y);", "8 call/invoke expression"], + ["o['m'](x,y);", "9 call/invoke expression"], + ["f(x)(y);", "10 call/invoke expression"], + ["f().x;", "11 call/invoke expression"], + + // eval + ["eval('x');", "1 eval"], + ["(eval)('x');", "2 eval"], + ["(1,eval)('x');", "3 eval"], + ["eval(x,y);", "4 eval"], + // new expression + ["new f();", "1 new expression"], + ["new o;", "2 new expression"], + ["new o.m;", "3 new expression"], + ["new o.m(x);", "4 new expression"], + ["new o.m(x,y);", "5 new expression"], + // prefix/postfix + ["++x;", "1 pre/postfix"], + ["x++;", "2 pre/postfix"], + ["--x;", "3 pre/postfix"], + ["x--;", "4 pre/postfix"], + ["x ++;", "5 pre/postfix"], + ["x /* comment */ ++;", "6 pre/postfix"], + ["++ /* comment */ x;", "7 pre/postfix"], + // unary operators + ["delete x;", "1 unary operator"], + ["void x;", "2 unary operator"], + ["+ x;", "3 unary operator"], + ["-x;", "4 unary operator"], + ["~x;", "5 unary operator"], + ["!x;", "6 unary operator"], + // meh + ["new Date++;", "new date ++"], + ["+x++;", " + x ++"], + // expression expressions + ["1 * 2;", "1 expression expressions"], + ["1 / 2;", "2 expression expressions"], + ["1 % 2;", "3 expression expressions"], + ["1 + 2;", "4 expression expressions"], + ["1 - 2;", "5 expression expressions"], + ["1 << 2;", "6 expression expressions"], + ["1 >>> 2;", "7 expression expressions"], + ["1 >> 2;", "8 expression expressions"], + ["1 * 2 + 3;", "9 expression expressions"], + ["(1+2)*3;", "10 expression expressions"], + ["1*(2+3);", "11 expression expressions"], + ["xy;", "13 expression expressions"], + ["x<=y;", "14 expression expressions"], + ["x>=y;", "15 expression expressions"], + ["x instanceof y;", "16 expression expressions"], + ["x in y;", "17 expression expressions"], + ["x&y;", "18 expression expressions"], + ["x^y;", "19 expression expressions"], + ["x|y;", "20 expression expressions"], + ["x+y>>= y;", "1 assignment"], + ["x <<= y;", "2 assignment"], + ["x = y;", "3 assignment"], + ["x += y;", "4 assignment"], + ["x /= y;", "5 assignment"], + // comma + ["x, y;", "comma"], + // block + ["{};", "1 block"], + ["{x;};", "2 block"], + ["{x;y;};", "3 block"], + // vars + ["var x;", "1 var"], + ["var x,y;", "2 var"], + ["var x=1,y=2;", "3 var"], + ["var x,y=2;", "4 var"], + // empty + [";", "1 empty"], + ["\n;", "2 empty"], + // expression statement + ["x;", "1 expression statement"], + ["5;", "2 expression statement"], + ["1+2;", "3 expression statement"], + // if + ["if (c) x; else y;", "1 if statement"], + ["if (c) x;", "2 if statement"], + ["if (c) {} else {};", "3 if statement"], + ["if (c1) if (c2) s1; else s2;", "4 if statement"], + // while + ["do s; while (e);", "1 while statement"], + ["do { s; } while (e);", "2 while statement"], + ["while (e) s;", "3 while statement"], + ["while (e) { s; };", "4 while statement"], + // for + ["for (;;) ;", "1 for statement"], + ["for (;c;x++) x;", "2 for statement"], + ["for (i;i> 1; +var c = 8 >>> 1; \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue34.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue34.js new file mode 100644 index 0000000..022f7a3 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue34.js @@ -0,0 +1,3 @@ +var a = {}; +a["this"] = 1; +a["that"] = 2; \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue4.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue4.js new file mode 100644 index 0000000..0b76103 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue4.js @@ -0,0 +1,3 @@ +var a = 2e3; +var b = 2e-3; +var c = 2e-5; \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue48.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue48.js new file mode 100644 index 0000000..031e85b --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue48.js @@ -0,0 +1 @@ +var s, i; s = ''; i = 0; \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue50.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue50.js new file mode 100644 index 0000000..060f9df --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue50.js @@ -0,0 +1,9 @@ +function bar(a) { + try { + foo(); + } catch(e) { + alert("Exception caught (foo not defined)"); + } + alert(a); // 10 in FF, "[object Error]" in IE +} +bar(10); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue53.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue53.js new file mode 100644 index 0000000..4f8b32f --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue53.js @@ -0,0 +1 @@ +x = (y, z) diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue54.1.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue54.1.js new file mode 100644 index 0000000..967052e --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue54.1.js @@ -0,0 +1,3 @@ +foo.toString(); +a.toString(16); +b.toString.call(c); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue68.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue68.js new file mode 100644 index 0000000..14054d0 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue68.js @@ -0,0 +1,5 @@ +function f() { + if (a) return; + g(); + function g(){} +}; diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue69.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue69.js new file mode 100644 index 0000000..d25ecd6 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue69.js @@ -0,0 +1 @@ +[(a,b)] diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue9.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue9.js new file mode 100644 index 0000000..6158861 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/issue9.js @@ -0,0 +1,4 @@ +var a = { + a: 1, + b: 2, // <-- trailing comma +}; diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/mangle.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/mangle.js new file mode 100644 index 0000000..c271a26 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/mangle.js @@ -0,0 +1,5 @@ +(function() { + var x = function fun(a, fun, b) { + return fun; + }; +}()); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/null_string.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/null_string.js new file mode 100644 index 0000000..a675b1c --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/null_string.js @@ -0,0 +1 @@ +var nullString = "\0" \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/strict-equals.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/strict-equals.js new file mode 100644 index 0000000..b631f4c --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/strict-equals.js @@ -0,0 +1,3 @@ +typeof a === 'string' +b + "" !== c + "" +d < e === f < g diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/var.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/var.js new file mode 100644 index 0000000..609a35d --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/var.js @@ -0,0 +1,3 @@ +// var declarations after each other should be combined +var a = 1; +var b = 2; \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/whitespace.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/whitespace.js new file mode 100644 index 0000000..6a15c46 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/whitespace.js @@ -0,0 +1,21 @@ +function id(a) { + // Form-Feed + // Vertical Tab + // No-Break Space + ᠎// Mongolian Vowel Separator +  // En quad +  // Em quad +  // En space +  // Em space +  // Three-Per-Em Space +  // Four-Per-Em Space +  // Six-Per-Em Space +  // Figure Space +  // Punctuation Space +  // Thin Space +  // Hair Space +  // Narrow No-Break Space +  // Medium Mathematical Space +  // Ideographic Space + return a; +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/with.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/with.js new file mode 100644 index 0000000..de266ed --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/compress/test/with.js @@ -0,0 +1,2 @@ +with({}) { +}; diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/scripts.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/scripts.js new file mode 100644 index 0000000..5d334ff --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/test/unit/scripts.js @@ -0,0 +1,55 @@ +var fs = require('fs'), + uglify = require('../../uglify-js'), + jsp = uglify.parser, + nodeunit = require('nodeunit'), + path = require('path'), + pro = uglify.uglify; + +var Script = process.binding('evals').Script; + +var scriptsPath = __dirname; + +function compress(code) { + var ast = jsp.parse(code); + ast = pro.ast_mangle(ast); + ast = pro.ast_squeeze(ast, { no_warnings: true }); + ast = pro.ast_squeeze_more(ast); + return pro.gen_code(ast); +}; + +var testDir = path.join(scriptsPath, "compress", "test"); +var expectedDir = path.join(scriptsPath, "compress", "expected"); + +function getTester(script) { + return function(test) { + var testPath = path.join(testDir, script); + var expectedPath = path.join(expectedDir, script); + var content = fs.readFileSync(testPath, 'utf-8'); + var outputCompress = compress(content); + + // Check if the noncompressdata is larger or same size as the compressed data + test.ok(content.length >= outputCompress.length); + + // Check that a recompress gives the same result + var outputReCompress = compress(content); + test.equal(outputCompress, outputReCompress); + + // Check if the compressed output is what is expected + var expected = fs.readFileSync(expectedPath, 'utf-8'); + test.equal(outputCompress, expected.replace(/(\r?\n)+$/, "")); + + test.done(); + }; +}; + +var tests = {}; + +var scripts = fs.readdirSync(testDir); +for (var i in scripts) { + var script = scripts[i]; + if (/\.js$/.test(script)) { + tests[script] = getTester(script); + } +} + +module.exports = nodeunit.testCase(tests); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/269.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/269.js new file mode 100644 index 0000000..256ad1c --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/269.js @@ -0,0 +1,13 @@ +var jsp = require("uglify-js").parser; +var pro = require("uglify-js").uglify; + +var test_code = "var JSON;JSON||(JSON={});"; + +var ast = jsp.parse(test_code, false, false); +var nonembed_token_code = pro.gen_code(ast); +ast = jsp.parse(test_code, false, true); +var embed_token_code = pro.gen_code(ast); + +console.log("original: " + test_code); +console.log("no token: " + nonembed_token_code); +console.log(" token: " + embed_token_code); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/app.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/app.js new file mode 100644 index 0000000..2c6257e --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/app.js @@ -0,0 +1,22315 @@ +/* Modernizr 2.0.6 (Custom Build) | MIT & BSD + * Build: http://www.modernizr.com/download/#-iepp + */ +;window.Modernizr=function(a,b,c){function w(a,b){return!!~(""+a).indexOf(b)}function v(a,b){return typeof a===b}function u(a,b){return t(prefixes.join(a+";")+(b||""))}function t(a){j.cssText=a}var d="2.0.6",e={},f=b.documentElement,g=b.head||b.getElementsByTagName("head")[0],h="modernizr",i=b.createElement(h),j=i.style,k,l=Object.prototype.toString,m={},n={},o={},p=[],q,r={}.hasOwnProperty,s;!v(r,c)&&!v(r.call,c)?s=function(a,b){return r.call(a,b)}:s=function(a,b){return b in a&&v(a.constructor.prototype[b],c)};for(var x in m)s(m,x)&&(q=x.toLowerCase(),e[q]=m[x](),p.push((e[q]?"":"no-")+q));t(""),i=k=null,a.attachEvent&&function(){var a=b.createElement("div");a.innerHTML="";return a.childNodes.length!==1}()&&function(a,b){function s(a){var b=-1;while(++b to avoid XSS via location.hash (#9521) + quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + trimLeft = /^\s+/, + trimRight = /\s+$/, + + // Check for digits + rdigit = /\d/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, + rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + + // Useragent RegExp + rwebkit = /(webkit)[ \/]([\w.]+)/, + ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, + rmsie = /(msie) ([\w.]+)/, + rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, + + // Matches dashed string for camelizing + rdashAlpha = /-([a-z]|[0-9])/ig, + rmsPrefix = /^-ms-/, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return ( letter + "" ).toUpperCase(); + }, + + // Keep a UserAgent string for use with jQuery.browser + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, + + // The deferred used on DOM ready + readyList, + + // The ready event handler + DOMContentLoaded, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwn = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + trim = String.prototype.trim, + indexOf = Array.prototype.indexOf, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context && document.body ) { + this.context = document; + this[0] = document.body; + this.selector = selector; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = quickExpr.exec( selector ); + } + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = (context ? context.ownerDocument || context : document); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { + ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); + selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes; + } + + return jQuery.merge( this, selector ); + + // HANDLE: $("#id") + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return (context || rootjQuery).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if (selector.selector !== undefined) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.6.3", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return slice.call( this, 0 ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = this.constructor(); + + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + jQuery.merge( ret, elems ); + } + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + (this.selector ? " " : "") + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Attach the listeners + jQuery.bindReady(); + + // Add the callback + readyList.done( fn ); + + return this; + }, + + eq: function( i ) { + return i === -1 ? + this.slice( i ) : + this.slice( i, +i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + // Either a released hold or an DOMready/load event and not yet ready + if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger( "ready" ).unbind( "ready" ); + } + } + }, + + bindReady: function() { + if ( readyList ) { + return; + } + + readyList = jQuery._Deferred(); + + // Catch cases where $(document).ready() is called after the + // browser event has already occurred. + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + return setTimeout( jQuery.ready, 1 ); + } + + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", DOMContentLoaded ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var toplevel = false; + + try { + toplevel = window.frameElement == null; + } catch(e) {} + + if ( document.documentElement.doScroll && toplevel ) { + doScrollCheck(); + } + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + // A crude way of determining if an object is a window + isWindow: function( obj ) { + return obj && typeof obj === "object" && "setInterval" in obj; + }, + + isNaN: function( obj ) { + return obj == null || !rdigit.test( obj ) || isNaN( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw msg; + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return (new Function( "return " + data ))(); + + } + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + var xml, tmp; + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && rnotwhite.test( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || jQuery.isFunction( object ); + + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { + break; + } + } + } + } + + return object; + }, + + // Use native String.trim function wherever possible + trim: trim ? + function( text ) { + return text == null ? + "" : + trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); + }, + + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; + + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // The extra typeof function check is to prevent crashes + // in Safari 2 (See: #3039) + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + var type = jQuery.type( array ); + + if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { + push.call( ret, array ); + } else { + jQuery.merge( ret, array ); + } + } + + return ret; + }, + + inArray: function( elem, array ) { + if ( !array ) { + return -1; + } + + if ( indexOf ) { + return indexOf.call( array, elem ); + } + + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === elem ) { + return i; + } + } + + return -1; + }, + + merge: function( first, second ) { + var i = first.length, + j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var ret = [], retVal; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, key, ret = [], + i = 0, + length = elems.length, + // jquery objects are treated as arrays + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( key in elems ) { + value = callback( elems[ key ], key, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + if ( typeof context === "string" ) { + var tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + var args = slice.call( arguments, 2 ), + proxy = function() { + return fn.apply( context, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + + return proxy; + }, + + // Mutifunctional method to get and set values to a collection + // The value/s can optionally be executed if it's a function + access: function( elems, key, value, exec, fn, pass ) { + var length = elems.length; + + // Setting many attributes + if ( typeof key === "object" ) { + for ( var k in key ) { + jQuery.access( elems, k, key[k], exec, fn, value ); + } + return elems; + } + + // Setting one attribute + if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = !pass && exec && jQuery.isFunction(value); + + for ( var i = 0; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + + return elems; + } + + // Getting an attribute + return length ? fn( elems[0], key ) : undefined; + }, + + now: function() { + return (new Date()).getTime(); + }, + + // Use of jQuery.browser is frowned upon. + // More details: http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function( ua ) { + ua = ua.toLowerCase(); + + var match = rwebkit.exec( ua ) || + ropera.exec( ua ) || + rmsie.exec( ua ) || + ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || + []; + + return { browser: match[1] || "", version: match[2] || "0" }; + }, + + sub: function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; + }, + + browser: {} +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +browserMatch = jQuery.uaMatch( userAgent ); +if ( browserMatch.browser ) { + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; +} + +// Deprecated, use jQuery.browser.webkit instead +if ( jQuery.browser.webkit ) { + jQuery.browser.safari = true; +} + +// IE doesn't match non-breaking spaces with \s +if ( rnotwhite.test( "\xA0" ) ) { + trimLeft = /^[\s\xA0]+/; + trimRight = /[\s\xA0]+$/; +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); + +// Cleanup functions for the document ready method +if ( document.addEventListener ) { + DOMContentLoaded = function() { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + }; + +} else if ( document.attachEvent ) { + DOMContentLoaded = function() { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; +} + +// The DOM ready check for Internet Explorer +function doScrollCheck() { + if ( jQuery.isReady ) { + return; + } + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch(e) { + setTimeout( doScrollCheck, 1 ); + return; + } + + // and execute any waiting functions + jQuery.ready(); +} + +return jQuery; + +})(); + + +var // Promise methods + promiseMethods = "done fail isResolved isRejected promise then always pipe".split( " " ), + // Static reference to slice + sliceDeferred = [].slice; + +jQuery.extend({ + // Create a simple deferred (one callbacks list) + _Deferred: function() { + var // callbacks list + callbacks = [], + // stored [ context , args ] + fired, + // to avoid firing when already doing so + firing, + // flag to know if the deferred has been cancelled + cancelled, + // the deferred itself + deferred = { + + // done( f1, f2, ...) + done: function() { + if ( !cancelled ) { + var args = arguments, + i, + length, + elem, + type, + _fired; + if ( fired ) { + _fired = fired; + fired = 0; + } + for ( i = 0, length = args.length; i < length; i++ ) { + elem = args[ i ]; + type = jQuery.type( elem ); + if ( type === "array" ) { + deferred.done.apply( deferred, elem ); + } else if ( type === "function" ) { + callbacks.push( elem ); + } + } + if ( _fired ) { + deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] ); + } + } + return this; + }, + + // resolve with given context and args + resolveWith: function( context, args ) { + if ( !cancelled && !fired && !firing ) { + // make sure args are available (#8421) + args = args || []; + firing = 1; + try { + while( callbacks[ 0 ] ) { + callbacks.shift().apply( context, args ); + } + } + finally { + fired = [ context, args ]; + firing = 0; + } + } + return this; + }, + + // resolve with this as context and given arguments + resolve: function() { + deferred.resolveWith( this, arguments ); + return this; + }, + + // Has this deferred been resolved? + isResolved: function() { + return !!( firing || fired ); + }, + + // Cancel + cancel: function() { + cancelled = 1; + callbacks = []; + return this; + } + }; + + return deferred; + }, + + // Full fledged deferred (two callbacks list) + Deferred: function( func ) { + var deferred = jQuery._Deferred(), + failDeferred = jQuery._Deferred(), + promise; + // Add errorDeferred methods, then and promise + jQuery.extend( deferred, { + then: function( doneCallbacks, failCallbacks ) { + deferred.done( doneCallbacks ).fail( failCallbacks ); + return this; + }, + always: function() { + return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments ); + }, + fail: failDeferred.done, + rejectWith: failDeferred.resolveWith, + reject: failDeferred.resolve, + isRejected: failDeferred.isResolved, + pipe: function( fnDone, fnFail ) { + return jQuery.Deferred(function( newDefer ) { + jQuery.each( { + done: [ fnDone, "resolve" ], + fail: [ fnFail, "reject" ] + }, function( handler, data ) { + var fn = data[ 0 ], + action = data[ 1 ], + returned; + if ( jQuery.isFunction( fn ) ) { + deferred[ handler ](function() { + returned = fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise().then( newDefer.resolve, newDefer.reject ); + } else { + newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); + } + }); + } else { + deferred[ handler ]( newDefer[ action ] ); + } + }); + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + if ( obj == null ) { + if ( promise ) { + return promise; + } + promise = obj = {}; + } + var i = promiseMethods.length; + while( i-- ) { + obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ]; + } + return obj; + } + }); + // Make sure only one callback list will be used + deferred.done( failDeferred.cancel ).fail( deferred.cancel ); + // Unexpose cancel + delete deferred.cancel; + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + return deferred; + }, + + // Deferred helper + when: function( firstParam ) { + var args = arguments, + i = 0, + length = args.length, + count = length, + deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? + firstParam : + jQuery.Deferred(); + function resolveFunc( i ) { + return function( value ) { + args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + if ( !( --count ) ) { + // Strange bug in FF4: + // Values changed onto the arguments object sometimes end up as undefined values + // outside the $.when method. Cloning the object into a fresh array solves the issue + deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) ); + } + }; + } + if ( length > 1 ) { + for( ; i < length; i++ ) { + if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) { + args[ i ].promise().then( resolveFunc(i), deferred.reject ); + } else { + --count; + } + } + if ( !count ) { + deferred.resolveWith( deferred, args ); + } + } else if ( deferred !== firstParam ) { + deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); + } + return deferred.promise(); + } +}); + + + +jQuery.support = (function() { + + var div = document.createElement( "div" ), + documentElement = document.documentElement, + all, + a, + select, + opt, + input, + marginDiv, + support, + fragment, + body, + testElementParent, + testElement, + testElementStyle, + tds, + events, + eventName, + i, + isSupported; + + // Preliminary tests + div.setAttribute("className", "t"); + div.innerHTML = "
a"; + + + all = div.getElementsByTagName( "*" ); + a = div.getElementsByTagName( "a" )[ 0 ]; + + // Can't get basic test support + if ( !all || !all.length || !a ) { + return {}; + } + + // First batch of supports tests + select = document.createElement( "select" ); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName( "input" )[ 0 ]; + + support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: ( div.firstChild.nodeType === 3 ), + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName( "tbody" ).length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName( "link" ).length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: ( a.getAttribute( "href" ) === "/a" ), + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.55$/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: ( input.value === "on" ), + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // Will be defined later + submitBubbles: true, + changeBubbles: true, + focusinBubbles: false, + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true + }; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent( "onclick", function() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + support.noCloneEvent = false; + }); + div.cloneNode( true ).fireEvent( "onclick" ); + } + + // Check if a radio maintains it's value + // after being appended to the DOM + input = document.createElement("input"); + input.value = "t"; + input.setAttribute("type", "radio"); + support.radioValue = input.value === "t"; + + input.setAttribute("checked", "checked"); + div.appendChild( input ); + fragment = document.createDocumentFragment(); + fragment.appendChild( div.firstChild ); + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + div.innerHTML = ""; + + // Figure out if the W3C box model works as expected + div.style.width = div.style.paddingLeft = "1px"; + + body = document.getElementsByTagName( "body" )[ 0 ]; + // We use our own, invisible, body unless the body is already present + // in which case we use a div (#9239) + testElement = document.createElement( body ? "div" : "body" ); + testElementStyle = { + visibility: "hidden", + width: 0, + height: 0, + border: 0, + margin: 0, + background: "none" + }; + if ( body ) { + jQuery.extend( testElementStyle, { + position: "absolute", + left: "-1000px", + top: "-1000px" + }); + } + for ( i in testElementStyle ) { + testElement.style[ i ] = testElementStyle[ i ]; + } + testElement.appendChild( div ); + testElementParent = body || documentElement; + testElementParent.insertBefore( testElement, testElementParent.firstChild ); + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + support.boxModel = div.offsetWidth === 2; + + if ( "zoom" in div.style ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.style.display = "inline"; + div.style.zoom = 1; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 ); + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = ""; + div.innerHTML = "
"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 2 ); + } + + div.innerHTML = "
t
"; + tds = div.getElementsByTagName( "td" ); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE < 8 fail this test) + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + div.innerHTML = ""; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + if ( document.defaultView && document.defaultView.getComputedStyle ) { + marginDiv = document.createElement( "div" ); + marginDiv.style.width = "0"; + marginDiv.style.marginRight = "0"; + div.appendChild( marginDiv ); + support.reliableMarginRight = + ( parseInt( ( document.defaultView.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; + } + + // Remove the body element we added + testElement.innerHTML = ""; + testElementParent.removeChild( testElement ); + + // Technique from Juriy Zaytsev + // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/ + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( div.attachEvent ) { + for( i in { + submit: 1, + change: 1, + focusin: 1 + } ) { + eventName = "on" + i; + isSupported = ( eventName in div ); + if ( !isSupported ) { + div.setAttribute( eventName, "return;" ); + isSupported = ( typeof div[ eventName ] === "function" ); + } + support[ i + "Bubbles" ] = isSupported; + } + } + + // Null connected elements to avoid leaks in IE + testElement = fragment = select = opt = body = marginDiv = div = input = null; + + return support; +})(); + +// Keep track of boxModel +jQuery.boxModel = jQuery.support.boxModel; + + + + +var rbrace = /^(?:\{.*\}|\[.*\])$/, + rmultiDash = /([a-z])([A-Z])/g; + +jQuery.extend({ + cache: {}, + + // Please use with caution + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, ret, + internalKey = jQuery.expando, + getByName = typeof name === "string", + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || (pvt && id && (cache[ id ] && !cache[ id ][ internalKey ]))) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ jQuery.expando ] = id = ++jQuery.uuid; + } else { + id = jQuery.expando; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery + // metadata on plain JS objects when the object is serialized using + // JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name); + } else { + cache[ id ] = jQuery.extend(cache[ id ], name); + } + } + + thisCache = cache[ id ]; + + // Internal jQuery data is stored in a separate object inside the object's data + // cache in order to avoid key collisions between internal data and user-defined + // data + if ( pvt ) { + if ( !thisCache[ internalKey ] ) { + thisCache[ internalKey ] = {}; + } + + thisCache = thisCache[ internalKey ]; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should + // not attempt to inspect the internal events object using jQuery.data, as this + // internal data object is undocumented and subject to change. + if ( name === "events" && !thisCache[name] ) { + return thisCache[ internalKey ] && thisCache[ internalKey ].events; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( getByName ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, + + // Reference to internal data cache key + internalKey = jQuery.expando, + + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + + // See jQuery.data for more information + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ]; + + if ( thisCache ) { + + // Support interoperable removal of hyphenated or camelcased keys + if ( !thisCache[ name ] ) { + name = jQuery.camelCase( name ); + } + + delete thisCache[ name ]; + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !isEmptyDataObject(thisCache) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( pvt ) { + delete cache[ id ][ internalKey ]; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject(cache[ id ]) ) { + return; + } + } + + var internalCache = cache[ id ][ internalKey ]; + + // Browsers that fail expando deletion also refuse to delete expandos on + // the window, but it will allow it on all other JS objects; other browsers + // don't care + // Ensure that `cache` is not a window object #10080 + if ( jQuery.support.deleteExpando || !cache.setInterval ) { + delete cache[ id ]; + } else { + cache[ id ] = null; + } + + // We destroyed the entire user cache at once because it's faster than + // iterating through each key, but we need to continue to persist internal + // data if it existed + if ( internalCache ) { + cache[ id ] = {}; + // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery + // metadata on plain JS objects when the object is serialized using + // JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + + cache[ id ][ internalKey ] = internalCache; + + // Otherwise, we need to eliminate the expando on the node to avoid + // false lookups in the cache for entries that no longer exist + } else if ( isNode ) { + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( jQuery.support.deleteExpando ) { + delete elem[ jQuery.expando ]; + } else if ( elem.removeAttribute ) { + elem.removeAttribute( jQuery.expando ); + } else { + elem[ jQuery.expando ] = null; + } + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + if ( elem.nodeName ) { + var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; + + if ( match ) { + return !(match === true || elem.getAttribute("classid") !== match); + } + } + + return true; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var data = null; + + if ( typeof key === "undefined" ) { + if ( this.length ) { + data = jQuery.data( this[0] ); + + if ( this[0].nodeType === 1 ) { + var attr = this[0].attributes, name; + for ( var i = 0, l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.substring(5) ); + + dataAttr( this[0], name, data[ name ] ); + } + } + } + } + + return data; + + } else if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + var parts = key.split("."); + parts[1] = parts[1] ? "." + parts[1] : ""; + + if ( value === undefined ) { + data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); + + // Try to fetch any internally stored data first + if ( data === undefined && this.length ) { + data = jQuery.data( this[0], key ); + data = dataAttr( this[0], key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + + } else { + return this.each(function() { + var $this = jQuery( this ), + args = [ parts[0], value ]; + + $this.triggerHandler( "setData" + parts[1] + "!", args ); + jQuery.data( this, key, value ); + $this.triggerHandler( "changeData" + parts[1] + "!", args ); + }); + } + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + var name = "data-" + key.replace( rmultiDash, "$1-$2" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + !jQuery.isNaN( data ) ? parseFloat( data ) : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// TODO: This is a hack for 1.5 ONLY to allow objects with a single toJSON +// property to be considered empty objects; this property always exists in +// order to make sure JSON.stringify does not expose internal metadata +function isEmptyDataObject( obj ) { + for ( var name in obj ) { + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + + + + +function handleQueueMarkDefer( elem, type, src ) { + var deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + defer = jQuery.data( elem, deferDataKey, undefined, true ); + if ( defer && + ( src === "queue" || !jQuery.data( elem, queueDataKey, undefined, true ) ) && + ( src === "mark" || !jQuery.data( elem, markDataKey, undefined, true ) ) ) { + // Give room for hard-coded callbacks to fire first + // and eventually mark/queue something else on the element + setTimeout( function() { + if ( !jQuery.data( elem, queueDataKey, undefined, true ) && + !jQuery.data( elem, markDataKey, undefined, true ) ) { + jQuery.removeData( elem, deferDataKey, true ); + defer.resolve(); + } + }, 0 ); + } +} + +jQuery.extend({ + + _mark: function( elem, type ) { + if ( elem ) { + type = (type || "fx") + "mark"; + jQuery.data( elem, type, (jQuery.data(elem,type,undefined,true) || 0) + 1, true ); + } + }, + + _unmark: function( force, elem, type ) { + if ( force !== true ) { + type = elem; + elem = force; + force = false; + } + if ( elem ) { + type = type || "fx"; + var key = type + "mark", + count = force ? 0 : ( (jQuery.data( elem, key, undefined, true) || 1 ) - 1 ); + if ( count ) { + jQuery.data( elem, key, count, true ); + } else { + jQuery.removeData( elem, key, true ); + handleQueueMarkDefer( elem, type, "mark" ); + } + } + }, + + queue: function( elem, type, data ) { + if ( elem ) { + type = (type || "fx") + "queue"; + var q = jQuery.data( elem, type, undefined, true ); + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !q || jQuery.isArray(data) ) { + q = jQuery.data( elem, type, jQuery.makeArray(data), true ); + } else { + q.push( data ); + } + } + return q || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + fn = queue.shift(), + defer; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + } + + if ( fn ) { + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift("inprogress"); + } + + fn.call(elem, function() { + jQuery.dequeue(elem, type); + }); + } + + if ( !queue.length ) { + jQuery.removeData( elem, type + "queue", true ); + handleQueueMarkDefer( elem, type, "queue" ); + } + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + } + + if ( data === undefined ) { + return jQuery.queue( this[0], type ); + } + return this.each(function() { + var queue = jQuery.queue( this, type, data ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[time] || time : time; + type = type || "fx"; + + return this.queue( type, function() { + var elem = this; + setTimeout(function() { + jQuery.dequeue( elem, type ); + }, time ); + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, object ) { + if ( typeof type !== "string" ) { + object = type; + type = undefined; + } + type = type || "fx"; + var defer = jQuery.Deferred(), + elements = this, + i = elements.length, + count = 1, + deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + tmp; + function resolve() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + } + while( i-- ) { + if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || + ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || + jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && + jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) { + count++; + tmp.done( resolve ); + } + } + resolve(); + return defer.promise(); + } +}); + + + + +var rclass = /[\n\t\r]/g, + rspace = /\s+/, + rreturn = /\r/g, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea)?$/i, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + nodeHook, boolHook; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.attr ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.prop ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classNames, i, l, elem, + setClass, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call(this, j, this.className) ); + }); + } + + if ( value && typeof value === "string" ) { + classNames = value.split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className && classNames.length === 1 ) { + elem.className = value; + + } else { + setClass = " " + elem.className + " "; + + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { + setClass += classNames[ c ] + " "; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classNames, i, l, elem, className, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call(this, j, this.className) ); + }); + } + + if ( (value && typeof value === "string") || value === undefined ) { + classNames = (value || "").split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + className = (" " + elem.className + " ").replace( rclass, " " ); + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[ c ] + " ", " "); + } + elem.className = jQuery.trim( className ); + + } else { + elem.className = ""; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space seperated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " "; + for ( var i = 0, l = this.length; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var hooks, ret, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return undefined; + } + + var isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var self = jQuery(this), val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, + index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type === "select-one"; + + // Nothing was selected + if ( index < 0 ) { + return null; + } + + // Loop through all the selected options + for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { + var option = options[ i ]; + + // Don't return options that are disabled or in a disabled optgroup + if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && + (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + // Fixes Bug #2551 -- select.val() broken in IE after form.reset() + if ( one && !values.length && options.length ) { + return jQuery( options[ index ] ).val(); + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attrFn: { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true + }, + + attrFix: { + // Always normalize to ensure hook usage + tabindex: "tabIndex" + }, + + attr: function( elem, name, value, pass ) { + var nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return undefined; + } + + if ( pass && name in jQuery.attrFn ) { + return jQuery( elem )[ name ]( value ); + } + + // Fallback to prop when attributes are not supported + if ( !("getAttribute" in elem) ) { + return jQuery.prop( elem, name, value ); + } + + var ret, hooks, + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // Normalize the name if needed + if ( notxml ) { + name = jQuery.attrFix[ name ] || name; + + hooks = jQuery.attrHooks[ name ]; + + if ( !hooks ) { + // Use boolHook for boolean attributes + if ( rboolean.test( name ) ) { + hooks = boolHook; + + // Use nodeHook if available( IE6/7 ) + } else if ( nodeHook ) { + hooks = nodeHook; + } + } + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return undefined; + + } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, "" + value ); + return value; + } + + } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + + ret = elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, name ) { + var propName; + if ( elem.nodeType === 1 ) { + name = jQuery.attrFix[ name ] || name; + + jQuery.attr( elem, name, "" ); + elem.removeAttribute( name ); + + // Set corresponding property to false for boolean attributes + if ( rboolean.test( name ) && (propName = jQuery.propFix[ name ] || name) in elem ) { + elem[ propName ] = false; + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to it's default in case type is set after value + // This is for element creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + }, + // Use the value property for back compat + // Use the nodeHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return undefined; + } + + var ret, hooks, + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return (elem[ name ] = value); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabindex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + } + } +}); + +// Add the tabindex propHook to attrHooks for back-compat +jQuery.attrHooks.tabIndex = jQuery.propHooks.tabIndex; + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + // Fall back to attribute presence where some booleans are not supported + var attrNode; + return jQuery.prop( elem, name ) === true || ( attrNode = elem.getAttributeNode( name ) ) && attrNode.nodeValue !== false ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = true; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } +}; + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !jQuery.support.getSetAttribute ) { + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret; + ret = elem.getAttributeNode( name ); + // Return undefined if nodeValue is empty string + return ret && ret.nodeValue !== "" ? + ret.nodeValue : + undefined; + }, + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + ret = document.createAttribute( name ); + elem.setAttributeNode( ret ); + } + return (ret.nodeValue = value + ""); + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); +} + + +// Some attributes require a special call on IE +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret === null ? undefined : ret; + } + }); + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Normalize to lowercase since IE uppercases css property names + return elem.style.cssText.toLowerCase() || undefined; + }, + set: function( elem, value ) { + return (elem.style.cssText = "" + value); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }); +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return (elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0); + } + } + }); +}); + + + + +var rnamespaces = /\.(.*)$/, + rformElems = /^(?:textarea|input|select)$/i, + rperiod = /\./g, + rspaces = / /g, + rescape = /[^\w\s.|`]/g, + fcleanup = function( nm ) { + return nm.replace(rescape, "\\$&"); + }; + +/* + * A number of helper functions used for managing events. + * Many of the ideas behind this code originated from + * Dean Edwards' addEvent library. + */ +jQuery.event = { + + // Bind an event to an element + // Original by Dean Edwards + add: function( elem, types, handler, data ) { + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + if ( handler === false ) { + handler = returnFalse; + } else if ( !handler ) { + // Fixes bug #7229. Fix recommended by jdalton + return; + } + + var handleObjIn, handleObj; + + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + } + + // Make sure that the function being executed has a unique ID + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure + var elemData = jQuery._data( elem ); + + // If no elemData is found then we must be trying to bind to one of the + // banned noData elements + if ( !elemData ) { + return; + } + + var events = elemData.events, + eventHandle = elemData.handle; + + if ( !events ) { + elemData.events = events = {}; + } + + if ( !eventHandle ) { + elemData.handle = eventHandle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.handle.apply( eventHandle.elem, arguments ) : + undefined; + }; + } + + // Add elem as a property of the handle function + // This is to prevent a memory leak with non-native events in IE. + eventHandle.elem = elem; + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = types.split(" "); + + var type, i = 0, namespaces; + + while ( (type = types[ i++ ]) ) { + handleObj = handleObjIn ? + jQuery.extend({}, handleObjIn) : + { handler: handler, data: data }; + + // Namespaced event handlers + if ( type.indexOf(".") > -1 ) { + namespaces = type.split("."); + type = namespaces.shift(); + handleObj.namespace = namespaces.slice(0).sort().join("."); + + } else { + namespaces = []; + handleObj.namespace = ""; + } + + handleObj.type = type; + if ( !handleObj.guid ) { + handleObj.guid = handler.guid; + } + + // Get the current list of functions bound to this event + var handlers = events[ type ], + special = jQuery.event.special[ type ] || {}; + + // Init the event handler queue + if ( !handlers ) { + handlers = events[ type ] = []; + + // Check for a special event handler + // Only use addEventListener/attachEvent if the special + // events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add the function to the element's handler list + handlers.push( handleObj ); + + // Keep track of which events have been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, pos ) { + // don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + if ( handler === false ) { + handler = returnFalse; + } + + var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ), + events = elemData && elemData.events; + + if ( !elemData || !events ) { + return; + } + + // types is actually an event object here + if ( types && types.type ) { + handler = types.handler; + types = types.type; + } + + // Unbind all events for the element + if ( !types || typeof types === "string" && types.charAt(0) === "." ) { + types = types || ""; + + for ( type in events ) { + jQuery.event.remove( elem, type + types ); + } + + return; + } + + // Handle multiple events separated by a space + // jQuery(...).unbind("mouseover mouseout", fn); + types = types.split(" "); + + while ( (type = types[ i++ ]) ) { + origType = type; + handleObj = null; + all = type.indexOf(".") < 0; + namespaces = []; + + if ( !all ) { + // Namespaced event handlers + namespaces = type.split("."); + type = namespaces.shift(); + + namespace = new RegExp("(^|\\.)" + + jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)"); + } + + eventType = events[ type ]; + + if ( !eventType ) { + continue; + } + + if ( !handler ) { + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( all || namespace.test( handleObj.namespace ) ) { + jQuery.event.remove( elem, origType, handleObj.handler, j ); + eventType.splice( j--, 1 ); + } + } + + continue; + } + + special = jQuery.event.special[ type ] || {}; + + for ( j = pos || 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( handler.guid === handleObj.guid ) { + // remove the given handler for the given type + if ( all || namespace.test( handleObj.namespace ) ) { + if ( pos == null ) { + eventType.splice( j--, 1 ); + } + + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + + if ( pos != null ) { + break; + } + } + } + + // remove generic event handler if no more handlers exist + if ( eventType.length === 0 || pos != null && eventType.length === 1 ) { + if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + ret = null; + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + var handle = elemData.handle; + if ( handle ) { + handle.elem = null; + } + + delete elemData.events; + delete elemData.handle; + + if ( jQuery.isEmptyObject( elemData ) ) { + jQuery.removeData( elem, undefined, true ); + } + } + }, + + // Events that are safe to short-circuit if no handlers are attached. + // Native DOM events should not be added, they may have inline handlers. + customEvent: { + "getData": true, + "setData": true, + "changeData": true + }, + + trigger: function( event, data, elem, onlyHandlers ) { + // Event object or event type + var type = event.type || event, + namespaces = [], + exclusive; + + if ( type.indexOf("!") >= 0 ) { + // Exclusive events trigger only for the exact event (no namespaces) + type = type.slice(0, -1); + exclusive = true; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + + if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { + // No jQuery handlers for this event type, and it can't have inline handlers + return; + } + + // Caller can pass in an Event, Object, or just an event type string + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + new jQuery.Event( type, event ) : + // Just the event type (string) + new jQuery.Event( type ); + + event.type = type; + event.exclusive = exclusive; + event.namespace = namespaces.join("."); + event.namespace_re = new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)"); + + // triggerHandler() and global events don't bubble or run the default action + if ( onlyHandlers || !elem ) { + event.preventDefault(); + event.stopPropagation(); + } + + // Handle a global trigger + if ( !elem ) { + // TODO: Stop taunting the data cache; remove global events and always attach to document + jQuery.each( jQuery.cache, function() { + // internalKey variable is just used to make it easier to find + // and potentially change this stuff later; currently it just + // points to jQuery.expando + var internalKey = jQuery.expando, + internalCache = this[ internalKey ]; + if ( internalCache && internalCache.events && internalCache.events[ type ] ) { + jQuery.event.trigger( event, data, internalCache.handle.elem ); + } + }); + return; + } + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // Clean up the event in case it is being reused + event.result = undefined; + event.target = elem; + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data != null ? jQuery.makeArray( data ) : []; + data.unshift( event ); + + var cur = elem, + // IE doesn't like method names with a colon (#3533, #8272) + ontype = type.indexOf(":") < 0 ? "on" + type : ""; + + // Fire event on the current element, then bubble up the DOM tree + do { + var handle = jQuery._data( cur, "handle" ); + + event.currentTarget = cur; + if ( handle ) { + handle.apply( cur, data ); + } + + // Trigger an inline bound script + if ( ontype && jQuery.acceptData( cur ) && cur[ ontype ] && cur[ ontype ].apply( cur, data ) === false ) { + event.result = false; + event.preventDefault(); + } + + // Bubble up to document, then to window + cur = cur.parentNode || cur.ownerDocument || cur === event.target.ownerDocument && window; + } while ( cur && !event.isPropagationStopped() ); + + // If nobody prevented the default action, do it now + if ( !event.isDefaultPrevented() ) { + var old, + special = jQuery.event.special[ type ] || {}; + + if ( (!special._default || special._default.call( elem.ownerDocument, event ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction)() check here because IE6/7 fails that test. + // IE<9 dies on focus to hidden element (#1486), may want to revisit a try/catch. + try { + if ( ontype && elem[ type ] ) { + // Don't re-trigger an onFOO event when we call its FOO() method + old = elem[ ontype ]; + + if ( old ) { + elem[ ontype ] = null; + } + + jQuery.event.triggered = type; + elem[ type ](); + } + } catch ( ieError ) {} + + if ( old ) { + elem[ ontype ] = old; + } + + jQuery.event.triggered = undefined; + } + } + + return event.result; + }, + + handle: function( event ) { + event = jQuery.event.fix( event || window.event ); + // Snapshot the handlers list since a called handler may add/remove events. + var handlers = ((jQuery._data( this, "events" ) || {})[ event.type ] || []).slice(0), + run_all = !event.exclusive && !event.namespace, + args = Array.prototype.slice.call( arguments, 0 ); + + // Use the fix-ed Event rather than the (read-only) native event + args[0] = event; + event.currentTarget = this; + + for ( var j = 0, l = handlers.length; j < l; j++ ) { + var handleObj = handlers[ j ]; + + // Triggered event must 1) be non-exclusive and have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event. + if ( run_all || event.namespace_re.test( handleObj.namespace ) ) { + // Pass in a reference to the handler function itself + // So that we can later remove it + event.handler = handleObj.handler; + event.data = handleObj.data; + event.handleObj = handleObj; + + var ret = handleObj.handler.apply( this, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + + if ( event.isImmediatePropagationStopped() ) { + break; + } + } + } + return event.result; + }, + + props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // store a copy of the original event object + // and "clone" to set read-only properties + var originalEvent = event; + event = jQuery.Event( originalEvent ); + + for ( var i = this.props.length, prop; i; ) { + prop = this.props[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary + if ( !event.target ) { + // Fixes #1925 where srcElement might not be defined either + event.target = event.srcElement || document; + } + + // check if target is a textnode (safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && event.fromElement ) { + event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement; + } + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && event.clientX != null ) { + var eventDocument = event.target.ownerDocument || document, + doc = eventDocument.documentElement, + body = eventDocument.body; + + event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); + event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); + } + + // Add which for key events + if ( event.which == null && (event.charCode != null || event.keyCode != null) ) { + event.which = event.charCode != null ? event.charCode : event.keyCode; + } + + // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) + if ( !event.metaKey && event.ctrlKey ) { + event.metaKey = event.ctrlKey; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && event.button !== undefined ) { + event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) )); + } + + return event; + }, + + // Deprecated, use jQuery.guid instead + guid: 1E8, + + // Deprecated, use jQuery.proxy instead + proxy: jQuery.proxy, + + special: { + ready: { + // Make sure the ready event is setup + setup: jQuery.bindReady, + teardown: jQuery.noop + }, + + live: { + add: function( handleObj ) { + jQuery.event.add( this, + liveConvert( handleObj.origType, handleObj.selector ), + jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); + }, + + remove: function( handleObj ) { + jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj ); + } + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + if ( elem.detachEvent ) { + elem.detachEvent( "on" + type, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !this.preventDefault ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // timeStamp is buggy for some events on Firefox(#3843) + // So we won't rely on the native value + this.timeStamp = jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Checks if an event happened on an element within another element +// Used in jQuery.event.special.mouseenter and mouseleave handlers +var withinElement = function( event ) { + + // Check if mouse(over|out) are still within the same parent element + var related = event.relatedTarget, + inside = false, + eventType = event.type; + + event.type = event.data; + + if ( related !== this ) { + + if ( related ) { + inside = jQuery.contains( this, related ); + } + + if ( !inside ) { + + jQuery.event.handle.apply( this, arguments ); + + event.type = eventType; + } + } +}, + +// In case of event delegation, we only need to rename the event.type, +// liveHandler will take care of the rest. +delegate = function( event ) { + event.type = event.data; + jQuery.event.handle.apply( this, arguments ); +}; + +// Create mouseenter and mouseleave events +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + setup: function( data ) { + jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig ); + }, + teardown: function( data ) { + jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement ); + } + }; +}); + +// submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function( data, namespaces ) { + if ( !jQuery.nodeName( this, "form" ) ) { + jQuery.event.add(this, "click.specialSubmit", function( e ) { + var elem = e.target, + type = jQuery.nodeName( elem, "input" ) ? elem.type : ""; + + if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) { + trigger( "submit", this, arguments ); + } + }); + + jQuery.event.add(this, "keypress.specialSubmit", function( e ) { + var elem = e.target, + type = jQuery.nodeName( elem, "input" ) ? elem.type : ""; + + if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) { + trigger( "submit", this, arguments ); + } + }); + + } else { + return false; + } + }, + + teardown: function( namespaces ) { + jQuery.event.remove( this, ".specialSubmit" ); + } + }; + +} + +// change delegation, happens here so we have bind. +if ( !jQuery.support.changeBubbles ) { + + var changeFilters, + + getVal = function( elem ) { + var type = jQuery.nodeName( elem, "input" ) ? elem.type : "", + val = elem.value; + + if ( type === "radio" || type === "checkbox" ) { + val = elem.checked; + + } else if ( type === "select-multiple" ) { + val = elem.selectedIndex > -1 ? + jQuery.map( elem.options, function( elem ) { + return elem.selected; + }).join("-") : + ""; + + } else if ( jQuery.nodeName( elem, "select" ) ) { + val = elem.selectedIndex; + } + + return val; + }, + + testChange = function testChange( e ) { + var elem = e.target, data, val; + + if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) { + return; + } + + data = jQuery._data( elem, "_change_data" ); + val = getVal(elem); + + // the current data will be also retrieved by beforeactivate + if ( e.type !== "focusout" || elem.type !== "radio" ) { + jQuery._data( elem, "_change_data", val ); + } + + if ( data === undefined || val === data ) { + return; + } + + if ( data != null || val ) { + e.type = "change"; + e.liveFired = undefined; + jQuery.event.trigger( e, arguments[1], elem ); + } + }; + + jQuery.event.special.change = { + filters: { + focusout: testChange, + + beforedeactivate: testChange, + + click: function( e ) { + var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : ""; + + if ( type === "radio" || type === "checkbox" || jQuery.nodeName( elem, "select" ) ) { + testChange.call( this, e ); + } + }, + + // Change has to be called before submit + // Keydown will be called before keypress, which is used in submit-event delegation + keydown: function( e ) { + var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : ""; + + if ( (e.keyCode === 13 && !jQuery.nodeName( elem, "textarea" ) ) || + (e.keyCode === 32 && (type === "checkbox" || type === "radio")) || + type === "select-multiple" ) { + testChange.call( this, e ); + } + }, + + // Beforeactivate happens also before the previous element is blurred + // with this event you can't trigger a change event, but you can store + // information + beforeactivate: function( e ) { + var elem = e.target; + jQuery._data( elem, "_change_data", getVal(elem) ); + } + }, + + setup: function( data, namespaces ) { + if ( this.type === "file" ) { + return false; + } + + for ( var type in changeFilters ) { + jQuery.event.add( this, type + ".specialChange", changeFilters[type] ); + } + + return rformElems.test( this.nodeName ); + }, + + teardown: function( namespaces ) { + jQuery.event.remove( this, ".specialChange" ); + + return rformElems.test( this.nodeName ); + } + }; + + changeFilters = jQuery.event.special.change.filters; + + // Handle when the input is .focus()'d + changeFilters.focus = changeFilters.beforeactivate; +} + +function trigger( type, elem, args ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + // Don't pass args or remember liveFired; they apply to the donor event. + var event = jQuery.extend( {}, args[ 0 ] ); + event.type = type; + event.originalEvent = {}; + event.liveFired = undefined; + jQuery.event.handle.call( elem, event ); + if ( event.isDefaultPrevented() ) { + args[ 0 ].preventDefault(); + } +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + + function handler( donor ) { + // Donor event is always a native one; fix it and switch its type. + // Let focusin/out handler cancel the donor focus/blur event. + var e = jQuery.event.fix( donor ); + e.type = fix; + e.originalEvent = {}; + jQuery.event.trigger( e, null, e.target ); + if ( e.isDefaultPrevented() ) { + donor.preventDefault(); + } + } + }); +} + +jQuery.each(["bind", "one"], function( i, name ) { + jQuery.fn[ name ] = function( type, data, fn ) { + var handler; + + // Handle object literals + if ( typeof type === "object" ) { + for ( var key in type ) { + this[ name ](key, data, type[key], fn); + } + return this; + } + + if ( arguments.length === 2 || data === false ) { + fn = data; + data = undefined; + } + + if ( name === "one" ) { + handler = function( event ) { + jQuery( this ).unbind( event, handler ); + return fn.apply( this, arguments ); + }; + handler.guid = fn.guid || jQuery.guid++; + } else { + handler = fn; + } + + if ( type === "unload" && name !== "one" ) { + this.one( type, data, fn ); + + } else { + for ( var i = 0, l = this.length; i < l; i++ ) { + jQuery.event.add( this[i], type, handler, data ); + } + } + + return this; + }; +}); + +jQuery.fn.extend({ + unbind: function( type, fn ) { + // Handle object literals + if ( typeof type === "object" && !type.preventDefault ) { + for ( var key in type ) { + this.unbind(key, type[key]); + } + + } else { + for ( var i = 0, l = this.length; i < l; i++ ) { + jQuery.event.remove( this[i], type, fn ); + } + } + + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.live( types, data, fn, selector ); + }, + + undelegate: function( selector, types, fn ) { + if ( arguments.length === 0 ) { + return this.unbind( "live" ); + + } else { + return this.die( types, null, fn, selector ); + } + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + + triggerHandler: function( type, data ) { + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +var liveMap = { + focus: "focusin", + blur: "focusout", + mouseenter: "mouseover", + mouseleave: "mouseout" +}; + +jQuery.each(["live", "die"], function( i, name ) { + jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) { + var type, i = 0, match, namespaces, preType, + selector = origSelector || this.selector, + context = origSelector ? this : jQuery( this.context ); + + if ( typeof types === "object" && !types.preventDefault ) { + for ( var key in types ) { + context[ name ]( key, data, types[key], selector ); + } + + return this; + } + + if ( name === "die" && !types && + origSelector && origSelector.charAt(0) === "." ) { + + context.unbind( origSelector ); + + return this; + } + + if ( data === false || jQuery.isFunction( data ) ) { + fn = data || returnFalse; + data = undefined; + } + + types = (types || "").split(" "); + + while ( (type = types[ i++ ]) != null ) { + match = rnamespaces.exec( type ); + namespaces = ""; + + if ( match ) { + namespaces = match[0]; + type = type.replace( rnamespaces, "" ); + } + + if ( type === "hover" ) { + types.push( "mouseenter" + namespaces, "mouseleave" + namespaces ); + continue; + } + + preType = type; + + if ( liveMap[ type ] ) { + types.push( liveMap[ type ] + namespaces ); + type = type + namespaces; + + } else { + type = (liveMap[ type ] || type) + namespaces; + } + + if ( name === "live" ) { + // bind live handler + for ( var j = 0, l = context.length; j < l; j++ ) { + jQuery.event.add( context[j], "live." + liveConvert( type, selector ), + { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } ); + } + + } else { + // unbind live handler + context.unbind( "live." + liveConvert( type, selector ), fn ); + } + } + + return this; + }; +}); + +function liveHandler( event ) { + var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret, + elems = [], + selectors = [], + events = jQuery._data( this, "events" ); + + // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911) + if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) { + return; + } + + if ( event.namespace ) { + namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)"); + } + + event.liveFired = this; + + var live = events.live.slice(0); + + for ( j = 0; j < live.length; j++ ) { + handleObj = live[j]; + + if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) { + selectors.push( handleObj.selector ); + + } else { + live.splice( j--, 1 ); + } + } + + match = jQuery( event.target ).closest( selectors, event.currentTarget ); + + for ( i = 0, l = match.length; i < l; i++ ) { + close = match[i]; + + for ( j = 0; j < live.length; j++ ) { + handleObj = live[j]; + + if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) && !close.elem.disabled ) { + elem = close.elem; + related = null; + + // Those two events require additional checking + if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) { + event.type = handleObj.preType; + related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0]; + + // Make sure not to accidentally match a child element with the same selector + if ( related && jQuery.contains( elem, related ) ) { + related = elem; + } + } + + if ( !related || related !== elem ) { + elems.push({ elem: elem, handleObj: handleObj, level: close.level }); + } + } + } + } + + for ( i = 0, l = elems.length; i < l; i++ ) { + match = elems[i]; + + if ( maxLevel && match.level > maxLevel ) { + break; + } + + event.currentTarget = match.elem; + event.data = match.handleObj.data; + event.handleObj = match.handleObj; + + ret = match.handleObj.origHandler.apply( match.elem, arguments ); + + if ( ret === false || event.isPropagationStopped() ) { + maxLevel = match.level; + + if ( ret === false ) { + stop = false; + } + if ( event.isImmediatePropagationStopped() ) { + break; + } + } + } + + return stop; +} + +function liveConvert( type, selector ) { + return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspaces, "&"); +} + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; + + if ( jQuery.attrFn ) { + jQuery.attrFn[ name ] = true; + } +}); + + + +/*! + * Sizzle CSS Selector Engine + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true, + rBackslash = /\\/g, + rNonWord = /\W/; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function() { + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function( selector, context, results, seed ) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var m, set, checkSet, extra, ret, cur, pop, i, + prune = true, + contextXML = Sizzle.isXML( context ), + parts = [], + soFar = selector; + + // Reset the position of the chunker regexp (start from head) + do { + chunker.exec( "" ); + m = chunker.exec( soFar ); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context ); + + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set ); + } + } + + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? + Sizzle.filter( ret.expr, ret.set )[0] : + ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + + set = ret.expr ? + Sizzle.filter( ret.expr, ret.set ) : + ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray( set ); + + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function( results ) { + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + } + + return results; +}; + +Sizzle.matches = function( expr, set ) { + return Sizzle( expr, null, null, set ); +}; + +Sizzle.matchesSelector = function( node, expr ) { + return Sizzle( expr, null, null, [node] ).length > 0; +}; + +Sizzle.find = function( expr, context, isXML ) { + var set; + + if ( !expr ) { + return []; + } + + for ( var i = 0, l = Expr.order.length; i < l; i++ ) { + var match, + type = Expr.order[i]; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + var left = match[1]; + match.splice( 1, 1 ); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace( rBackslash, "" ); + set = Expr.find[ type ]( match, context, isXML ); + + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( "*" ) : + []; + } + + return { set: set, expr: expr }; +}; + +Sizzle.filter = function( expr, set, inplace, not ) { + var match, anyFound, + old = expr, + result = [], + curLoop = set, + isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); + + while ( expr && set.length ) { + for ( var type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + var found, item, + filter = Expr.filter[ type ], + left = match[1]; + + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( var i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + var pass = not ^ !!found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + + } else { + curLoop[i] = false; + } + + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw "Syntax error, unrecognized expression: " + msg; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + + leftMatch: {}, + + attrMap: { + "class": "className", + "for": "htmlFor" + }, + + attrHandle: { + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + type: function( elem ) { + return elem.getAttribute( "type" ); + } + }, + + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !rNonWord.test( part ), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + + ">": function( checkSet, part ) { + var elem, + isPartStr = typeof part === "string", + i = 0, + l = checkSet.length; + + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + + "": function(checkSet, part, isXML){ + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + "~": function( checkSet, part, isXML ) { + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } + }, + + find: { + ID: function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }, + + NAME: function( match, context ) { + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], + results = context.getElementsByName( match[1] ); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + + TAG: function( match, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( match[1] ); + } + } + }, + preFilter: { + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + match = " " + match[1].replace( rBackslash, "" ) + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + + ID: function( match ) { + return match[1].replace( rBackslash, "" ); + }, + + TAG: function( match, curLoop ) { + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, + + CHILD: function( match ) { + if ( match[1] === "nth" ) { + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + match[2] = match[2].replace(/^\+|\s*/g, ''); + + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + var name = match[1] = match[1].replace( rBackslash, "" ); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + // Handle if an un-quoted value was used + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + + PSEUDO: function( match, curLoop, inplace, result, not ) { + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + + if ( !inplace ) { + result.push.apply( result, ret ); + } + + return false; + } + + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + + POS: function( match ) { + match.unshift( true ); + + return match; + } + }, + + filters: { + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + }, + + disabled: function( elem ) { + return elem.disabled === true; + }, + + checked: function( elem ) { + return elem.checked === true; + }, + + selected: function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + parent: function( elem ) { + return !!elem.firstChild; + }, + + empty: function( elem ) { + return !elem.firstChild; + }, + + has: function( elem, i, match ) { + return !!Sizzle( match[3], elem ).length; + }, + + header: function( elem ) { + return (/h\d/i).test( elem.nodeName ); + }, + + text: function( elem ) { + var attr = elem.getAttribute( "type" ), type = elem.type; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); + }, + + radio: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; + }, + + checkbox: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; + }, + + file: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; + }, + + password: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; + }, + + submit: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "submit" === elem.type; + }, + + image: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; + }, + + reset: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "reset" === elem.type; + }, + + button: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && "button" === elem.type || name === "button"; + }, + + input: function( elem ) { + return (/input|select|textarea|button/i).test( elem.nodeName ); + }, + + focus: function( elem ) { + return elem === elem.ownerDocument.activeElement; + } + }, + setFilters: { + first: function( elem, i ) { + return i === 0; + }, + + last: function( elem, i, match, array ) { + return i === array.length - 1; + }, + + even: function( elem, i ) { + return i % 2 === 0; + }, + + odd: function( elem, i ) { + return i % 2 === 1; + }, + + lt: function( elem, i, match ) { + return i < match[3] - 0; + }, + + gt: function( elem, i, match ) { + return i > match[3] - 0; + }, + + nth: function( elem, i, match ) { + return match[3] - 0 === i; + }, + + eq: function( elem, i, match ) { + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function( elem, match, i, array ) { + var name = match[1], + filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0; + + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + + } else { + Sizzle.error( name ); + } + }, + + CHILD: function( elem, match ) { + var type = match[1], + node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + + case "nth": + var first = match[2], + last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + var doneName = match[0], + parent = elem.parentNode; + + if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { + var count = 0; + + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + + parent.sizcache = doneName; + } + + var diff = elem.nodeIndex - last; + + if ( first === 0 ) { + return diff === 0; + + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + + ID: function( elem, match ) { + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + + TAG: function( elem, match ) { + return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; + }, + + CLASS: function( elem, match ) { + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + + ATTR: function( elem, match ) { + var name = match[1], + result = Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + + POS: function( elem, match, i, array ) { + var name = match[2], + filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} + +var makeArray = function( array, results ) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +// Provide a fallback method if it does not work +} catch( e ) { + makeArray = function( array, results ) { + var i = 0, + ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder, siblingCheck; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + return a.compareDocumentPosition ? -1 : 1; + } + + return a.compareDocumentPosition(b) & 4 ? -1 : 1; + }; + +} else { + sortOrder = function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + + siblingCheck = function( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; + }; +} + +// Utility function for retreiving the text value of an array of DOM nodes +Sizzle.getText = function( elems ) { + var ret = "", elem; + + for ( var i = 0; elems[i]; i++ ) { + elem = elems[i]; + + // Get the text from text nodes and CDATA nodes + if ( elem.nodeType === 3 || elem.nodeType === 4 ) { + ret += elem.nodeValue; + + // Traverse everything else, except comment nodes + } else if ( elem.nodeType !== 8 ) { + ret += Sizzle.getText( elem.childNodes ); + } + } + + return ret; +}; + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(), + root = document.documentElement; + + form.innerHTML = ""; + + // Inject it into the root element, check its status, and remove it quickly + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( document.getElementById( id ) ) { + Expr.find.ID = function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + + return m ? + m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? + [m] : + undefined : + []; + } + }; + + Expr.filter.ID = function( elem, match ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + + // release memory in IE + root = form = null; +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function( match, context ) { + var results = context.getElementsByTagName( match[1] ); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = ""; + + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + + Expr.attrHandle.href = function( elem ) { + return elem.getAttribute( "href", 2 ); + }; + } + + // release memory in IE + div = null; +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, + div = document.createElement("div"), + id = "__sizzle__"; + + div.innerHTML = "

"; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function( query, context, extra, seed ) { + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && !Sizzle.isXML(context) ) { + // See if we find a selector to speed up + var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); + + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { + // Speed-up: Sizzle("TAG") + if ( match[1] ) { + return makeArray( context.getElementsByTagName( query ), extra ); + + // Speed-up: Sizzle(".CLASS") + } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { + return makeArray( context.getElementsByClassName( match[2] ), extra ); + } + } + + if ( context.nodeType === 9 ) { + // Speed-up: Sizzle("body") + // The body element only exists once, optimize finding it + if ( query === "body" && context.body ) { + return makeArray( [ context.body ], extra ); + + // Speed-up: Sizzle("#ID") + } else if ( match && match[3] ) { + var elem = context.getElementById( match[3] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id === match[3] ) { + return makeArray( [ elem ], extra ); + } + + } else { + return makeArray( [], extra ); + } + } + + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(qsaError) {} + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + var oldContext = context, + old = context.getAttribute( "id" ), + nid = old || id, + hasParent = context.parentNode, + relativeHierarchySelector = /^\s*[+~]/.test( query ); + + if ( !old ) { + context.setAttribute( "id", nid ); + } else { + nid = nid.replace( /'/g, "\\$&" ); + } + if ( relativeHierarchySelector && hasParent ) { + context = context.parentNode; + } + + try { + if ( !relativeHierarchySelector || hasParent ) { + return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); + } + + } catch(pseudoError) { + } finally { + if ( !old ) { + oldContext.removeAttribute( "id" ); + } + } + } + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + // release memory in IE + div = null; + })(); +} + +(function(){ + var html = document.documentElement, + matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; + + if ( matches ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9 fails this) + var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), + pseudoWorks = false; + + try { + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( document.documentElement, "[test!='']:sizzle" ); + + } catch( pseudoError ) { + pseudoWorks = true; + } + + Sizzle.matchesSelector = function( node, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); + + if ( !Sizzle.isXML( node ) ) { + try { + if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { + var ret = matches.call( node, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || !disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9, so check for that + node.document && node.document.nodeType !== 11 ) { + return ret; + } + } + } catch(e) {} + } + + return Sizzle(expr, null, null, [node]).length > 0; + }; + } +})(); + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
"; + + // Opera can't find a second classname (in 9.6) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function( match, context, isXML ) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + // release memory in IE + div = null; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +if ( document.documentElement.contains ) { + Sizzle.contains = function( a, b ) { + return a !== b && (a.contains ? a.contains(b) : true); + }; + +} else if ( document.documentElement.compareDocumentPosition ) { + Sizzle.contains = function( a, b ) { + return !!(a.compareDocumentPosition(b) & 16); + }; + +} else { + Sizzle.contains = function() { + return false; + }; +} + +Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function( selector, context ) { + var match, + tmpSet = [], + later = "", + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.filters; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})(); + + +var runtil = /Until$/, + rparentsprev = /^(?:parents|prevUntil|prevAll)/, + // Note: This RegExp should be improved, or likely pulled from Sizzle + rmultiselector = /,/, + isSimple = /^.[^:#\[\.,]*$/, + slice = Array.prototype.slice, + POS = jQuery.expr.match.POS, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var self = this, + i, l; + + if ( typeof selector !== "string" ) { + return jQuery( selector ).filter(function() { + for ( i = 0, l = self.length; i < l; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }); + } + + var ret = this.pushStack( "", "find", selector ), + length, n, r; + + for ( i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( n = length; n < ret.length; n++ ) { + for ( r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var targets = jQuery( target ); + return this.filter(function() { + for ( var i = 0, l = targets.length; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && ( typeof selector === "string" ? + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var ret = [], i, l, cur = this[0]; + + // Array + if ( jQuery.isArray( selectors ) ) { + var match, selector, + matches = {}, + level = 1; + + if ( cur && selectors.length ) { + for ( i = 0, l = selectors.length; i < l; i++ ) { + selector = selectors[i]; + + if ( !matches[ selector ] ) { + matches[ selector ] = POS.test( selector ) ? + jQuery( selector, context || this.context ) : + selector; + } + } + + while ( cur && cur.ownerDocument && cur !== context ) { + for ( selector in matches ) { + match = matches[ selector ]; + + if ( match.jquery ? match.index( cur ) > -1 : jQuery( cur ).is( match ) ) { + ret.push({ selector: selector, elem: cur, level: level }); + } + } + + cur = cur.parentNode; + level++; + } + } + + return ret; + } + + // String + var pos = POS.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( i = 0, l = this.length; i < l; i++ ) { + cur = this[i]; + + while ( cur ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + + } else { + cur = cur.parentNode; + if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { + break; + } + } + } + } + + ret = ret.length > 1 ? jQuery.unique( ret ) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + andSelf: function() { + return this.add( this.prevObject ); + } +}); + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return jQuery.nth( elem, 2, "nextSibling" ); + }, + prev: function( elem ) { + return jQuery.nth( elem, 2, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( elem.parentNode.firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.makeArray( elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ), + // The variable 'args' was introduced in + // https://github.com/jquery/jquery/commit/52a0238 + // to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed. + // http://code.google.com/p/v8/issues/detail?id=1050 + args = slice.call(arguments); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, args.join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + nth: function( cur, result, dir, elem ) { + result = result || 1; + var num = 0; + + for ( ; cur; cur = cur[dir] ) { + if ( cur.nodeType === 1 && ++num === result ) { + break; + } + } + + return cur; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return (elem === qualifier) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return (jQuery.inArray( elem, qualifier ) >= 0) === keep; + }); +} + + + + +var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, + rtagName = /<([\w:]+)/, + rtbody = /
", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + col: [ 2, "", "
" ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }; + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE can't serialize and + + + */ + +(function() { + this.loggly = function(opts) { + this.user_agent = get_agent(); + this.browser_size = get_size(); + log_methods = {'error': 5, 'warn': 4, 'info': 3, 'debug': 2, 'log': 1}; + if (!opts.url) throw new Error("Please include a Loggly HTTP URL."); + if (!opts.level) { + this.level = log_methods['info']; + } else { + this.level = log_methods[opts.level]; + } + this.log = function(data) { + if (log_methods['log'] == this.level) { + opts.data = data; + janky(opts); + } + }; + this.debug = function(data) { + if (log_methods['debug'] >= this.level) { + opts.data = data; + janky(opts); + } + }; + this.info = function(data) { + if (log_methods['info'] >= this.level) { + opts.data = data; + janky(opts); + } + }; + this.warn = function(data) { + if (log_methods['warn'] >= this.level) { + opts.data = data; + janky(opts); + } + }; + this.error = function(data) { + if (log_methods['error'] >= this.level) { + opts.data = data; + janky(opts); + } + }; + }; + this.janky = function(opts) { + janky._form(function(iframe, form) { + form.setAttribute("action", opts.url); + form.setAttribute("method", "post"); + janky._input(iframe, form, opts.data); + form.submit(); + setTimeout(function(){ + document.body.removeChild(iframe); + }, 2000); + }); + }; + this.janky._form = function(cb) { + var iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + iframe.style.display = "none"; + setTimeout(function() { + var form = iframe.contentWindow.document.createElement("form"); + iframe.contentWindow.document.body.appendChild(form); + cb(iframe, form); + }, 0); + }; + this.janky._input = function(iframe, form, data) { + var inp = iframe.contentWindow.document.createElement("input"); + inp.setAttribute("type", "hidden"); + inp.setAttribute("name", "source"); + inp.value = "castor " + data; + form.appendChild(inp); + }; + this.get_agent = function () { + return navigator.appCodeName + navigator.appName + navigator.appVersion; + }; + this.get_size = function () { + var width = 0; var height = 0; + if( typeof( window.innerWidth ) == 'number' ) { + width = window.innerWidth; height = window.innerHeight; + } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) { + width = document.documentElement.clientWidth; height = document.documentElement.clientHeight; + } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) { + width = document.body.clientWidth; height = document.body.clientHeight; + } + return {'height': height, 'width': width}; + }; +})(); + + +jsworld={};jsworld.formatIsoDateTime=function(a,b){if(typeof a==="undefined")a=new Date;if(typeof b==="undefined")b=false;var c=jsworld.formatIsoDate(a)+" "+jsworld.formatIsoTime(a);if(b){var d=a.getHours()-a.getUTCHours();var e=Math.abs(d);var f=a.getUTCMinutes();var g=a.getMinutes();if(g!=f&&f<30&&d<0)e--;if(g!=f&&f>30&&d>0)e--;var h;if(g!=f)h=":30";else h=":00";var i;if(e<10)i="0"+e+h;else i=""+e+h;if(d<0)i="-"+i;else i="+"+i;c=c+i}return c};jsworld.formatIsoDate=function(a){if(typeof a==="undefined")a=new Date;var b=a.getFullYear();var c=a.getMonth()+1;var d=a.getDate();return b+"-"+jsworld._zeroPad(c,2)+"-"+jsworld._zeroPad(d,2)};jsworld.formatIsoTime=function(a){if(typeof a==="undefined")a=new Date;var b=a.getHours();var c=a.getMinutes();var d=a.getSeconds();return jsworld._zeroPad(b,2)+":"+jsworld._zeroPad(c,2)+":"+jsworld._zeroPad(d,2)};jsworld.parseIsoDateTime=function(a){if(typeof a!="string")throw"Error: The parameter must be a string";var b=a.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)/);if(b===null)b=a.match(/^(\d\d\d\d)(\d\d)(\d\d)[T ](\d\d)(\d\d)(\d\d)/);if(b===null)b=a.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d)(\d\d)(\d\d)/);if(b===null)b=a.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)/);if(b===null)throw"Error: Invalid ISO-8601 date/time string";var c=parseInt(b[1],10);var d=parseInt(b[2],10);var e=parseInt(b[3],10);var f=parseInt(b[4],10);var g=parseInt(b[5],10);var h=parseInt(b[6],10);if(d<1||d>12||e<1||e>31||f<0||f>23||g<0||g>59||h<0||h>59)throw"Error: Invalid ISO-8601 date/time value";var i=new Date(c,d-1,e,f,g,h);if(i.getDate()!=e||i.getMonth()+1!=d)throw"Error: Invalid date";return i};jsworld.parseIsoDate=function(a){if(typeof a!="string")throw"Error: The parameter must be a string";var b=a.match(/^(\d\d\d\d)-(\d\d)-(\d\d)/);if(b===null)b=a.match(/^(\d\d\d\d)(\d\d)(\d\d)/);if(b===null)throw"Error: Invalid ISO-8601 date string";var c=parseInt(b[1],10);var d=parseInt(b[2],10);var e=parseInt(b[3],10);if(d<1||d>12||e<1||e>31)throw"Error: Invalid ISO-8601 date value";var f=new Date(c,d-1,e);if(f.getDate()!=e||f.getMonth()+1!=d)throw"Error: Invalid date";return f};jsworld.parseIsoTime=function(a){if(typeof a!="string")throw"Error: The parameter must be a string";var b=a.match(/^(\d\d):(\d\d):(\d\d)/);if(b===null)b=a.match(/^(\d\d)(\d\d)(\d\d)/);if(b===null)throw"Error: Invalid ISO-8601 date/time string";var c=parseInt(b[1],10);var d=parseInt(b[2],10);var e=parseInt(b[3],10);if(c<0||c>23||d<0||d>59||e<0||e>59)throw"Error: Invalid ISO-8601 time value";return new Date(0,0,0,c,d,e)};jsworld._trim=function(a){var b=" \n\r\t\f \u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000";for(var c=0;c=0;c--){if(b.indexOf(a.charAt(c))===-1){a=a.substring(0,c+1);break}}return b.indexOf(a.charAt(0))===-1?a:""};jsworld._isNumber=function(a){if(typeof a=="number")return true;if(typeof a!="string")return false;var b=a+"";return/^-?(\d+|\d*\.\d+)$/.test(b)};jsworld._isInteger=function(a){if(typeof a!="number"&&typeof a!="string")return false;var b=a+"";return/^-?\d+$/.test(b)};jsworld._isFloat=function(a){if(typeof a!="number"&&typeof a!="string")return false;var b=a+"";return/^-?\.\d+?$/.test(b)};jsworld._hasOption=function(a,b){if(typeof a!="string"||typeof b!="string")return false;if(b.indexOf(a)!=-1)return true;else return false};jsworld._stringReplaceAll=function(a,b,c){var d;if(b.length==1&&c.length==1){d="";for(var e=0;e0){if(d.length>0)g=parseInt(d.shift(),10);if(isNaN(g))throw"Error: Invalid grouping";if(g==-1){e=a.substring(0,f)+e;break}f-=g;if(f<1){e=a.substring(0,f+g)+e;break}e=c+a.substring(f,f+g)+e}return e};jsworld._formatFractionPart=function(a,b){for(var c=0;a.length0)return a;else throw"Empty or no string"};if(a==null||typeof a!="object")throw"Error: Invalid/missing locale properties";if(typeof a.decimal_point!="string")throw"Error: Invalid/missing decimal_point property";this.decimal_point=a.decimal_point;if(typeof a.thousands_sep!="string")throw"Error: Invalid/missing thousands_sep property";this.thousands_sep=a.thousands_sep;if(typeof a.grouping!="string")throw"Error: Invalid/missing grouping property";this.grouping=a.grouping;if(typeof a.int_curr_symbol!="string")throw"Error: Invalid/missing int_curr_symbol property";if(!/[A-Za-z]{3}.?/.test(a.int_curr_symbol))throw"Error: Invalid int_curr_symbol property";this.int_curr_symbol=a.int_curr_symbol;if(typeof a.currency_symbol!="string")throw"Error: Invalid/missing currency_symbol property";this.currency_symbol=a.currency_symbol;if(typeof a.frac_digits!="number"&&a.frac_digits<0)throw"Error: Invalid/missing frac_digits property";this.frac_digits=a.frac_digits;if(a.mon_decimal_point===null||a.mon_decimal_point==""){if(this.frac_digits>0)throw"Error: Undefined mon_decimal_point property";else a.mon_decimal_point=""}if(typeof a.mon_decimal_point!="string")throw"Error: Invalid/missing mon_decimal_point property";this.mon_decimal_point=a.mon_decimal_point;if(typeof a.mon_thousands_sep!="string")throw"Error: Invalid/missing mon_thousands_sep property";this.mon_thousands_sep=a.mon_thousands_sep;if(typeof a.mon_grouping!="string")throw"Error: Invalid/missing mon_grouping property";this.mon_grouping=a.mon_grouping;if(typeof a.positive_sign!="string")throw"Error: Invalid/missing positive_sign property";this.positive_sign=a.positive_sign;if(typeof a.negative_sign!="string")throw"Error: Invalid/missing negative_sign property";this.negative_sign=a.negative_sign;if(a.p_cs_precedes!==0&&a.p_cs_precedes!==1)throw"Error: Invalid/missing p_cs_precedes property, must be 0 or 1";this.p_cs_precedes=a.p_cs_precedes;if(a.n_cs_precedes!==0&&a.n_cs_precedes!==1)throw"Error: Invalid/missing n_cs_precedes, must be 0 or 1";this.n_cs_precedes=a.n_cs_precedes;if(a.p_sep_by_space!==0&&a.p_sep_by_space!==1&&a.p_sep_by_space!==2)throw"Error: Invalid/missing p_sep_by_space property, must be 0, 1 or 2";this.p_sep_by_space=a.p_sep_by_space;if(a.n_sep_by_space!==0&&a.n_sep_by_space!==1&&a.n_sep_by_space!==2)throw"Error: Invalid/missing n_sep_by_space property, must be 0, 1, or 2";this.n_sep_by_space=a.n_sep_by_space;if(a.p_sign_posn!==0&&a.p_sign_posn!==1&&a.p_sign_posn!==2&&a.p_sign_posn!==3&&a.p_sign_posn!==4)throw"Error: Invalid/missing p_sign_posn property, must be 0, 1, 2, 3 or 4";this.p_sign_posn=a.p_sign_posn;if(a.n_sign_posn!==0&&a.n_sign_posn!==1&&a.n_sign_posn!==2&&a.n_sign_posn!==3&&a.n_sign_posn!==4)throw"Error: Invalid/missing n_sign_posn property, must be 0, 1, 2, 3 or 4";this.n_sign_posn=a.n_sign_posn;if(typeof a.int_frac_digits!="number"&&a.int_frac_digits<0)throw"Error: Invalid/missing int_frac_digits property";this.int_frac_digits=a.int_frac_digits;if(a.int_p_cs_precedes!==0&&a.int_p_cs_precedes!==1)throw"Error: Invalid/missing int_p_cs_precedes property, must be 0 or 1";this.int_p_cs_precedes=a.int_p_cs_precedes;if(a.int_n_cs_precedes!==0&&a.int_n_cs_precedes!==1)throw"Error: Invalid/missing int_n_cs_precedes property, must be 0 or 1";this.int_n_cs_precedes=a.int_n_cs_precedes;if(a.int_p_sep_by_space!==0&&a.int_p_sep_by_space!==1&&a.int_p_sep_by_space!==2)throw"Error: Invalid/missing int_p_sep_by_spacev, must be 0, 1 or 2";this.int_p_sep_by_space=a.int_p_sep_by_space;if(a.int_n_sep_by_space!==0&&a.int_n_sep_by_space!==1&&a.int_n_sep_by_space!==2)throw"Error: Invalid/missing int_n_sep_by_space property, must be 0, 1, or 2";this.int_n_sep_by_space=a.int_n_sep_by_space;if(a.int_p_sign_posn!==0&&a.int_p_sign_posn!==1&&a.int_p_sign_posn!==2&&a.int_p_sign_posn!==3&&a.int_p_sign_posn!==4)throw"Error: Invalid/missing int_p_sign_posn property, must be 0, 1, 2, 3 or 4";this.int_p_sign_posn=a.int_p_sign_posn;if(a.int_n_sign_posn!==0&&a.int_n_sign_posn!==1&&a.int_n_sign_posn!==2&&a.int_n_sign_posn!==3&&a.int_n_sign_posn!==4)throw"Error: Invalid/missing int_n_sign_posn property, must be 0, 1, 2, 3 or 4";this.int_n_sign_posn=a.int_n_sign_posn;if(a==null||typeof a!="object")throw"Error: Invalid/missing time locale properties";try{this.abday=this._parseList(a.abday,7)}catch(b){throw"Error: Invalid abday property: "+b}try{this.day=this._parseList(a.day,7)}catch(b){throw"Error: Invalid day property: "+b}try{this.abmon=this._parseList(a.abmon,12)}catch(b){throw"Error: Invalid abmon property: "+b}try{this.mon=this._parseList(a.mon,12)}catch(b){throw"Error: Invalid mon property: "+b}try{this.d_fmt=this._validateFormatString(a.d_fmt)}catch(b){throw"Error: Invalid d_fmt property: "+b}try{this.t_fmt=this._validateFormatString(a.t_fmt)}catch(b){throw"Error: Invalid t_fmt property: "+b}try{this.d_t_fmt=this._validateFormatString(a.d_t_fmt)}catch(b){throw"Error: Invalid d_t_fmt property: "+b}try{var c=this._parseList(a.am_pm,2);this.am=c[0];this.pm=c[1]}catch(b){this.am="";this.pm=""}this.getAbbreviatedWeekdayName=function(a){if(typeof a=="undefined"||a===null)return this.abday;if(!jsworld._isInteger(a)||a<0||a>6)throw"Error: Invalid weekday argument, must be an integer [0..6]";return this.abday[a]};this.getWeekdayName=function(a){if(typeof a=="undefined"||a===null)return this.day;if(!jsworld._isInteger(a)||a<0||a>6)throw"Error: Invalid weekday argument, must be an integer [0..6]";return this.day[a]};this.getAbbreviatedMonthName=function(a){if(typeof a=="undefined"||a===null)return this.abmon;if(!jsworld._isInteger(a)||a<0||a>11)throw"Error: Invalid month argument, must be an integer [0..11]";return this.abmon[a]};this.getMonthName=function(a){if(typeof a=="undefined"||a===null)return this.mon;if(!jsworld._isInteger(a)||a<0||a>11)throw"Error: Invalid month argument, must be an integer [0..11]";return this.mon[a]};this.getDecimalPoint=function(){return this.decimal_point};this.getCurrencySymbol=function(){return this.currency_symbol};this.getIntCurrencySymbol=function(){return this.int_curr_symbol.substring(0,3)};this.currencySymbolPrecedes=function(){if(this.p_cs_precedes==1)return true;else return false};this.intCurrencySymbolPrecedes=function(){if(this.int_p_cs_precedes==1)return true;else return false};this.getMonetaryDecimalPoint=function(){return this.mon_decimal_point};this.getFractionalDigits=function(){return this.frac_digits};this.getIntFractionalDigits=function(){return this.int_frac_digits}};jsworld.NumericFormatter=function(a){if(typeof a!="object"||a._className!="jsworld.Locale")throw"Constructor error: You must provide a valid jsworld.Locale instance";this.lc=a;this.format=function(a,b){if(typeof a=="string")a=jsworld._trim(a);if(!jsworld._isNumber(a))throw"Error: The input is not a number";var c=parseFloat(a,10);var d=jsworld._getPrecision(b);if(d!=-1)c=Math.round(c*Math.pow(10,d))/Math.pow(10,d);var e=jsworld._splitNumber(String(c));var f;if(c===0)f="0";else f=jsworld._hasOption("^",b)?e.integer:jsworld._formatIntegerPart(e.integer,this.lc.grouping,this.lc.thousands_sep);var g=d!=-1?jsworld._formatFractionPart(e.fraction,d):e.fraction;var h=g.length?f+this.lc.decimal_point+g:f;if(jsworld._hasOption("~",b)||c===0){return h}else{if(jsworld._hasOption("+",b)||c<0){if(c>0)return"+"+h;else if(c<0)return"-"+h;else return h}else{return h}}}};jsworld.DateTimeFormatter=function(a){if(typeof a!="object"||a._className!="jsworld.Locale")throw"Constructor error: You must provide a valid jsworld.Locale instance.";this.lc=a;this.formatDate=function(a){var b=null;if(typeof a=="string"){try{b=jsworld.parseIsoDate(a)}catch(c){b=jsworld.parseIsoDateTime(a)}}else if(a!==null&&typeof a=="object"){b=a}else{throw"Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"}return this._applyFormatting(b,this.lc.d_fmt)};this.formatTime=function(a){var b=null;if(typeof a=="string"){try{b=jsworld.parseIsoTime(a)}catch(c){b=jsworld.parseIsoDateTime(a)}}else if(a!==null&&typeof a=="object"){b=a}else{throw"Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"}return this._applyFormatting(b,this.lc.t_fmt)};this.formatDateTime=function(a){var b=null;if(typeof a=="string"){b=jsworld.parseIsoDateTime(a)}else if(a!==null&&typeof a=="object"){b=a}else{throw"Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"}return this._applyFormatting(b,this.lc.d_t_fmt)};this._applyFormatting=function(a,b){b=b.replace(/%%/g,"%");b=b.replace(/%a/g,this.lc.abday[a.getDay()]);b=b.replace(/%A/g,this.lc.day[a.getDay()]);b=b.replace(/%b/g,this.lc.abmon[a.getMonth()]);b=b.replace(/%B/g,this.lc.mon[a.getMonth()]);b=b.replace(/%d/g,jsworld._zeroPad(a.getDate(),2));b=b.replace(/%e/g,jsworld._spacePad(a.getDate(),2));b=b.replace(/%F/g,a.getFullYear()+"-"+jsworld._zeroPad(a.getMonth()+1,2)+"-"+jsworld._zeroPad(a.getDate(),2));b=b.replace(/%h/g,this.lc.abmon[a.getMonth()]);b=b.replace(/%H/g,jsworld._zeroPad(a.getHours(),2));b=b.replace(/%I/g,jsworld._zeroPad(this._hours12(a.getHours()),2));b=b.replace(/%k/g,a.getHours());b=b.replace(/%l/g,this._hours12(a.getHours()));b=b.replace(/%m/g,jsworld._zeroPad(a.getMonth()+1,2));b=b.replace(/%n/g,"\n");b=b.replace(/%M/g,jsworld._zeroPad(a.getMinutes(),2));b=b.replace(/%p/g,this._getAmPm(a.getHours()));b=b.replace(/%P/g,this._getAmPm(a.getHours()).toLocaleLowerCase());b=b.replace(/%R/g,jsworld._zeroPad(a.getHours(),2)+":"+jsworld._zeroPad(a.getMinutes(),2));b=b.replace(/%S/g,jsworld._zeroPad(a.getSeconds(),2));b=b.replace(/%T/g,jsworld._zeroPad(a.getHours(),2)+":"+jsworld._zeroPad(a.getMinutes(),2)+":"+jsworld._zeroPad(a.getSeconds(),2));b=b.replace(/%w/g,this.lc.day[a.getDay()]);b=b.replace(/%y/g,(new String(a.getFullYear())).substring(2));b=b.replace(/%Y/g,a.getFullYear());b=b.replace(/%Z/g,"");b=b.replace(/%[a-zA-Z]/g,"");return b};this._hours12=function(a){if(a===0)return 12;else if(a>12)return a-12;else return a};this._getAmPm=function(a){if(a===0||a>12)return this.lc.pm;else return this.lc.am}};jsworld.MonetaryFormatter=function(a,b,c){if(typeof a!="object"||a._className!="jsworld.Locale")throw"Constructor error: You must provide a valid jsworld.Locale instance";this.lc=a;this.currencyFractionDigits={AFN:0,ALL:0,AMD:0,BHD:3,BIF:0,BYR:0,CLF:0,CLP:0,COP:0,CRC:0,DJF:0,GNF:0,GYD:0,HUF:0,IDR:0,IQD:0,IRR:0,ISK:0,JOD:3,JPY:0,KMF:0,KRW:0,KWD:3,LAK:0,LBP:0,LYD:3,MGA:0,MMK:0,MNT:0,MRO:0,MUR:0,OMR:3,PKR:0,PYG:0,RSD:0,RWF:0,SLL:0,SOS:0,STD:0,SYP:0,TND:3,TWD:0,TZS:0,UGX:0,UZS:0,VND:0,VUV:0,XAF:0,XOF:0,XPF:0,YER:0,ZMK:0};if(typeof b=="string"){this.currencyCode=b.toUpperCase();var d=this.currencyFractionDigits[this.currencyCode];if(typeof d!="number")d=2;this.lc.frac_digits=d;this.lc.int_frac_digits=d}else{this.currencyCode=this.lc.int_curr_symbol.substring(0,3).toUpperCase()}this.intSep=this.lc.int_curr_symbol.charAt(3);if(this.currencyCode==this.lc.int_curr_symbol.substring(0,3)){this.internationalFormatting=false;this.curSym=this.lc.currency_symbol}else{if(typeof c=="string"){this.curSym=c;this.internationalFormatting=false}else{this.internationalFormatting=true}}this.getCurrencySymbol=function(){return this.curSym};this.currencySymbolPrecedes=function(a){if(typeof a=="string"&&a=="i"){if(this.lc.int_p_cs_precedes==1)return true;else return false}else{if(this.internationalFormatting){if(this.lc.int_p_cs_precedes==1)return true;else return false}else{if(this.lc.p_cs_precedes==1)return true;else return false}}};this.getDecimalPoint=function(){return this.lc.mon_decimal_point};this.getFractionalDigits=function(a){if(typeof a=="string"&&a=="i"){return this.lc.int_frac_digits}else{if(this.internationalFormatting)return this.lc.int_frac_digits;else return this.lc.frac_digits}};this.format=function(a,b){var c;if(typeof a=="string"){a=jsworld._trim(a);c=parseFloat(a);if(typeof c!="number"||isNaN(c))throw"Error: Amount string not a number"}else if(typeof a=="number"){c=a}else{throw"Error: Amount not a number"}var d=jsworld._getPrecision(b);if(d==-1){if(this.internationalFormatting||jsworld._hasOption("i",b))d=this.lc.int_frac_digits;else d=this.lc.frac_digits}c=Math.round(c*Math.pow(10,d))/Math.pow(10,d);var e=jsworld._splitNumber(String(c));var f;if(c===0)f="0";else f=jsworld._hasOption("^",b)?e.integer:jsworld._formatIntegerPart(e.integer,this.lc.mon_grouping,this.lc.mon_thousands_sep);var g;if(d==-1){if(this.internationalFormatting||jsworld._hasOption("i",b))g=jsworld._formatFractionPart(e.fraction,this.lc.int_frac_digits);else g=jsworld._formatFractionPart(e.fraction,this.lc.frac_digits)}else{g=jsworld._formatFractionPart(e.fraction,d)}var h;if(this.lc.frac_digits>0||g.length)h=f+this.lc.mon_decimal_point+g;else h=f;if(jsworld._hasOption("~",b)){return h}else{var i=jsworld._hasOption("!",b)?true:false;var j=c<0?"-":"+";if(this.internationalFormatting||jsworld._hasOption("i",b)){if(i)return this._formatAsInternationalCurrencyWithNoSym(j,h);else return this._formatAsInternationalCurrency(j,h)}else{if(i)return this._formatAsLocalCurrencyWithNoSym(j,h);else return this._formatAsLocalCurrency(j,h)}}};this._formatAsLocalCurrency=function(a,b){if(a=="+"){if(this.lc.p_sign_posn===0&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return"("+b+this.curSym+")"}else if(this.lc.p_sign_posn===0&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return"("+this.curSym+b+")"}else if(this.lc.p_sign_posn===0&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return"("+b+" "+this.curSym+")"}else if(this.lc.p_sign_posn===0&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return"("+this.curSym+" "+b+")"}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return this.lc.positive_sign+b+this.curSym}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+this.curSym+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return this.lc.positive_sign+b+" "+this.curSym}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+this.curSym+" "+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return this.lc.positive_sign+" "+b+this.curSym}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+" "+this.curSym+b}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return b+this.curSym+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.curSym+b+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return b+" "+this.curSym+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.curSym+" "+b+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return b+this.curSym+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.curSym+b+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign+this.curSym}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+this.curSym+b}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return b+" "+this.lc.positive_sign+this.curSym}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+this.curSym+" "+b}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign+" "+this.curSym}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+" "+this.curSym+b}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return b+this.curSym+this.lc.positive_sign}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.curSym+this.lc.positive_sign+b}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return b+" "+this.curSym+this.lc.positive_sign}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.curSym+this.lc.positive_sign+" "+b}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return b+this.curSym+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.curSym+" "+this.lc.positive_sign+b}}else if(a=="-"){if(this.lc.n_sign_posn===0&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return"("+b+this.curSym+")"}else if(this.lc.n_sign_posn===0&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return"("+this.curSym+b+")"}else if(this.lc.n_sign_posn===0&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return"("+b+" "+this.curSym+")"}else if(this.lc.n_sign_posn===0&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return"("+this.curSym+" "+b+")"}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return this.lc.negative_sign+b+this.curSym}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+this.curSym+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return this.lc.negative_sign+b+" "+this.curSym}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+this.curSym+" "+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return this.lc.negative_sign+" "+b+this.curSym}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+this.curSym+b}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return b+this.curSym+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.curSym+b+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return b+" "+this.curSym+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.curSym+" "+b+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return b+this.curSym+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.curSym+b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return b+this.lc.negative_sign+this.curSym}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+this.curSym+b}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return b+" "+this.lc.negative_sign+this.curSym}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+this.curSym+" "+b}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return b+this.lc.negative_sign+" "+this.curSym}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+this.curSym+b}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return b+this.curSym+this.lc.negative_sign}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.curSym+this.lc.negative_sign+b}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return b+" "+this.curSym+this.lc.negative_sign}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.curSym+this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return b+this.curSym+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.curSym+" "+this.lc.negative_sign+b}}throw"Error: Invalid POSIX LC MONETARY definition"};this._formatAsInternationalCurrency=function(a,b){if(a=="+"){if(this.lc.int_p_sign_posn===0&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return"("+b+this.currencyCode+")"}else if(this.lc.int_p_sign_posn===0&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return"("+this.currencyCode+b+")"}else if(this.lc.int_p_sign_posn===0&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return"("+b+this.intSep+this.currencyCode+")"}else if(this.lc.int_p_sign_posn===0&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return"("+this.currencyCode+this.intSep+b+")"}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return this.lc.positive_sign+b+this.currencyCode}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.currencyCode+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return this.lc.positive_sign+b+this.intSep+this.currencyCode}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.currencyCode+this.intSep+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return this.lc.positive_sign+this.intSep+b+this.currencyCode}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+this.currencyCode+b}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return b+this.currencyCode+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.currencyCode+b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.currencyCode+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.currencyCode+this.intSep+b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return b+this.currencyCode+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.currencyCode+b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return b+this.lc.positive_sign+this.currencyCode}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.currencyCode+b}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.lc.positive_sign+this.currencyCode}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.currencyCode+this.intSep+b}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return b+this.lc.positive_sign+this.intSep+this.currencyCode}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+this.currencyCode+b}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return b+this.currencyCode+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.currencyCode+this.lc.positive_sign+b}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.currencyCode+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.currencyCode+this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return b+this.currencyCode+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.currencyCode+this.intSep+this.lc.positive_sign+b}}else if(a=="-"){if(this.lc.int_n_sign_posn===0&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return"("+b+this.currencyCode+")"}else if(this.lc.int_n_sign_posn===0&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return"("+this.currencyCode+b+")"}else if(this.lc.int_n_sign_posn===0&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return"("+b+this.intSep+this.currencyCode+")"}else if(this.lc.int_n_sign_posn===0&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return"("+this.currencyCode+this.intSep+b+")"}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return this.lc.negative_sign+b+this.currencyCode}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.currencyCode+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return this.lc.negative_sign+b+this.intSep+this.currencyCode}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.currencyCode+this.intSep+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return this.lc.negative_sign+this.intSep+b+this.currencyCode}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+this.currencyCode+b}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return b+this.currencyCode+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.currencyCode+b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.currencyCode+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.currencyCode+this.intSep+b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return b+this.currencyCode+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.currencyCode+b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return b+this.lc.negative_sign+this.currencyCode}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.currencyCode+b}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.lc.negative_sign+this.currencyCode}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.currencyCode+this.intSep+b}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return b+this.lc.negative_sign+this.intSep+this.currencyCode}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+this.currencyCode+b}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return b+this.currencyCode+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.currencyCode+this.lc.negative_sign+b}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.currencyCode+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.currencyCode+this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return b+this.currencyCode+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.currencyCode+this.intSep+this.lc.negative_sign+b}}throw"Error: Invalid POSIX LC MONETARY definition"};this._formatAsLocalCurrencyWithNoSym=function(a,b){if(a=="+"){if(this.lc.p_sign_posn===0){return"("+b+")"}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return this.lc.positive_sign+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return this.lc.positive_sign+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return this.lc.positive_sign+" "+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+" "+b}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return b+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return b+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return b+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+" "+b}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+" "+b}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return b+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+" "+b}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return b+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+b}}else if(a=="-"){if(this.lc.n_sign_posn===0){return"("+b+")"}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return this.lc.negative_sign+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return this.lc.negative_sign+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return b+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return b+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+b}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+b}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+b}}throw"Error: Invalid POSIX LC MONETARY definition"};this._formatAsInternationalCurrencyWithNoSym=function(a,b){if(a=="+"){if(this.lc.int_p_sign_posn===0){return"("+b+")"}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return this.lc.positive_sign+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return this.lc.positive_sign+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+b}}else if(a=="-"){if(this.lc.int_n_sign_posn===0){return"("+b+")"}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return this.lc.negative_sign+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return this.lc.negative_sign+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+b}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+b}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+b}}throw"Error: Invalid POSIX LC_MONETARY definition"}};jsworld.NumericParser=function(a){if(typeof a!="object"||a._className!="jsworld.Locale")throw"Constructor error: You must provide a valid jsworld.Locale instance";this.lc=a;this.parse=function(a){if(typeof a!="string")throw"Parse error: Argument must be a string";var b=jsworld._trim(a);b=jsworld._stringReplaceAll(a,this.lc.thousands_sep,"");b=jsworld._stringReplaceAll(b,this.lc.decimal_point,".");if(jsworld._isNumber(b))return parseFloat(b,10);else throw"Parse error: Invalid number string"}};jsworld.DateTimeParser=function(a){if(typeof a!="object"||a._className!="jsworld.Locale")throw"Constructor error: You must provide a valid jsworld.Locale instance.";this.lc=a;this.parseTime=function(a){if(typeof a!="string")throw"Parse error: Argument must be a string";var b=this._extractTokens(this.lc.t_fmt,a);var c=false;if(b.hour!==null&&b.minute!==null&&b.second!==null){c=true}else if(b.hourAmPm!==null&&b.am!==null&&b.minute!==null&&b.second!==null){if(b.am){b.hour=parseInt(b.hourAmPm,10)}else{if(b.hourAmPm==12)b.hour=0;else b.hour=parseInt(b.hourAmPm,10)+12}c=true}if(c)return jsworld._zeroPad(b.hour,2)+":"+jsworld._zeroPad(b.minute,2)+":"+jsworld._zeroPad(b.second,2);else throw"Parse error: Invalid/ambiguous time string"};this.parseDate=function(a){if(typeof a!="string")throw"Parse error: Argument must be a string";var b=this._extractTokens(this.lc.d_fmt,a);var c=false;if(b.year!==null&&b.month!==null&&b.day!==null){c=true}if(c)return jsworld._zeroPad(b.year,4)+"-"+jsworld._zeroPad(b.month,2)+"-"+jsworld._zeroPad(b.day,2);else throw"Parse error: Invalid date string"};this.parseDateTime=function(a){if(typeof a!="string")throw"Parse error: Argument must be a string";var b=this._extractTokens(this.lc.d_t_fmt,a);var c=false;var d=false;if(b.hour!==null&&b.minute!==null&&b.second!==null){c=true}else if(b.hourAmPm!==null&&b.am!==null&&b.minute!==null&&b.second!==null){if(b.am){b.hour=parseInt(b.hourAmPm,10)}else{if(b.hourAmPm==12)b.hour=0;else b.hour=parseInt(b.hourAmPm,10)+12}c=true}if(b.year!==null&&b.month!==null&&b.day!==null){d=true}if(d&&c)return jsworld._zeroPad(b.year,4)+"-"+jsworld._zeroPad(b.month,2)+"-"+jsworld._zeroPad(b.day,2)+" "+jsworld._zeroPad(b.hour,2)+":"+jsworld._zeroPad(b.minute,2)+":"+jsworld._zeroPad(b.second,2);else throw"Parse error: Invalid/ambiguous date/time string"};this._extractTokens=function(a,b){var c={year:null,month:null,day:null,hour:null,hourAmPm:null,am:null,minute:null,second:null,weekday:null};while(a.length>0){if(a.charAt(0)=="%"&&a.charAt(1)!=""){var d=a.substring(0,2);if(d=="%%"){b=b.substring(1)}else if(d=="%a"){for(var e=0;e31)throw"Parse error: Unrecognised day of the month (%e)";b=b.substring(f.length)}else if(d=="%F"){if(/^\d\d\d\d/.test(b)){c.year=parseInt(b.substring(0,4),10);b=b.substring(4)}else{throw"Parse error: Unrecognised date (%F)"}if(jsworld._stringStartsWith(b,"-"))b=b.substring(1);else throw"Parse error: Unrecognised date (%F)";if(/^0[1-9]|1[0-2]/.test(b)){c.month=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised date (%F)";if(jsworld._stringStartsWith(b,"-"))b=b.substring(1);else throw"Parse error: Unrecognised date (%F)";if(/^0[1-9]|[1-2][0-9]|3[0-1]/.test(b)){c.day=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised date (%F)"}else if(d=="%H"){if(/^[0-1][0-9]|2[0-3]/.test(b)){c.hour=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised hour (%H)"}else if(d=="%I"){if(/^0[1-9]|1[0-2]/.test(b)){c.hourAmPm=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised hour (%I)"}else if(d=="%k"){var g=b.match(/^(\d{1,2})/);c.hour=parseInt(g,10);if(isNaN(c.hour)||c.hour<0||c.hour>23)throw"Parse error: Unrecognised hour (%k)";b=b.substring(g.length)}else if(d=="%l"){var g=b.match(/^(\d{1,2})/);c.hourAmPm=parseInt(g,10);if(isNaN(c.hourAmPm)||c.hourAmPm<1||c.hourAmPm>12)throw"Parse error: Unrecognised hour (%l)";b=b.substring(g.length)}else if(d=="%m"){if(/^0[1-9]|1[0-2]/.test(b)){c.month=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised month (%m)"}else if(d=="%M"){if(/^[0-5][0-9]/.test(b)){c.minute=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised minute (%M)"}else if(d=="%n"){if(b.charAt(0)=="\n")b=b.substring(1);else throw"Parse error: Unrecognised new line (%n)"}else if(d=="%p"){if(jsworld._stringStartsWith(b,this.lc.am)){c.am=true;b=b.substring(this.lc.am.length)}else if(jsworld._stringStartsWith(b,this.lc.pm)){c.am=false;b=b.substring(this.lc.pm.length)}else throw"Parse error: Unrecognised AM/PM value (%p)"}else if(d=="%P"){if(jsworld._stringStartsWith(b,this.lc.am.toLowerCase())){c.am=true;b=b.substring(this.lc.am.length)}else if(jsworld._stringStartsWith(b,this.lc.pm.toLowerCase())){c.am=false;b=b.substring(this.lc.pm.length)}else throw"Parse error: Unrecognised AM/PM value (%P)"}else if(d=="%R"){if(/^[0-1][0-9]|2[0-3]/.test(b)){c.hour=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised time (%R)";if(jsworld._stringStartsWith(b,":"))b=b.substring(1);else throw"Parse error: Unrecognised time (%R)";if(/^[0-5][0-9]/.test(b)){c.minute=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised time (%R)"}else if(d=="%S"){if(/^[0-5][0-9]/.test(b)){c.second=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised second (%S)"}else if(d=="%T"){if(/^[0-1][0-9]|2[0-3]/.test(b)){c.hour=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised time (%T)";if(jsworld._stringStartsWith(b,":"))b=b.substring(1);else throw"Parse error: Unrecognised time (%T)";if(/^[0-5][0-9]/.test(b)){c.minute=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised time (%T)";if(jsworld._stringStartsWith(b,":"))b=b.substring(1);else throw"Parse error: Unrecognised time (%T)";if(/^[0-5][0-9]/.test(b)){c.second=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised time (%T)"}else if(d=="%w"){if(/^\d/.test(b)){c.weekday=parseInt(b.substring(0,1),10);b=b.substring(1)}else throw"Parse error: Unrecognised weekday number (%w)"}else if(d=="%y"){if(/^\d\d/.test(b)){var h=parseInt(b.substring(0,2),10);if(h>50)c.year=1900+h;else c.year=2e3+h;b=b.substring(2)}else throw"Parse error: Unrecognised year (%y)"}else if(d=="%Y"){if(/^\d\d\d\d/.test(b)){c.year=parseInt(b.substring(0,4),10);b=b.substring(4)}else throw"Parse error: Unrecognised year (%Y)"}else if(d=="%Z"){if(a.length===0)break}a=a.substring(2)}else{if(a.charAt(0)!=b.charAt(0))throw'Parse error: Unexpected symbol "'+b.charAt(0)+'" in date/time string';a=a.substring(1);b=b.substring(1)}}return c}};jsworld.MonetaryParser=function(a){if(typeof a!="object"||a._className!="jsworld.Locale")throw"Constructor error: You must provide a valid jsworld.Locale instance";this.lc=a;this.parse=function(a){if(typeof a!="string")throw"Parse error: Argument must be a string";var b=this._detectCurrencySymbolType(a);var c,d;if(b=="local"){c="local";d=a.replace(this.lc.getCurrencySymbol(),"")}else if(b=="int"){c="int";d=a.replace(this.lc.getIntCurrencySymbol(),"")}else if(b=="none"){c="local";d=a}else throw"Parse error: Internal assert failure";d=jsworld._stringReplaceAll(d,this.lc.mon_thousands_sep,"");d=d.replace(this.lc.mon_decimal_point,".");d=d.replace(/\s*/g,"");d=this._removeLocalNonNegativeSign(d,c);d=this._normaliseNegativeSign(d,c);if(jsworld._isNumber(d))return parseFloat(d,10);else throw"Parse error: Invalid currency amount string"};this._detectCurrencySymbolType=function(a){if(this.lc.getCurrencySymbol().length>this.lc.getIntCurrencySymbol().length){if(a.indexOf(this.lc.getCurrencySymbol())!=-1)return"local";else if(a.indexOf(this.lc.getIntCurrencySymbol())!=-1)return"int";else return"none"}else{if(a.indexOf(this.lc.getIntCurrencySymbol())!=-1)return"int";else if(a.indexOf(this.lc.getCurrencySymbol())!=-1)return"local";else return"none"}};this._removeLocalNonNegativeSign=function(a,b){a=a.replace(this.lc.positive_sign,"");if((b=="local"&&this.lc.p_sign_posn===0||b=="int"&&this.lc.int_p_sign_posn===0)&&/\(\d+\.?\d*\)/.test(a)){a=a.replace("(","");a=a.replace(")","")}return a};this._normaliseNegativeSign=function(a,b){a=a.replace(this.lc.negative_sign,"-");if(b=="local"&&this.lc.n_sign_posn===0||b=="int"&&this.lc.int_n_sign_posn===0){if(/^\(\d+\.?\d*\)$/.test(a)){a=a.replace("(","");a=a.replace(")","");return"-"+a}}if(b=="local"&&this.lc.n_sign_posn==2||b=="int"&&this.lc.int_n_sign_posn==2){if(/^\d+\.?\d*-$/.test(a)){a=a.replace("-","");return"-"+a}}if(b=="local"&&this.lc.n_cs_precedes===0&&this.lc.n_sign_posn==3||b=="local"&&this.lc.n_cs_precedes===0&&this.lc.n_sign_posn==4||b=="int"&&this.lc.int_n_cs_precedes===0&&this.lc.int_n_sign_posn==3||b=="int"&&this.lc.int_n_cs_precedes===0&&this.lc.int_n_sign_posn==4){if(/^\d+\.?\d*-$/.test(a)){a=a.replace("-","");return"-"+a}}return a}} + + +if(typeof POSIX_LC == "undefined") var POSIX_LC = {}; + +POSIX_LC.en_US = { + "decimal_point" : ".", + "thousands_sep" : ",", + "grouping" : "3", + "abday" : ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"], + "day" : ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"], + "abmon" : ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"], + "mon" : ["January","February","March","April","May","June","July","August","September","October","November","December"], + "d_fmt" : "%m/%e/%y", + "t_fmt" : "%I:%M:%S %p", + "d_t_fmt" : "%B %e, %Y %I:%M:%S %p %Z", + "am_pm" : ["AM","PM"], + "int_curr_symbol" : "USD ", + "currency_symbol" : "\u0024", + "mon_decimal_point" : ".", + "mon_thousands_sep" : ",", + "mon_grouping" : "3", + "positive_sign" : "", + "negative_sign" : "-", + "int_frac_digits" : 2, + "frac_digits" : 2, + "p_cs_precedes" : 1, + "n_cs_precedes" : 1, + "p_sep_by_space" : 0, + "n_sep_by_space" : 0, + "p_sign_posn" : 1, + "n_sign_posn" : 1, + "int_p_cs_precedes" : 1, + "int_n_cs_precedes" : 1, + "int_p_sep_by_space" : 0, + "int_n_sep_by_space" : 0, + "int_p_sign_posn" : 1, + "int_n_sign_posn" : 1 +} + +if(typeof POSIX_LC == "undefined") var POSIX_LC = {}; + +POSIX_LC.fr_FR = { + "decimal_point" : ",", + "thousands_sep" : "\u00a0", + "grouping" : "3", + "abday" : ["dim.","lun.","mar.", + "mer.","jeu.","ven.", + "sam."], + "day" : ["dimanche","lundi","mardi", + "mercredi","jeudi","vendredi", + "samedi"], + "abmon" : ["janv.","f\u00e9vr.","mars", + "avr.","mai","juin", + "juil.","ao\u00fbt","sept.", + "oct.","nov.","d\u00e9c."], + "mon" : ["janvier","f\u00e9vrier","mars", + "avril","mai","juin", + "juillet","ao\u00fbt","septembre", + "octobre","novembre","d\u00e9cembre"], + "d_fmt" : "%d/%m/%y", + "t_fmt" : "%H:%M:%S", + "d_t_fmt" : "%e %B %Y %H:%M:%S %Z", + "am_pm" : ["AM","PM"], + "int_curr_symbol" : "EUR ", + "currency_symbol" : "\u20ac", + "mon_decimal_point" : ",", + "mon_thousands_sep" : "\u00a0", + "mon_grouping" : "3", + "positive_sign" : "", + "negative_sign" : "-", + "int_frac_digits" : 2, + "frac_digits" : 2, + "p_cs_precedes" : 0, + "n_cs_precedes" : 0, + "p_sep_by_space" : 1, + "n_sep_by_space" : 1, + "p_sign_posn" : 1, + "n_sign_posn" : 1, + "int_p_cs_precedes" : 0, + "int_n_cs_precedes" : 0, + "int_p_sep_by_space" : 1, + "int_n_sep_by_space" : 1, + "int_p_sign_posn" : 1, + "int_n_sign_posn" : 1 +}; + +/** https://github.com/csnover/js-iso8601 */(function(n,f){var u=n.parse,c=[1,4,5,6,7,10,11];n.parse=function(t){var i,o,a=0;if(o=/^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(t)){for(var v=0,r;r=c[v];++v)o[r]=+o[r]||0;o[2]=(+o[2]||1)-1,o[3]=+o[3]||1,o[8]!=="Z"&&o[9]!==f&&(a=o[10]*60+o[11],o[9]==="+"&&(a=0-a)),i=n.UTC(o[1],o[2],o[3],o[4],o[5]+a,o[6],o[7])}else i=u?u(t):NaN;return i}})(Date) + +/*! + * geo-location-javascript v0.4.3 + * http://code.google.com/p/geo-location-javascript/ + * + * Copyright (c) 2009 Stan Wiechers + * Licensed under the MIT licenses. + * + * Revision: $Rev: 68 $: + * Author: $Author: whoisstan $: + * Date: $Date: 2010-02-15 13:42:19 +0100 (Mon, 15 Feb 2010) $: + */ +var geo_position_js=function() { + + var pub = {}; + var provider=null; + + pub.getCurrentPosition = function(successCallback,errorCallback,options) + { + provider.getCurrentPosition(successCallback, errorCallback,options); + } + + pub.init = function() + { + try + { + if (typeof(geo_position_js_simulator)!="undefined") + { + provider=geo_position_js_simulator; + } + else if (typeof(bondi)!="undefined" && typeof(bondi.geolocation)!="undefined") + { + provider=bondi.geolocation; + } + else if (typeof(navigator.geolocation)!="undefined") + { + provider=navigator.geolocation; + pub.getCurrentPosition = function(successCallback, errorCallback, options) + { + function _successCallback(p) + { + //for mozilla geode,it returns the coordinates slightly differently + if(typeof(p.latitude)!="undefined") + { + successCallback({timestamp:p.timestamp, coords: {latitude:p.latitude,longitude:p.longitude}}); + } + else + { + successCallback(p); + } + } + provider.getCurrentPosition(_successCallback,errorCallback,options); + } + } + else if(typeof(window.google)!="undefined" && typeof(google.gears)!="undefined") + { + provider=google.gears.factory.create('beta.geolocation'); + } + else if ( typeof(Mojo) !="undefined" && typeof(Mojo.Service.Request)!="Mojo.Service.Request") + { + provider=true; + pub.getCurrentPosition = function(successCallback, errorCallback, options) + { + + parameters={}; + if(options) + { + //http://developer.palm.com/index.php?option=com_content&view=article&id=1673#GPS-getCurrentPosition + if (options.enableHighAccuracy && options.enableHighAccuracy==true) + { + parameters.accuracy=1; + } + if (options.maximumAge) + { + parameters.maximumAge=options.maximumAge; + } + if (options.responseTime) + { + if(options.responseTime<5) + { + parameters.responseTime=1; + } + else if (options.responseTime<20) + { + parameters.responseTime=2; + } + else + { + parameters.timeout=3; + } + } + } + + + r=new Mojo.Service.Request('palm://com.palm.location', { + method:"getCurrentPosition", + parameters:parameters, + onSuccess: function(p){successCallback({timestamp:p.timestamp, coords: {latitude:p.latitude, longitude:p.longitude,heading:p.heading}});}, + onFailure: function(e){ + if (e.errorCode==1) + { + errorCallback({code:3,message:"Timeout"}); + } + else if (e.errorCode==2) + { + errorCallback({code:2,message:"Position Unavailable"}); + } + else + { + errorCallback({code:0,message:"Unknown Error: webOS-code"+errorCode}); + } + } + }); + } + + } + else if (typeof(device)!="undefined" && typeof(device.getServiceObject)!="undefined") + { + provider=device.getServiceObject("Service.Location", "ILocation"); + + //override default method implementation + pub.getCurrentPosition = function(successCallback, errorCallback, options) + { + function callback(transId, eventCode, result) { + if (eventCode == 4) + { + errorCallback({message:"Position unavailable", code:2}); + } + else + { + //no timestamp of location given? + successCallback({timestamp:null, coords: {latitude:result.ReturnValue.Latitude, longitude:result.ReturnValue.Longitude, altitude:result.ReturnValue.Altitude,heading:result.ReturnValue.Heading}}); + } + } + //location criteria + var criteria = new Object(); + criteria.LocationInformationClass = "BasicLocationInformation"; + //make the call + provider.ILocation.GetLocation(criteria,callback); + } + } + } + catch (e){ + alert("error="+e); + if(typeof(console)!="undefined") + { + console.log(e); + } + return false; + } + return provider!=null; + } + + + return pub; +}(); +// Couldn't get unminified version to work , go here for docs => https://github.com/iamnoah/writeCapture +(function(E,a){var j=a.document;function A(Q){var Z=j.createElement("div");j.body.insertBefore(Z,null);E.replaceWith(Z,'\n
\n
\n
\n \n\n
\n
\n \n
\n

'); + __out.push(__sanitize(t('Invite Link'))); + __out.push(' '); + __out.push(__sanitize(USER.referral_url)); + __out.push('

\n\n \n\n
\n\n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "templates/clients/login": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + __out.push('
\n\t

'); + __out.push(__sanitize(t('Sign In'))); + __out.push('

\n\t
\n\t\t
\n\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\n\t\t\t
\n\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\n\t\t\t
\n\n
\n\n

'); + __out.push(__sanitize(t('Forgot Password?'))); + __out.push('

\n\n\t\t
\n\t
\n
\n\n
\n
\n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "templates/clients/modules/credit_card": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + var printCard; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + if (this.cards === "new") { + __out.push('\n
\n
\n
\n
\n
\n
\n \n \n
\n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n
\n
\n \n \n
\n
\n
\n \n \n
\n
\n
\n \n \n
\n
\n
\n \n
\n
\n
\n'); + } else { + __out.push('\n '); + printCard = __bind(function(card, index) { + var exp, style; + __out.push('\n
\n '); + style = "background-position:-173px"; + __out.push('\n '); + if (card.get("card_type") === "Visa") { + style = "background-position:0px"; + } + __out.push('\n '); + if (card.get("card_type") === "MasterCard") { + style = "background-position:-42px"; + } + __out.push('\n '); + if (card.get("card_type") === "American Express") { + style = "background-position:-130px"; + } + __out.push('\n '); + if (card.get("card_type") === "Discover Card") { + style = "background-position:-85px"; + } + __out.push('\n
\n
\n ****'); + __out.push(__sanitize(card.get("card_number"))); + __out.push('\n \n '); + if (card.get("card_expiration")) { + __out.push('\n '); + __out.push(__sanitize(t('Expiry'))); + __out.push('\n '); + exp = card.get('card_expiration').split('-'); + __out.push('\n '); + __out.push(__sanitize("" + exp[0] + "-" + exp[1])); + __out.push('\n '); + } + __out.push('\n \n \n \n '); + if (card.get("default")) { + __out.push('\n ('); + __out.push(__sanitize(t('default card'))); + __out.push(')\n '); + } + __out.push('\n '); + if (this.cards.length > 1 && !card.get("default")) { + __out.push('\n '); + __out.push(__sanitize(t('make default'))); + __out.push('\n '); + } + __out.push('\n \n '); + __out.push(__sanitize(t('Edit'))); + __out.push('\n \n '); + if (this.cards.length > 1) { + __out.push('\n '); + __out.push(__sanitize(t('Delete'))); + __out.push('\n '); + } + __out.push('\n
\n '); + _.each(this.cards.models, printCard); + __out.push('\n
\n
\n\n'); + } + __out.push('\n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "templates/clients/modules/sub_header": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + __out.push('
\n
'); + __out.push(__sanitize(this.heading)); + __out.push('
\n
\n '); + if (window.USER.first_name) { + __out.push('\n '); + __out.push(__sanitize(t('Hello Greeting', { + name: USER.first_name + }))); + __out.push('\n '); + } + __out.push('\n
\n
\n
\n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "templates/clients/promotions": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + var promo, _i, _len, _ref; + __out.push(require('templates/clients/modules/sub_header').call(this, { + heading: t("Promotions") + })); + __out.push('\n\n
\n
\n
\n \n \n
\n
\n \n \n\n \n
\n '); + if (this.promos.length > 0) { + __out.push('\n
\n

'); + __out.push(__sanitize(t('Your Available Promotions'))); + __out.push('

\n \n \n\n \n \n \n \n \n \n \n \n '); + _ref = this.promos; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + promo = _ref[_i]; + __out.push('\n \n \n \n \n \n \n '); + } + __out.push('\n \n
'); + __out.push(__sanitize(t('Code'))); + __out.push(''); + __out.push(__sanitize(t('Details'))); + __out.push(''); + __out.push(__sanitize(t('Starts'))); + __out.push(''); + __out.push(__sanitize(t('Expires'))); + __out.push('
'); + __out.push(__sanitize(promo.code)); + __out.push(''); + __out.push(__sanitize(promo.description)); + __out.push(''); + __out.push(__sanitize(app.helpers.formatDate(promo.starts_at, true, "America/Los_Angeles"))); + __out.push(''); + __out.push(__sanitize(app.helpers.formatDate(promo.ends_at, true, "America/Los_Angeles"))); + __out.push('
\n
\n '); + } else { + __out.push('\n\n

'); + __out.push(__sanitize(t('No Active Promotions'))); + __out.push('

\n '); + } + __out.push('\n\n
\n
\n
\n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "templates/clients/request": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + var showFavoriteLocation; + showFavoriteLocation = function(location, index) { + var alphabet; + __out.push('\n '); + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + __out.push('\n
\n '); + __out.push(__sanitize(location.nickname)); + return __out.push('\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n

'); + __out.push(__sanitize(t('Driver Name:'))); + __out.push('

\n

\n
\n

'); + __out.push(__sanitize(t('Driver #:'))); + __out.push('

\n

\n
\n

'); + __out.push(__sanitize(t('Pickup Address:'))); + __out.push('

\n

\n
\n ');
+      __out.push(__sanitize(t('Add to Favorite Locations')));
+      __out.push('\n
\n
\n

\n '); + __out.push(__sanitize(t('Nickname:'))); + __out.push('\n \n \n \n \n
\n
\n
\n
\n

'); + __out.push(__sanitize(t('Your last trip'))); + __out.push('

\n
\n \n ');
+      __out.push(__sanitize(t('Star')));
+      __out.push('\n ');
+      __out.push(__sanitize(t('Star')));
+      __out.push('\n ');
+      __out.push(__sanitize(t('Star')));
+      __out.push('\n ');
+      __out.push(__sanitize(t('Star')));
+      __out.push('\n ');
+      __out.push(__sanitize(t('Star')));
+      __out.push('\n \n \n
\n \n
\n \n
\n \n
\n \n\n
\n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "templates/shared/menu": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + __out.push('\n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "translations/en": function(exports, require, module) {(function() { + exports.translations = { + "Uber": "Uber", + "Sign Up": "Sign Up", + "Ride Request": "Ride Request", + "Invite Friends": "Invite Friends", + "Promotions": "Promotions", + "Billing": "Billing", + "Settings": "Settings", + "Forgot Password?": "Forgot Password?", + "Password Recovery": "Password Recovery", + "Login": "Login", + "Trip Detail": "Trip Detail", + "Password Reset": "Password Reset", + "Confirm Email": "Confirm Email", + "Request Ride": "Request Ride", + "Credit Card Number": "Credit Card Number", + "month": "month", + "01-Jan": "01-Jan", + "02-Feb": "02-Feb", + "03-Mar": "03-Mar", + "04-Apr": "04-Apr", + "05-May": "05-May", + "06-Jun": "06-Jun", + "07-Jul": "07-Jul", + "08-Aug": "08-Aug", + "09-Sep": "09-Sep", + "10-Oct": "10-Oct", + "11-Nov": "11-Nov", + "12-Dec": "12-Dec", + "year": "year", + "CVV": "CVV", + "Category": "Category", + "personal": "personal", + "business": "business", + "Default Credit Card": "Default Credit Card", + "Add Credit Card": "Add Credit Card", + "Expiry": "Expiry", + "default card": "default card", + "make default": "make default", + "Edit": "Edit", + "Delete": "Delete", + "Expiry Month": "Expiry Month", + "Expiry Year": "Expiry Year", + "Unable to Verify Card": "Unable to verify card at this time. Please try again later.", + "Credit Card Update Succeeded": "Your card has been successfully updated!", + "Credit Card Update Failed": "We couldn't save your changes. Please try again in a few minutes.", + "Credit Card Delete Succeeded": "Your card has been deleted!", + "Credit Card Delete Failed": "We were unable to delete your card. Please try again later.", + "Credit Card Update Category Succeeded": "Successfully changed card category!", + "Credit Card Update Category Failed": "We couldn't change your card category. Please try again in a few minutes.", + "Credit Card Update Default Succeeded": "Successfully changed default card!", + "Credit Card Update Default Failed": "We couldn't change your default card. Please try again in a few minutes.", + "Hello Greeting": "Hello, <%= name %>", + "Card Ending in": "Card Ending in", + "Trip Map": "Trip Map", + "Amount": "Amount: <%= amount %>", + "Last Attempt to Bill": "Last Attempt to Bill: <%= date %>", + "Charge": "Charge", + "Uber Credit Balance Note": "Your account has an UberCredit balance of <%= amount %>. When billing for trips, we'll deplete your UberCredit balance before applying charges to your credit card.", + "Please Add Credit Card": "Please add a credit card to bill your outstanding charges.", + "Credit Cards": "Credit Cards", + "add a new credit card": "add a new credit card", + "Account Balance": "Account Balance", + "Arrears": "Arrears", + "Billing Succeeded": "Your card was successfully billed.", + "Confirm Email Succeeded": "Successfully confirmed email token, redirecting to log in page...", + "Confirm Email Failed": "Unable to confirm email. Please contact support@uber.com if this problem persists.", + "Email Already Confirmed": "Your email address has already been confirmed, redirecting to log in page...", + "Credit Card Added": "Credit Card Added", + "No Credit Card": "No Credit Card", + "Mobile Number Confirmed": "Mobile Number Confirmed", + "No Confirmed Mobile": "No Confirmed Mobile", + "E-mail Address Confirmed": "E-mail Address Confirmed", + "No Confirmed E-mail": "No Confirmed E-mail", + 'Reply to sign up text': 'Reply "GO" to the text message you received at sign up.', + "Resend text message": "Resend text message", + "Click sign up link": "Click the link in the email you received at sign up.", + "Resend email": "Resend email", + "Add a credit card to ride": "Add a credit card and you'll be ready to ride Uber.", + "Your Most Recent Trip": "Your Most Recent Trip", + "details": "details", + "Your Trip History ": "Your Trip History ", + "Status": "Status", + "Here's how it works:": "Here's how it works:", + "Show all trips": "Show all trips", + "Set your location:": "Set your location:", + "App search for address": "iPhone/Android app: fix the pin or search for an address", + "SMS text address": "SMS: text your address to UBRCAB (827222)", + "Confirm pickup request": "Confirm your pickup request", + "Uber sends ETA": "Uber will send you an ETA (usually within 5-10 minutes)", + "Car arrives": "When your car is arriving, Uber will inform you again.", + "Ride to destination": "Hop in the car and tell the driver your destination.", + "Thank your driver": "That’s it! Please thank your driver but remember that your tip is included and no cash is necessary.", + "Trip started here": "Trip started here", + "Trip ended here": "Trip ended here", + "Sending Email": "Sending email...", + "Resend Email Succeeded": "We just sent the email. Please click on the confirmation link you recieve.", + "Resend Email Failed": "There was an error sending the email. Please contact support if the problem persists.", + "Resend Text Succeeded": 'We just sent the text message. Please reply "GO" to the message you recieve. It may take a few minutes for the message to reach you phone.', + "Resend Text Failed": "There was an error sending the text message. Please contact support if the problem persists.", + "Password Reset Error": "There was an error processing your password reset request.", + "New Password": "New Password", + "Forgot Password": "Forgot Password", + "Forgot Password Error": "Your email address could not be found. Please make sure to use the same email address you used when you signed up.", + "Forgot Password Success": "Please check your email for a link to reset your password.", + "Forgot Password Enter Email": 'Enter your email address and Uber will send you a link to reset your password. If you remember your password, you can sign in here.', + "Invite friends": "Invite friends", + "Give $ Get $": "Give $10, Get $10", + "Give $ Get $ Description": "Every friend you invite to Uber gets $10 of Uber credit. After someone you’ve invited takes his/her first ride, you get $10 of Uber credits too!", + "What are you waiting for?": "So, what are you waiting for? Invite away!", + "Tweet": "Tweet", + "Invite Link": "Email or IM this link to your friends:", + "Email Address": "Email Address", + "Reset Password": "Reset Password", + "Enter Promotion Code": "If you have a promotion code, enter it here:", + "Your Active Promotions": "Your Active Promotions", + "Code": "Code", + "Details": "Details", + "Trips Remaining": "Trips Remaining", + "Expires": "Expires", + "No Active Promotions": "There are no active promotions on your account.", + "Your Available Promotions": "Your Available Promotions", + "Where do you want us to pick you up?": "Where do you want us to pick you up?", + "Address to search": "Address to search", + "Search": "Search", + "Driver Name:": "Driver Name:", + "Driver #:": "Driver #:", + "Pickup Address:": "Pickup Address:", + "Add to Favorite Locations": "Add to Favorite Locations", + "Star": "Star", + "Nickname:": "Nickname:", + "Add": "Add", + "Your last trip": "Your last trip", + "Please rate your driver:": "Please rate your driver:", + "Comments: (optional)": "Comments: (optional)", + "Rate Trip": "Rate Trip", + "Pickup time:": "Pickup time:", + "Miles:": "Miles:", + "Trip time:": "Trip time:", + "Fare:": "Fare:", + "Favorite Locations": "Favorite Locations", + "Search Results": "Search Results", + "You have no favorite locations saved.": "You have no favorite locations saved.", + "Loading...": "Loading...", + "Request Pickup": "Request Pickup", + "Cancel Pickup": "Cancel Pickup", + "Requesting Closest Driver": "Requesting the closest driver to pick you up...", + "En Route": "You are currently en route...", + "Rate Last Trip": "Please rate your trip to make another request", + "Rate Before Submitting": "Please rate your trip before submitting the form", + "Address too short": "Address too short", + "or did you mean": "or did you mean", + "Search Address Failed": "Unable to find the given address. Please enter another address close to your location.", + "Sending pickup request...": "Sending pickup request...", + "Cancel Request Prompt": "Are you sure you want to cancel your request?", + "Cancel Request Arrived Prompt": 'Are you sure you want to cancel your request? Your driver has arrived so there is a $10 cancellation fee. It may help to call your driver now', + "Favorite Location Nickname Length Error": "Nickname has to be atleast 3 characters", + "Favorite Location Save Succeeded": "Location Saved!", + "Favorite Location Save Failed": "Unable to save your location. Please try again later.", + "Favorite Location Title": "Favorite Location <%= id %>", + "Search Location Title": "Search Location <%= id %>", + "ETA Message": "ETA: Around <%= minutes %> Minutes", + "Nearest Cab Message": "The closest driver is approximately <%= minutes %> minute(s) away", + "Arrival ETA Message": "Your Uber will arrive in about <%= minutes %> minute(s)", + "Arriving Now Message": "Your Uber is arriving now...", + "Rating Driver Failed": "Unable to contact server. Please try again later or email support if this issue persists.", + "Account Information": "Account Information", + "Mobile Phone Information": "Mobile Phone Information", + "settings": "settings", + "Information": "Information", + "Picture": "Picture", + "Change password": "Change password", + "Your current Picture": "Your current Picture", + "Your Favorite Locations": "Your Favorite Locations", + "You have no favorite locations saved.": "You have no favorite locations saved.", + "Purpose of Mobile": "We send text messages to your mobile phone to tell you when your driver is arriving. You can also request trips using text messages.", + "Country": "Country", + "Mobile Number": "Mobile Number", + "Submit": "Submit", + "Favorite Location": "Favorite Location", + "No Approximate Address": "Could not find an approximate address", + "Address:": "Address:", + "Information Update Succeeded": "Your information has been updated!", + "Information Update Failed": "We couldn't update your information. Please try again in few minutes or contact support if the problem persists.", + "Location Delete Succeeded": "Location deleted!", + "Location Delete Failed": "We were unable to delete your favorite location. Please try again later or contact support of the issue persists.", + "Location Edit Succeeded": "Changes Saved!", + "Location Edit Failed": "We couldn't save your changes. Please try again in a few minutes.", + "Picture Update Succeeded": "Your picture has been updated!", + "Picture Update Failed": "We couldn't change your picture. Please try again in a few minutes.", + "Personal Information": "Personal Information", + "Mobile Phone Number": "Mobile Phone Number", + "Payment Information": "Payment Information", + "Purpose of Credit Card": "We keep your credit card on file so that your trip go as fast as possible. You will not be charged until you take a trip.", + "Your card will not be charged until you take a trip.": "Your card will not be charged until you take a trip.", + "Credit Card Number": "Credit Card Number", + "Expiration Date": "Expiration Date", + "Promotion Code": "Promotion Code", + "Enter Promo Here": "If you have a code for a promotion, invitation or group deal, you can enter it here.", + "Promotion Code Input Label": "Promotion, Invite or Groupon Code (optional)", + "Terms and Conditions": "Terms and Conditions", + "HELP": "HELP", + "STOP": "STOP", + "Legal Information": "Legal Information", + "Sign Up Agreement": "By signing up, I agree to the Uber <%= terms_link %> and <%= privacy_link %> and understand that Uber is a request tool, not a transportation carrier.", + "Sign Up Agreement Error": "You must agree to the Uber Terms and Conditions and Privacy Policy to continue.", + "Message and Data Rates Disclosure": "Message and Data Rates May Apply. Reply <%= help_string %> to 827-222 for help. Reply <%= stop_string %> to 827-222 to stop texts. For additional assistance, visit support.uber.com or call (866) 576-1039. Supported Carriers: AT&T, Sprint, Verizon, and T-Mobile.", + "I Agree": "I agree to the Terms & Conditions and Privacy Policy", + "Security Code": "Security Code", + "Type of Card": "Type of Card", + "Personal": "Personal", + "Business": "Business", + "Code": "Code", + "Zip or Postal Code": "Zip or Postal Code", + "Your Trip": "Your Trip", + "Trip Info": "Trip Info", + "Request a fare review": "Request a fare review", + "Fare Review Submitted": "Your fare review has been submitted. We'll get back to you soon about your request. Sorry for any inconvenience this may have caused!", + "Fair Price Consideration": "We're committed to delivering Uber service at a fair price. Before requesting a fare review, please consider:", + "Your Fare Calculation": "Your Fare Calculation", + "Charges": "Charges", + "Discounts": "Discounts", + "Total Charge": "Total Charge", + "Uber pricing information": "Uber pricing information", + "Uber Pricing Information Message": "<%= learn_link %> is published on our website.", + "GPS Point Capture Disclosure": "Due to a finite number of GPS point captures, corners on your trip map may appear cut off or rounded. These minor inaccuracies result in a shorter measured distance, which always results in a cheaper trip.", + "Fare Review Note": "Please elaborate on why this trip requires a fare review. Your comments below will help us better establish the correct price for your trip:", + "Fare Review Error": "There was an error submitting the review. Please ensure that you have a message.", + "Sign In": "Sign In" + }; +}).call(this); +}, "translations/fr": function(exports, require, module) {(function() { + exports.translations = { + "Uber": "Uber", + "Sign Up": "Inscription", + "Ride Request": "Passer une Commande", + "Invite Friends": "Inviter vos Amis", + "Promotions": "Promotions", + "Billing": "Paiement", + "Settings": "Paramètres", + "Forgot Password?": "Mot de passe oublié ?", + "Password Recovery": "Récupération du mot de passe", + "Login": "Connexion", + "Trip Detail": "Détail de la Course", + "Password Reset": "Réinitialisation du mot de passe", + "Confirm Email": "Confirmation de l’e-mail", + "Request Ride": "Passer une Commande", + "Credit Card Number": "Numéro de Carte de Crédit", + "month": "mois", + "01-Jan": "01-Jan", + "02-Feb": "02-Fév", + "03-Mar": "03-Mar", + "04-Apr": "04-Avr", + "05-May": "05-Mai", + "06-Jun": "06-Juin", + "07-Jul": "07-Jui", + "08-Aug": "08-Aoû", + "09-Sep": "09-Sep", + "10-Oct": "10-Oct", + "11-Nov": "11-Nov", + "12-Dec": "12-Déc", + "year": "année", + "CVV": "Code de Sécurité", + "Category": "Type", + "personal": "personnel", + "business": "entreprise", + "Default Credit Card": "Carte par Défaut", + "Add Credit Card": "Ajouter une Carte", + "Expiry": "Expire", + "default card": "carte par défaut", + "make default": "choisir par défaut", + "Edit": "Modifier", + "Delete": "Supprimer", + "Expiry Month": "Mois d’Expiration", + "Expiry Year": "Année d’Expiration", + "Unable to Verify Card": "Impossible de vérifier la carte pour le moment. Merci de réessayer un peu plus tard.", + "Credit Card Update Succeeded": "Votre carte a été mise à jour avec succès !", + "Credit Card Update Failed": "Nous ne pouvons enregistrer vos changements. Merci de réessayer dans quelques minutes.", + "Credit Card Delete Succeeded": "Votre carte a été supprimée !", + "Credit Card Delete Failed": "Nous n’avons pas été en mesure de supprimer votre carte. Merci de réessayer plus tard.", + "Credit Card Update Category Succeeded": "Changement de catégorie de carte réussi !", + "Credit Card Update Category Failed": "Nous ne pouvons pas changer la catégorie de votre carte. Merci de réessayer dans quelques minutes.", + "Credit Card Update Default Succeeded": "Carte par défaut changée avec succès !", + "Credit Card Update Default Failed": "Nous ne pouvons pas changer votre carte par défaut. Merci de réessayer dans quelques minutes.", + "Hello Greeting": "Bonjour, <%= name %>", + "Card Ending in": "La carte expire dans", + "Trip Map": "Carte des Courses", + "Amount": "Montant: <%= amount %>", + "Last Attempt to Bill": "Dernière tentative de prélèvement : <%= date %>", + "Charge": "Débit", + "Uber Credit Balance Note": "Votre compte a un solde de <%= amount %> UberCredits. Lorsque nous facturons des courses, nous réduirons votre solde d’UberCredits avant de prélever votre carte de crédit.", + "Please Add Credit Card": "Merci d’ajouter une carte de crédit pour que nous puissions vous facturer.", + "Credit Cards": "Cartes de crédit", + "add a new credit card": "Ajouter une nouvelle carte de crédit", + "Account Balance": "Solde du compte", + "Arrears": "Arriérés", + "Billing Succeeded": "Votre carte a été correctement débitée.", + "Confirm Email Succeeded": "L’adresse e-mail a bien été validée, vous êtes redirigé vers le tableau de bord...", + "Confirm Email Failed": "Impossible de confirmer l’adresse e-mail. Merci de contacter support@uber.com si le problème persiste.", + "Credit Card Added": "Carte de crédit ajoutée", + "No Credit Card": "Pas de carte de crédit", + "Mobile Number Confirmed": "Numéro de téléphone confirmé", + "No Confirmed Mobile": "Pas de numéro de téléphone confirmé", + "E-mail Address Confirmed": "Adresse e-mail confirmée", + "No Confirmed E-mail": "Pas d’adresse e-mail confirmée", + 'Reply to sign up text': 'Répondre "GO" au SMS que vous avez reçu à l’inscription.', + "Resend text message": "Renvoyer le SMS", + "Click sign up link": "Cliquez sur le lien contenu dans l’e-mail reçu à l’inscription.", + "Resend email": "Renvoyer l’e-mail", + "Add a credit card to ride": "Ajouter une carte de crédit et vous serez prêt à voyager avec Uber.", + "Your Most Recent Trip": "Votre course la plus récente", + "details": "détails", + "Your Trip History": "Historique de votre trajet", + "Status": "Statut", + "Here's how it works:": "Voici comment ça marche :", + "Show all trips": "Montrer toutes les courses", + "Set your location:": "Définir votre position :", + "App search for address": "Application iPhone/Android : positionner la punaise ou rechercher une adresse", + "SMS text address": "SMS : envoyez votre adresse à UBRCAB (827222)", + "Confirm pickup request": "Validez la commande", + "Uber sends ETA": "Uber envoie un temps d’attente estimé (habituellement entre 5 et 10 minutes)", + "Car arrives": "Lorsque votre voiture arrive, Uber vous en informera encore..", + "Ride to destination": "Montez dans la voiture et donnez votre destination au chauffeur.", + "Thank your driver": "C’est tout ! Remerciez le chauffeur mais souvenez-vous que les pourboires sont compris et qu’il n’est pas nécessaire d’avoir du liquide sur soi.", + "Trip started here": "La course a commencé ici.", + "Trip ended here": "La course s’est terminée ici.", + "Sending Email": "Envoi de l’e-mail...", + "Resend Email Succeeded": "Nous venons d’envoyer l’e-mail. Merci de cliquer sur le lien de confirmation que vous avez reçu.", + "Resend Email Failed": "Il y a eu un problème lors de l’envoi de l’email. Merci de contacter le support si le problème persiste.", + "Resend Text Succeeded": 'Nous venons d’envoyer le SMS. Merci de répondre "GO" au message que vous avez reçu. Il se peut que cela prenne quelques minutes pour que le message arrive sur votre téléphone.', + "Resend Text Failed": "Il y a eu un problème lors de l’envoi du SMS. Merci de contacter le support si le problème persiste.", + "Password Reset Error": "Il y a eu une error lors de la réinitialisation de votre mot de passe.", + "New Password:": "Nouveau mot de passe:", + "Forgot Password Error": "Votre nom d’utilisateur / adresse email ne peut être trouvé. Merci d’utiliser la même qu’à l’inscription.", + "Forgot Password Success": "Merci de consulter votre boîte mail pour suivre la demande de ‘réinitialisation de mot de passe.", + "Forgot Password Enter Email": "Merci de saisir votre adresse email et nous vous enverrons un lien vous permettant de réinitialiser votre mot de passe :", + "Invite friends": "Inviter vos amis", + "Give $ Get $": "Donnez $10, Recevez $10", + "Give $ Get $ Description": "Chaque ami que vous invitez à Uber recevra $10 de crédits Uber. Dès lors qu’une personne que vous aurez invité aura utilisé Uber pour la première, vous recevrez $10 de crédits Uber également !", + "What are you waiting for?": "N’attendez plus ! Lancez les invitations !", + "Tweet": "Tweeter", + "Invite Link": "Envoyez ce lien par email ou messagerie instantanée à vos amis :", + "Enter Promotion Code": "Si vous avez un code promo, saisissez-le ici:", + "Your Active Promotions": "Vos Codes Promos Actifs", + "Code": "Code", + "Details": "Détails", + "Trips Remaining": "Courses restantes", + "Expires": "Expire", + "No Active Promotions": "Vous n’avez pas de code promo actif.", + "Your Available Promotions": "Votres Promos Disponibles", + "Where do you want us to pick you up?": "Où souhaitez-vous que nous vous prenions en charge ?", + "Address to search": "Adresse à rechercher", + "Search": "Chercher", + "Driver Name:": "Nom du chauffeur:", + "Driver #:": "# Chauffeur:", + "Pickup Address:": "Lieu de prise en charge:", + "Add to Favorite Locations": "Ajoutez aux Lieux Favoris", + "Star": "Étoiles", + "Nickname:": "Pseudo", + "Add": "Ajouter", + "Your last trip": "Votre dernière course", + "Please rate your driver:": "Merci de noter votre chauffeur :", + "Comments: (optional)": "Commentaires: (optionnel)", + "Rate Trip": "Notez votre course", + "Pickup time:": "Heure de Prise en Charge :", + "Miles:": "Kilomètres :", + "Trip time:": "Temps de course :", + "Fare:": "Tarif :", + "Favorite Locations": "Lieux Favoris", + "Search Results": "Résultats", + "You have no favorite locations saved.": "Vous n’avez pas de lieux de prise en charge favoris.", + "Loading...": "Chargement...", + "Request Pickup": "Commander ici", + "Cancel Pickup": "Annuler", + "Requesting Closest Driver": "Nous demandons au chauffeur le plus proche de vous prendre en charge...", + "En Route": "Vous êtes actuellement en route...", + "Rate Last Trip": "Merci de noter votre précédent trajet pour faire une autre course.", + "Rate Before Submitting": "Merci de noter votre trajet avant de le valider.", + "Address too short": "L’adresse est trop courte", + "or did you mean": "ou vouliez-vous dire", + "Search Address Failed": "Impossible de trouver l’adresse spécifiée. Merci de saisir une autre adresse proche de l’endroit où vous vous trouvez.", + "Sending pickup request...": "Envoi de la demande de prise en charge...", + "Cancel Request Prompt": "Voulez-vous vraiment annuler votre demande ?", + "Cancel Request Arrived Prompt": 'Voulez-vous vraiment annuler votre demande ? Votre chauffeur est arrivé, vous serez donc facturé de $10 de frais d’annulation. Il pourrait être utile que vous appeliez votre chauffeur maintenant.', + "Favorite Location Nickname Length Error": "Le pseudo doit faire au moins 3 caractères de long", + "Favorite Location Save Succeeded": "Adresse enregistrée !", + "Favorite Location Save Failed": "Impossible d’enregistrer votre adresse. Merci de réessayer ultérieurement.", + "Favorite Location Title": "Adresse favorie <%= id %>", + "Search Location Title": "Recherche d’adresse <%= id %>", + "ETA Message": "Temps d’attente estimé: environ <%= minutes %> minutes", + "Nearest Cab Message": "Le chauffeur le plus proche sera là dans <%= minutes %> minute(s)", + "Arrival ETA Message": "Votre chauffeur arrivera dans <%= minutes %> minute(s)", + "Arriving Now Message": "Votre chauffeur est en approche...", + "Rating Driver Failed": "Impossible de contacter le serveur. Merci de réessayer ultérieurement ou de contacter le support si le problème persiste.", + "settings": "Paramètres", + "Information": "Information", + "Picture": "Photo", + "Change password": "Modifier votre mot de passe", + "Your current Picture": "Votre photo", + "Your Favorite Locations": "Vos lieux favoris", + "You have no favorite locations saved.": "Vous n’avez pas de lieu favori", + "Account Information": "Informations Personnelles", + "Mobile Phone Information": "Informations de Mobile", + "Change Your Password": "Changez votre mot de passe.", + "Country": "Pays", + "Language": "Langue", + "Favorite Location": "Lieu favori", + "No Approximate Address": "Impossible de trouver une adresse même approximative", + "Address:": "Adresse :", + "Information Update Succeeded": "Vos informations ont été mises à jour !", + "Information Update Failed": "Nous n’avons pas pu mettre à jour vos informations. Merci de réessayer dans quelques instants ou de contacter le support si le problème persiste.", + "Location Delete Succeeded": "Adresse supprimée !", + "Location Delete Failed": "Nous n’avons pas pu supprimée votre adresse favorie. Merci de réessayer plus tard ou de contacter le support si le problème persiste.", + "Location Edit Succeeded": "Modifications sauvegardées !", + "Location Edit Failed": "Nous n’avons pas pu sauvegarder vos modifications. Merci de réessayer dans quelques minutes.", + "Picture Update Succeeded": "Votre photo a été mise à jour !", + "Picture Update Failed": "Nous n’avons pas pu mettre à jour votre photo. Merci de réessayer dans quelques instants.", + "Personal Information": "Informations Personnelles", + "Mobile Phone Number": "Numéro de Téléphone Portable", + "Payment Information": "Informations de Facturation", + "Your card will not be charged until you take a trip.": "Votre carte ne sera pas débitée avant votre premier trajet.", + "Card Number": "Numéro de Carte", + "Promotion Code Input Label": "Code promo, code d’invitation ou “deal” acheté en ligne (optionnel)", + "Terms and Conditions": "Conditions Générales", + "HELP": "HELP", + "STOP": "STOP", + "Sign Up Agreement": "En souscrivant, j’accepte les <%= terms_link %> et <%= privacy_link %> et comprends qu’Uber est un outil de commande de chauffeur, et non un transporteur.", + "Sign Up Agreement Error": "Vous devez accepter les Conditions Générales d’utilisation d’Uber Terms and Conditions et la Politique de Confidentialité pour continuer.", + "Message and Data Rates Disclosure": "Les frais d’envoi de SMS et de consommation de données peuvent s’appliquer. Répondez <%= help_string %> au 827-222 pour obtenir de l’aide. Répondez <%= stop_string %> au 827-222 pour ne plus recevoir de SMS. Pour plus d’aide, visitez support.uber.com ou appelez le (866) 576-1039. Opérateurs supportés: AT&T, Sprint, Verizon, T-Mobile, Orange, SFR et Bouygues Telecom.", + "Zip/Postal Code": "Code Postal", + "Expiration Date": "Date D'expiration", + "Security Code": "Code de Sécurité", + "Type of Card": "Type", + "Personal": "Personnel", + "Business": "Entreprise", + "Promotion Code": "Code Promo", + "Legal Information": "Mentions Légales", + "I Agree": "J'accepte.", + "Your Trip": "Votre Course", + "Trip Info": "Informations de la Course", + "Request a fare review": "Demander un contrôle du tarif", + "Fare Review Submitted": "Votre demande de contrôle du tarif a été soumis. Nous reviendrons vers vous rapidement concernant cette demande. Nous nous excusons pour les dérangements éventuellement occasionnés !", + "Fair Price Consideration": "Nous nous engageons à proposer Uber à un tarif juste. Avant de demander un contrôle du tarif, merci de prendre en compte :", + "Your Fare Calculation": "Calcul du Prix", + "Charges": "Coûts", + "Discounts": "Réductions", + "Total Charge": "Coût total", + "Uber pricing information": "Information sur les prix d’Uber", + "Uber Pricing Information Message": "<%= learn_link %> est disponible sur notre site web.", + "GPS Point Capture Disclosure": "A cause d’un nombre limité de coordonnées GPS sauvegardées, les angles de votre trajet sur la carte peuvent apparaître coupés ou arrondis. Ces légères incohérences débouchent sur des distances mesurées plus courtes, ce qui implique toujours un prix du trajet moins élevé.", + "Fare Review Note": "Merci de nous expliquer pourquoi le tarif de cette course nécessite d’être contrôlé. Vos commentaires ci-dessous nous aideront à établir un prix plus juste si nécessaire :", + "Fare Review Error": "Il y a eu une erreur lors de l’envoi de la demande. Assurez-vous d’avoir bien ajouté une description à votre demande." + }; +}).call(this); +}, "views/clients/billing": function(exports, require, module) {(function() { + var clientsBillingTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + clientsBillingTemplate = require('templates/clients/billing'); + exports.ClientsBillingView = (function() { + __extends(ClientsBillingView, UberView); + function ClientsBillingView() { + ClientsBillingView.__super__.constructor.apply(this, arguments); + } + ClientsBillingView.prototype.id = 'billing_view'; + ClientsBillingView.prototype.className = 'view_container'; + ClientsBillingView.prototype.events = { + 'click a#add_card': 'addCard', + 'click .charge_arrear': 'chargeArrear' + }; + ClientsBillingView.prototype.render = function() { + this.RefreshUserInfo(__bind(function() { + var cards, newForm; + this.HideSpinner(); + $(this.el).html(clientsBillingTemplate()); + if (USER.payment_gateway.payment_profiles.length === 0) { + newForm = new app.views.clients.modules.creditcard; + $(this.el).find("#add_card_wrapper").html(newForm.render(0).el); + } else { + cards = new app.views.clients.modules.creditcard; + $("#cards").html(cards.render("all").el); + } + return this.delegateEvents(); + }, this)); + return this; + }; + ClientsBillingView.prototype.addCard = function(e) { + var newCard; + e.preventDefault(); + newCard = new app.views.clients.modules.creditcard; + $('#cards').append(newCard.render("new").el); + return $("a#add_card").hide(); + }; + ClientsBillingView.prototype.chargeArrear = function(e) { + var $el, arrearId, attrs, cardId, options, tryCharge; + e.preventDefault(); + $(".error_message").text(""); + $el = $(e.currentTarget); + arrearId = $el.attr('id'); + cardId = $el.parent().find('#card_to_charge').val(); + this.ShowSpinner('submit'); + tryCharge = new app.models.clientbills({ + id: arrearId + }); + attrs = { + payment_profile_id: cardId, + dataType: 'json' + }; + options = { + success: __bind(function(data, textStatus, jqXHR) { + $el.parent().find(".success_message").text(t("Billing Succeeded")); + $el.hide(); + return $el.parent().find('#card_to_charge').hide(); + }, this), + error: __bind(function(jqXHR, status, errorThrown) { + return $el.parent().find(".error_message").text(JSON.parse(status.responseText).error); + }, this), + complete: __bind(function() { + return this.HideSpinner(); + }, this) + }; + return tryCharge.save(attrs, options); + }; + return ClientsBillingView; + })(); +}).call(this); +}, "views/clients/confirm_email": function(exports, require, module) {(function() { + var clientsConfirmEmailTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + clientsConfirmEmailTemplate = require('templates/clients/confirm_email'); + exports.ClientsConfirmEmailView = (function() { + __extends(ClientsConfirmEmailView, UberView); + function ClientsConfirmEmailView() { + ClientsConfirmEmailView.__super__.constructor.apply(this, arguments); + } + ClientsConfirmEmailView.prototype.id = 'confirm_email_view'; + ClientsConfirmEmailView.prototype.className = 'view_container'; + ClientsConfirmEmailView.prototype.render = function(token) { + var attrs; + $(this.el).html(clientsConfirmEmailTemplate()); + attrs = { + data: { + email_token: token + }, + success: __bind(function(data, textStatus, jqXHR) { + var show_dashboard; + this.HideSpinner(); + show_dashboard = function() { + return app.routers.clients.navigate('!/dashboard', true); + }; + if (data.status === 'OK') { + $('.success_message').show(); + return _.delay(show_dashboard, 3000); + } else if (data.status === 'ALREADY_COMFIRMED') { + $('.already_confirmed_message').show(); + return _.delay(show_dashboard, 3000); + } else { + return $('.error_message').show(); + } + }, this), + error: __bind(function(e) { + this.HideSpinner(); + return $('.error_message').show(); + }, this), + complete: function(status) { + return $('#attempt_text').hide(); + }, + dataType: 'json', + type: 'PUT', + url: "" + API + "/users/self" + }; + $.ajax(attrs); + this.ShowSpinner('submit'); + return this; + }; + return ClientsConfirmEmailView; + })(); +}).call(this); +}, "views/clients/dashboard": function(exports, require, module) {(function() { + var clientsDashboardTemplate; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsDashboardTemplate = require('templates/clients/dashboard'); + exports.ClientsDashboardView = (function() { + var displayFirstTrip; + __extends(ClientsDashboardView, UberView); + function ClientsDashboardView() { + this.showAllTrips = __bind(this.showAllTrips, this); + this.render = __bind(this.render, this); + ClientsDashboardView.__super__.constructor.apply(this, arguments); + } + ClientsDashboardView.prototype.id = 'dashboard_view'; + ClientsDashboardView.prototype.className = 'view_container'; + ClientsDashboardView.prototype.events = { + 'click a.confirmation': 'confirmationClick', + 'click #resend_email': 'resendEmail', + 'click #resend_mobile': 'resendMobile', + 'click #show_all_trips': 'showAllTrips' + }; + ClientsDashboardView.prototype.render = function() { + var displayPage, downloadTrips; + this.HideSpinner(); + displayPage = __bind(function() { + $(this.el).html(clientsDashboardTemplate()); + this.confirmationsSetup(); + return this.RequireMaps(__bind(function() { + if (USER.trips.models[0]) { + if (!USER.trips.models[0].get("points")) { + return USER.trips.models[0].fetch({ + data: { + relationships: 'points' + }, + success: __bind(function() { + this.CacheData("USERtrips", USER.trips); + return displayFirstTrip(); + }, this) + }); + } else { + return displayFirstTrip(); + } + } + }, this)); + }, this); + downloadTrips = __bind(function() { + return this.DownloadUserTrips(displayPage, false, 10); + }, this); + this.RefreshUserInfo(downloadTrips); + return this; + }; + displayFirstTrip = __bind(function() { + var bounds, endPos, map, myOptions, path, polyline, startPos; + myOptions = { + zoom: 12, + mapTypeId: google.maps.MapTypeId.ROADMAP, + zoomControl: false, + rotateControl: false, + panControl: false, + mapTypeControl: false, + scrollwheel: false + }; + if (USER.trips.length === 10) { + $("#show_all_trips").show(); + } + if (USER.trips.length > 0) { + map = new google.maps.Map(document.getElementById("trip_details_map"), myOptions); + bounds = new google.maps.LatLngBounds(); + path = []; + _.each(USER.trips.models[0].get('points'), __bind(function(point) { + path.push(new google.maps.LatLng(point.lat, point.lng)); + return bounds.extend(_.last(path)); + }, this)); + map.fitBounds(bounds); + startPos = new google.maps.Marker({ + position: _.first(path), + map: map, + title: t('Trip started here'), + icon: 'https://uber-static.s3.amazonaws.com/marker_start.png' + }); + endPos = new google.maps.Marker({ + position: _.last(path), + map: map, + title: t('Trip ended here'), + icon: 'https://uber-static.s3.amazonaws.com/marker_end.png' + }); + polyline = new google.maps.Polyline({ + path: path, + strokeColor: '#003F87', + strokeOpacity: 1, + strokeWeight: 5 + }); + return polyline.setMap(map); + } + }, ClientsDashboardView); + ClientsDashboardView.prototype.confirmationsSetup = function() { + var blink, cardForm, element, _ref, _ref2, _ref3, _ref4, _ref5; + blink = function(element) { + var opacity; + opacity = 0.5; + if (element.css('opacity') === "0.5") { + opacity = 1.0; + } + return element.fadeTo(2000, opacity, function() { + return blink(element); + }); + }; + if (((_ref = window.USER) != null ? (_ref2 = _ref.payment_gateway) != null ? (_ref3 = _ref2.payment_profiles) != null ? _ref3.length : void 0 : void 0 : void 0) === 0) { + element = $('#confirmed_credit_card'); + cardForm = new app.views.clients.modules.creditcard; + $('#card.info').append(cardForm.render().el); + blink(element); + } + if (((_ref4 = window.USER) != null ? _ref4.confirm_email : void 0) === false) { + element = $('#confirmed_email'); + blink(element); + } + if ((((_ref5 = window.USER) != null ? _ref5.confirm_mobile : void 0) != null) === false) { + element = $('#confirmed_mobile'); + return blink(element); + } + }; + ClientsDashboardView.prototype.confirmationClick = function(e) { + e.preventDefault(); + $('.info').hide(); + $('#more_info').show(); + switch (e.currentTarget.id) { + case "card": + return $('#card.info').slideToggle(); + case "mobile": + return $('#mobile.info').slideToggle(); + case "email": + return $('#email.info').slideToggle(); + } + }; + ClientsDashboardView.prototype.resendEmail = function(e) { + var $el; + e.preventDefault(); + $el = $(e.currentTarget); + $el.removeAttr('href').prop({ + disabled: true + }); + $el.html(t("Sending Email")); + return $.ajax({ + type: 'GET', + url: API + '/users/request_confirm_email', + data: { + token: USER.token + }, + dataType: 'json', + success: __bind(function(data, textStatus, jqXHR) { + return $el.html(t("Resend Email Succeeded")); + }, this), + error: __bind(function(jqXHR, textStatus, errorThrown) { + return $el.html(t("Resend Email Failed")); + }, this) + }); + }; + ClientsDashboardView.prototype.resendMobile = function(e) { + var $el; + e.preventDefault(); + $el = $(e.currentTarget); + $el.removeAttr('href').prop({ + disabled: true + }); + $el.html("Sending message..."); + return $.ajax({ + type: 'GET', + url: API + '/users/request_confirm_mobile', + data: { + token: USER.token + }, + dataType: 'json', + success: __bind(function(data, textStatus, jqXHR) { + return $el.html(t("Resend Text Succeeded")); + }, this), + error: __bind(function(jqXHR, textStatus, errorThrown) { + return $el.html(t("Resend Text Failed")); + }, this) + }); + }; + ClientsDashboardView.prototype.showAllTrips = function(e) { + e.preventDefault(); + $(e.currentTarget).hide(); + return this.DownloadUserTrips(this.render, true, 1000); + }; + return ClientsDashboardView; + }).call(this); +}).call(this); +}, "views/clients/forgot_password": function(exports, require, module) {(function() { + var clientsForgotPasswordTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + clientsForgotPasswordTemplate = require('templates/clients/forgot_password'); + exports.ClientsForgotPasswordView = (function() { + __extends(ClientsForgotPasswordView, UberView); + function ClientsForgotPasswordView() { + ClientsForgotPasswordView.__super__.constructor.apply(this, arguments); + } + ClientsForgotPasswordView.prototype.id = 'forgotpassword_view'; + ClientsForgotPasswordView.prototype.className = 'view_container modal_view_container'; + ClientsForgotPasswordView.prototype.events = { + "submit #password_reset": "passwordReset", + "click #password_reset_submit": "passwordReset", + "submit #forgot_password": "forgotPassword", + "click #forgot_password_submit": "forgotPassword" + }; + ClientsForgotPasswordView.prototype.render = function(token) { + this.HideSpinner(); + $(this.el).html(clientsForgotPasswordTemplate({ + token: token + })); + this.delegateEvents(); + return this; + }; + ClientsForgotPasswordView.prototype.forgotPassword = function(e) { + var attrs; + e.preventDefault(); + $('.success_message').hide(); + $(".error_message").hide(); + attrs = { + data: { + login: $("#login").val() + }, + success: __bind(function(data, textStatus, jqXHR) { + this.HideSpinner(); + $('.success_message').show(); + return $("#forgot_password").hide(); + }, this), + error: __bind(function(e) { + this.HideSpinner(); + return $('.error_message').show(); + }, this), + dataType: 'json', + type: 'PUT', + url: "" + API + "/users/forgot_password" + }; + $.ajax(attrs); + return this.ShowSpinner('submit'); + }; + ClientsForgotPasswordView.prototype.passwordReset = function(e) { + var attrs; + e.preventDefault(); + attrs = { + data: { + email_token: $("#token").val(), + password: $("#password").val() + }, + success: __bind(function(data, textStatus, jqXHR) { + this.HideSpinner(); + $.cookie('token', data.token); + amplify.store('USERjson', data); + app.refreshMenu(); + return location.hash = '!/dashboard'; + }, this), + error: __bind(function(e) { + this.HideSpinner(); + return $('#error_reset').show(); + }, this), + dataType: 'json', + type: 'PUT', + url: "" + API + "/users/self" + }; + $.ajax(attrs); + return this.ShowSpinner('submit'); + }; + return ClientsForgotPasswordView; + })(); +}).call(this); +}, "views/clients/invite": function(exports, require, module) {(function() { + var clientsInviteTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsInviteTemplate = require('templates/clients/invite'); + exports.ClientsInviteView = (function() { + __extends(ClientsInviteView, UberView); + function ClientsInviteView() { + ClientsInviteView.__super__.constructor.apply(this, arguments); + } + ClientsInviteView.prototype.id = 'invite_view'; + ClientsInviteView.prototype.className = 'view_container'; + ClientsInviteView.prototype.render = function() { + this.ReadUserInfo(); + this.HideSpinner(); + $(this.el).html(clientsInviteTemplate()); + console.log(screen); + return this; + }; + return ClientsInviteView; + })(); +}).call(this); +}, "views/clients/login": function(exports, require, module) {(function() { + var clientsLoginTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsLoginTemplate = require('templates/clients/login'); + exports.ClientsLoginView = (function() { + __extends(ClientsLoginView, UberView); + function ClientsLoginView() { + ClientsLoginView.__super__.constructor.apply(this, arguments); + } + ClientsLoginView.prototype.id = 'login_view'; + ClientsLoginView.prototype.className = 'view_container modal_view_container'; + ClientsLoginView.prototype.events = { + 'submit form': 'authenticate', + 'click button': 'authenticate' + }; + ClientsLoginView.prototype.initialize = function() { + _.bindAll(this, 'render'); + return this.render(); + }; + ClientsLoginView.prototype.render = function() { + this.HideSpinner(); + $(this.el).html(clientsLoginTemplate()); + this.delegateEvents(); + return this.place(); + }; + ClientsLoginView.prototype.authenticate = function(e) { + e.preventDefault(); + return $.ajax({ + type: 'POST', + url: API + '/auth/web_login/client', + data: { + login: $("#login").val(), + password: $("#password").val() + }, + dataType: 'json', + success: function(data, textStatus, jqXHR) { + $.cookie('user', JSON.stringify(data)); + $.cookie('token', data.token); + amplify.store('USERjson', data); + $('header').html(app.views.shared.menu.render().el); + return app.routers.clients.navigate('!/dashboard', true); + }, + error: function(jqXHR, textStatus, errorThrown) { + $.cookie('user', null); + $.cookie('token', null); + if (jqXHR.status === 403) { + $.cookie('redirected_user', JSON.stringify(JSON.parse(jqXHR.responseText).error_obj), { + domain: '.uber.com' + }); + window.location = 'http://partners.uber.com/'; + } + return $('.error_message').html(JSON.parse(jqXHR.responseText).error).hide().fadeIn(); + } + }); + }; + return ClientsLoginView; + })(); +}).call(this); +}, "views/clients/modules/credit_card": function(exports, require, module) {(function() { + var creditCardTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + creditCardTemplate = require('templates/clients/modules/credit_card'); + exports.CreditCardView = (function() { + __extends(CreditCardView, UberView); + function CreditCardView() { + CreditCardView.__super__.constructor.apply(this, arguments); + } + CreditCardView.prototype.id = 'creditcard_view'; + CreditCardView.prototype.className = 'module_container'; + CreditCardView.prototype.events = { + 'submit #credit_card_form': 'processNewCard', + 'click #new_card': 'processNewCard', + 'change #card_number': 'showCardType', + 'click .edit_card_show': 'showEditCard', + 'click .edit_card': 'editCard', + 'click .delete_card': 'deleteCard', + 'click .make_default': 'makeDefault', + 'change .use_case': 'saveUseCase' + }; + CreditCardView.prototype.initialize = function() { + return app.collections.paymentprofiles.bind("refresh", __bind(function() { + return this.RefreshUserInfo(__bind(function() { + this.render("all"); + return this.HideSpinner(); + }, this)); + }, this)); + }; + CreditCardView.prototype.render = function(cards) { + if (cards == null) { + cards = "new"; + } + if (cards === "all") { + app.collections.paymentprofiles.reset(USER.payment_gateway.payment_profiles); + cards = app.collections.paymentprofiles; + } + $(this.el).html(creditCardTemplate({ + cards: cards + })); + return this; + }; + CreditCardView.prototype.processNewCard = function(e) { + var $el, attrs, model, options; + e.preventDefault(); + this.ClearGlobalStatus(); + $el = $("#credit_card_form"); + $el.find('.error_message').html(""); + attrs = { + card_number: $el.find('#card_number').val(), + card_code: $el.find('#card_code').val(), + card_expiration_month: $el.find('#card_expiration_month').val(), + card_expiration_year: $el.find('#card_expiration_year').val(), + use_case: $el.find('#use_case').val(), + "default": $el.find('#default_check').prop("checked") + }; + options = { + statusCode: { + 200: __bind(function(e) { + this.HideSpinner(); + $('#cc_form_wrapper').hide(); + app.collections.paymentprofiles.trigger("refresh"); + $(this.el).remove(); + $("a#add_card").show(); + return $('section').html(app.views.clients.billing.render().el); + }, this), + 406: __bind(function(e) { + var error, errors, _i, _len, _ref, _results; + this.HideSpinner(); + errors = JSON.parse(e.responseText); + _ref = _.keys(errors); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + error = _ref[_i]; + _results.push(error === "top_of_form" ? $("#top_of_form").html(errors[error]) : $("#credit_card_form").find("#" + error).parent().find(".error_message").html(errors[error])); + } + return _results; + }, this), + 420: __bind(function(e) { + this.HideSpinner(); + return $("#top_of_form").html(t("Unable to Verify Card")); + }, this) + } + }; + this.ShowSpinner("submit"); + model = new app.models.paymentprofile; + model.save(attrs, options); + return app.collections.paymentprofiles.add(model); + }; + CreditCardView.prototype.showCardType = function(e) { + var $el, reAmerica, reDiscover, reMaster, reVisa, validCard; + reVisa = /^4\d{3}-?\d{4}-?\d{4}-?\d{4}$/; + reMaster = /^5[1-5]\d{2}-?\d{4}-?\d{4}-?\d{4}$/; + reAmerica = /^6011-?\d{4}-?\d{4}-?\d{4}$/; + reDiscover = /^3[4,7]\d{13}$/; + $el = $("#card_logos"); + validCard = false; + if (e.currentTarget.value.match(reVisa)) { + validCard = true; + } else if (e.currentTarget.value.match(reMaster)) { + $el.css('background-position', "-60px"); + validCard = true; + } else if (e.currentTarget.value.match(reAmerica)) { + $el.css('background-position', "-120px"); + validCard = true; + } else if (e.currentTarget.value.match(reDiscover)) { + $el.css('background-position', "-180px"); + validCard = true; + } + if (validCard) { + $el.css('width', "60px"); + return $el.css('margin-left', "180px"); + } else { + $el.css('width', "250px"); + return $el.css('margin-left', "80px"); + } + }; + CreditCardView.prototype.showEditCard = function(e) { + var $el, id; + e.preventDefault(); + $el = $(e.currentTarget); + if ($el.html() === t("Edit")) { + id = $el.html(t("Cancel")).parents("tr").attr("id").substring(1); + return $("#e" + id).show(); + } else { + id = $el.html(t("Edit")).parents("tr").attr("id").substring(1); + return $("#e" + id).hide(); + } + }; + CreditCardView.prototype.editCard = function(e) { + var $el, attrs, id, options; + e.preventDefault(); + this.ClearGlobalStatus(); + $el = $(e.currentTarget).parents("td"); + id = $el.parents("tr").attr("id").substring(1); + $el.attr('disabled', 'disabled'); + this.ShowSpinner('submit'); + attrs = { + card_expiration_month: $el.find('#card_expiration_month').val(), + card_expiration_year: $el.find('#card_expiration_year').val(), + card_code: $el.find('#card_code').val() + }; + options = { + success: __bind(function(response) { + this.HideSpinner(); + this.ShowSuccess(t("Credit Card Update Succeeded")); + $("#e" + id).hide(); + $("#d" + id).find(".edit_card_show").html(t("Edit")); + return app.collections.paymentprofiles.trigger("refresh"); + }, this), + error: __bind(function(e) { + this.HideSpinner(); + this.ShowError(t("Credit Card Update Failed")); + return $el.removeAttr('disabled'); + }, this) + }; + app.collections.paymentprofiles.models[id].set(attrs); + return app.collections.paymentprofiles.models[id].save({}, options); + }; + CreditCardView.prototype.deleteCard = function(e) { + var $el, id, options; + e.preventDefault(); + $el = $(e.currentTarget).parents("td"); + id = $el.parents("tr").attr("id").substring(1); + this.ClearGlobalStatus(); + this.ShowSpinner('submit'); + options = { + success: __bind(function(response) { + this.ShowSuccess(t("Credit Card Delete Succeeded")); + $("form").hide(); + app.collections.paymentprofiles.trigger("refresh"); + return $('section').html(app.views.clients.billing.render().el); + }, this), + error: __bind(function(xhr, e) { + this.HideSpinner(); + return this.ShowError(t("Credit Card Delete Failed")); + }, this) + }; + return app.collections.paymentprofiles.models[id].destroy(options); + }; + CreditCardView.prototype.saveUseCase = function(e) { + var $el, attrs, id, options, use_case; + this.ClearGlobalStatus(); + $el = $(e.currentTarget); + use_case = $el.val(); + id = $el.parents("tr").attr("id").substring(1); + attrs = { + use_case: use_case + }; + options = { + success: __bind(function(response) { + return this.ShowSuccess(t("Credit Card Update Category Succeeded")); + }, this), + error: __bind(function(e) { + return this.ShowError(t("Credit Card Update Category Failed")); + }, this) + }; + app.collections.paymentprofiles.models[id].set(attrs); + return app.collections.paymentprofiles.models[id].save({}, options); + }; + CreditCardView.prototype.makeDefault = function(e) { + var $el, attrs, id, options; + e.preventDefault(); + this.ClearGlobalStatus(); + $el = $(e.currentTarget).parents("td"); + id = $el.parents("tr").attr("id").substring(1); + attrs = { + "default": true + }; + options = { + success: __bind(function(response) { + this.ShowSuccess(t("Credit Card Update Default Succeeded")); + return app.collections.paymentprofiles.trigger("refresh"); + }, this), + error: __bind(function(e) { + return this.ShowError(t("Credit Card Update Default Failed")); + }, this) + }; + app.collections.paymentprofiles.models[id].set(attrs); + return app.collections.paymentprofiles.models[id].save({}, options); + }; + return CreditCardView; + })(); +}).call(this); +}, "views/clients/promotions": function(exports, require, module) {(function() { + var clientsPromotionsTemplate; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsPromotionsTemplate = require('templates/clients/promotions'); + exports.ClientsPromotionsView = (function() { + __extends(ClientsPromotionsView, UberView); + function ClientsPromotionsView() { + this.render = __bind(this.render, this); + ClientsPromotionsView.__super__.constructor.apply(this, arguments); + } + ClientsPromotionsView.prototype.id = 'promotions_view'; + ClientsPromotionsView.prototype.className = 'view_container'; + ClientsPromotionsView.prototype.events = { + 'submit form': 'submitPromo', + 'click button': 'submitPromo' + }; + ClientsPromotionsView.prototype.initialize = function() { + if (this.model) { + return this.RefreshUserInfo(this.render); + } + }; + ClientsPromotionsView.prototype.render = function() { + var renderTemplate; + this.ReadUserInfo(); + renderTemplate = __bind(function() { + $(this.el).html(clientsPromotionsTemplate({ + promos: window.USER.unexpired_client_promotions || [] + })); + return this.HideSpinner(); + }, this); + this.DownloadUserPromotions(renderTemplate); + return this; + }; + ClientsPromotionsView.prototype.submitPromo = function(e) { + var attrs, model, options, refreshTable; + e.preventDefault(); + this.ClearGlobalStatus(); + refreshTable = __bind(function() { + $('section').html(this.render().el); + return this.HideSpinner(); + }, this); + attrs = { + code: $('#code').val() + }; + options = { + success: __bind(function(response) { + this.HideSpinner(); + if (response.get('first_name')) { + return this.ShowSuccess("Your promotion has been applied in the form of an account credit. Click here to check your balance."); + } else { + this.ShowSuccess("Your promotion has successfully been applied"); + return this.RefreshUserInfo(this.render, true); + } + }, this), + statusCode: { + 400: __bind(function(e) { + this.ShowError(JSON.parse(e.responseText).error); + return this.HideSpinner(); + }, this) + } + }; + this.ShowSpinner("submit"); + model = new app.models.promotions; + return model.save(attrs, options); + }; + return ClientsPromotionsView; + })(); +}).call(this); +}, "views/clients/request": function(exports, require, module) {(function() { + var clientsRequestTemplate; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsRequestTemplate = require('templates/clients/request'); + exports.ClientsRequestView = (function() { + __extends(ClientsRequestView, UberView); + function ClientsRequestView() { + this.AjaxCall = __bind(this.AjaxCall, this); + this.AskDispatch = __bind(this.AskDispatch, this); + this.removeMarkers = __bind(this.removeMarkers, this); + this.displaySearchLoc = __bind(this.displaySearchLoc, this); + this.displayFavLoc = __bind(this.displayFavLoc, this); + this.showFavLoc = __bind(this.showFavLoc, this); + this.addToFavLoc = __bind(this.addToFavLoc, this); + this.removeCabs = __bind(this.removeCabs, this); + this.requestRide = __bind(this.requestRide, this); + this.rateTrip = __bind(this.rateTrip, this); + this.locationChange = __bind(this.locationChange, this); + this.panToLocation = __bind(this.panToLocation, this); + this.clickLocation = __bind(this.clickLocation, this); + this.searchLocation = __bind(this.searchLocation, this); + this.mouseoutLocation = __bind(this.mouseoutLocation, this); + this.mouseoverLocation = __bind(this.mouseoverLocation, this); + this.fetchTripDetails = __bind(this.fetchTripDetails, this); + this.submitRating = __bind(this.submitRating, this); + this.setStatus = __bind(this.setStatus, this); + this.initialize = __bind(this.initialize, this); + ClientsRequestView.__super__.constructor.apply(this, arguments); + } + ClientsRequestView.prototype.id = 'request_view'; + ClientsRequestView.prototype.className = 'view_container'; + ClientsRequestView.prototype.pollInterval = 2 * 1000; + ClientsRequestView.prototype.events = { + "submit #search_form": "searchAddress", + "click .locations_link": "locationLinkHandle", + "mouseover .location_row": "mouseoverLocation", + "mouseout .location_row": "mouseoutLocation", + "click .location_row": "clickLocation", + "click #search_location": "searchLocation", + "click #pickupHandle": "pickupHandle", + "click .stars": "rateTrip", + "submit #rating_form": "submitRating", + "click #addToFavButton": "showFavLoc", + "click #favLocNickname": "selectInputText", + "submit #favLoc_form": "addToFavLoc" + }; + ClientsRequestView.prototype.status = ""; + ClientsRequestView.prototype.pickupMarker = "https://uber-static.s3.amazonaws.com/pickup_marker.png"; + ClientsRequestView.prototype.cabMarker = "https://uber-static.s3.amazonaws.com/cab_marker.png"; + ClientsRequestView.prototype.initialize = function() { + var displayCabs; + displayCabs = __bind(function() { + return this.AskDispatch("NearestCab"); + }, this); + this.showCabs = _.throttle(displayCabs, this.pollInterval); + return this.numSearchToDisplay = 1; + }; + ClientsRequestView.prototype.setStatus = function(status) { + var autocomplete; + if (this.status === status) { + return; + } + try { + google.maps.event.trigger(this.map, 'resize'); + } catch (_e) {} + switch (status) { + case "init": + this.AskDispatch("StatusClient"); + this.status = "init"; + return this.ShowSpinner("load"); + case "ready": + this.HideSpinner(); + $(".panel").hide(); + $("#top_bar").fadeIn(); + $("#location_panel").fadeIn(); + $("#location_panel_control").fadeIn(); + $("#pickupHandle").attr("class", "button_green").fadeIn().find("span").html(t("Request Pickup")); + this.pickup_icon.setDraggable(true); + this.map.panTo(this.pickup_icon.getPosition()); + this.showCabs(); + try { + this.pickup_icon.setMap(this.map); + this.displayFavLoc(); + autocomplete = new google.maps.places.Autocomplete(document.getElementById('address'), { + types: ['geocode'] + }); + autocomplete.bindTo('bounds', this.map); + } catch (_e) {} + return this.status = "ready"; + case "searching": + this.HideSpinner(); + this.removeMarkers(); + $(".panel").hide(); + $("#top_bar").fadeOut(); + $("#status_message").html(t("Requesting Closest Driver")); + $("#pickupHandle").attr("class", "button_red").fadeIn().find("span").html(t("Cancel Pickup")); + this.pickup_icon.setDraggable(false); + this.pickup_icon.setMap(this.map); + return this.status = "searching"; + case "waiting": + this.HideSpinner(); + this.removeMarkers(); + $(".panel").hide(); + $("#top_bar").fadeOut(); + $("#pickupHandle").attr("class", "button_red").fadeIn().find("span").html(t("Cancel Pickup")); + $("#waiting_riding").fadeIn(); + this.pickup_icon.setDraggable(false); + this.pickup_icon.setMap(this.map); + return this.status = "waiting"; + case "arriving": + this.HideSpinner(); + this.removeMarkers(); + $(".panel").hide(); + $("#top_bar").fadeOut(); + $("#pickupHandle").attr("class", "button_red").fadeIn().find("span").html(t("Cancel Pickup")); + $("#waiting_riding").fadeIn(); + this.pickup_icon.setDraggable(false); + this.pickup_icon.setMap(this.map); + return this.status = "arriving"; + case "riding": + this.HideSpinner(); + this.removeMarkers(); + $(".panel").hide(); + $("#top_bar").fadeOut(); + $("#pickupHandle").fadeIn().attr("class", "button_red").find("span").html(t("Cancel Pickup")); + $("#waiting_riding").fadeIn(); + this.pickup_icon.setDraggable(false); + this.status = "riding"; + return $("#status_message").html(t("En Route")); + case "rate": + this.HideSpinner(); + $(".panel").hide(); + $("#pickupHandle").fadeOut(); + $("#trip_completed_panel").fadeIn(); + $('#status_message').html(t("Rate Last Trip")); + return this.status = "rate"; + } + }; + ClientsRequestView.prototype.render = function() { + this.ReadUserInfo(); + this.HideSpinner(); + this.ShowSpinner("load"); + $(this.el).html(clientsRequestTemplate()); + this.cabs = []; + this.RequireMaps(__bind(function() { + var center, myOptions, streetViewPano; + center = new google.maps.LatLng(37.7749295, -122.4194155); + this.markers = []; + this.pickup_icon = new google.maps.Marker({ + position: center, + draggable: true, + clickable: true, + icon: this.pickupMarker + }); + this.geocoder = new google.maps.Geocoder(); + myOptions = { + zoom: 12, + center: center, + mapTypeId: google.maps.MapTypeId.ROADMAP, + rotateControl: false, + rotateControl: false, + panControl: false + }; + this.map = new google.maps.Map($(this.el).find("#map_wrapper_right")[0], myOptions); + if (this.status === "ready") { + this.pickup_icon.setMap(this.map); + } + if (geo_position_js.init()) { + geo_position_js.getCurrentPosition(__bind(function(data) { + var location; + location = new google.maps.LatLng(data.coords.latitude, data.coords.longitude); + this.pickup_icon.setPosition(location); + this.map.panTo(location); + return this.map.setZoom(16); + }, this)); + } + this.setStatus("init"); + streetViewPano = this.map.getStreetView(); + google.maps.event.addListener(streetViewPano, 'visible_changed', __bind(function() { + if (streetViewPano.getVisible()) { + this.pickupMarker = "https://uber-static.s3.amazonaws.com/pickup_marker_large.png"; + this.cabMarker = "https://uber-static.s3.amazonaws.com/cab_marker_large.png"; + } else { + this.pickupMarker = "https://uber-static.s3.amazonaws.com/pickup_marker.png"; + this.cabMarker = "https://uber-static.s3.amazonaws.com/cab_marker.png"; + } + this.pickup_icon.setIcon(this.pickupMarker); + return _.each(this.cabs, __bind(function(cab) { + return cab.setIcon(this.cabMarker); + }, this)); + }, this)); + if (this.status === "ready") { + return this.displayFavLoc(); + } + }, this)); + return this; + }; + ClientsRequestView.prototype.submitRating = function(e) { + var $el, message, rating; + e.preventDefault(); + $el = $(e.currentTarget); + rating = 0; + _(5).times(function(num) { + if ($el.find(".stars#" + (num + 1)).attr("src") === "/web/img/star_active.png") { + return rating = num + 1; + } + }); + if (rating === 0) { + $("#status_message").html("").html(t("Rate Before Submitting")); + } else { + this.ShowSpinner("submit"); + this.AskDispatch("RatingDriver", { + rating: rating + }); + } + message = $el.find("#comments").val().toString(); + if (message.length > 5) { + return this.AskDispatch("Feedback", { + message: message + }); + } + }; + ClientsRequestView.prototype.fetchTripDetails = function(id) { + var trip; + trip = new app.models.trip({ + id: id + }); + return trip.fetch({ + data: { + relationships: 'points,driver,city' + }, + dataType: 'json', + success: __bind(function() { + var bounds, endPos, path, polyline, startPos; + bounds = new google.maps.LatLngBounds(); + path = []; + _.each(trip.get('points'), __bind(function(point) { + path.push(new google.maps.LatLng(point.lat, point.lng)); + return bounds.extend(_.last(path)); + }, this)); + startPos = new google.maps.Marker({ + position: _.first(path), + map: this.map, + title: t("Trip started here"), + icon: 'https://uber-static.s3.amazonaws.com/carstart.png' + }); + endPos = new google.maps.Marker({ + position: _.last(path), + map: this.map, + title: t("Trip ended here"), + icon: 'https://uber-static.s3.amazonaws.com/carstop.png' + }); + polyline = new google.maps.Polyline({ + path: path, + strokeColor: '#003F87', + strokeOpacity: 1, + strokeWeight: 5 + }); + polyline.setMap(this.map); + this.map.fitBounds(bounds); + $("#tripTime").html(app.helpers.parseDateTime(trip.get('pickup_local_time'), trip.get('city.timezone'))); + $("#tripDist").html(app.helpers.RoundNumber(trip.get('distance'), 2)); + $("#tripDur").html(app.helpers.FormatSeconds(trip.get('duration'))); + return $("#tripFare").html(app.helpers.FormatCurrency(trip.get('fare'))); + }, this) + }); + }; + ClientsRequestView.prototype.searchAddress = function(e) { + var $locationsDiv, address, alphabet, bounds, showResults; + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + try { + e.preventDefault(); + } catch (_e) {} + $('.error_message').html(""); + $locationsDiv = $("
"); + address = $('#address').val(); + bounds = new google.maps.LatLngBounds(); + if (address.length < 5) { + $('#status_message').html(t("Address too short")).fadeIn(); + return false; + } + showResults = __bind(function(address, index) { + var first_cell, row, second_cell; + if (index < this.numSearchToDisplay) { + first_cell = "
" + address.formatted_address + "
" + (t('or did you mean')) + "
" + address.formatted_address + "
+ + + +
Rod VaggGitHub/rvaggTwitter/@rvagg
Benjamin ByholmGitHub/kkoopa
Trevor NorrisGitHub/trevnorrisTwitter/@trevnorris
+ +Licence & copyright +----------------------- + +Copyright (c) 2013 Rod Vagg & NAN contributors (listed above). + +Native Abstractions for Node.js is licensed under an MIT +no-false-attribs license. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE file for more details. diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/nan.h b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/nan.h new file mode 100644 index 0000000..0aca864 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/nan.h @@ -0,0 +1,876 @@ +/********************************************************************************** + * NAN - Native Abstractions for Node.js + * + * Copyright (c) 2013 NAN contributors: + * - Rod Vagg + * - Benjamin Byholm + * - Trevor Norris + * + * MIT +no-false-attribs License + * + * Version 0.3.0 (current Node unstable: 0.11.5, Node stable: 0.10.16) + * + * ChangeLog: + * * 0.3.1 Aug 20 2013 + * - fix "not all control paths return a value" compile warning on some platforms + * + * * 0.3.0 Aug 19 2013 + * - Made NAN work with NPM + * - Lots of fixes to NanFromV8String, pulling in features from new Node core + * - Changed node::encoding to Nan::Encoding in NanFromV8String to unify the API + * - Added optional error number argument for NanThrowError() + * - Added NanInitPersistent() + * - Added NanReturnNull() and NanReturnEmptyString() + * - Added NanLocker and NanUnlocker + * - Added missing scopes + * - Made sure to clear disposed Persistent handles + * - Changed NanAsyncWorker to allocate error messages on the heap + * - Changed NanThrowError(Local) to NanThrowError(Handle) + * - Fixed leak in NanAsyncWorker when errmsg is used + * + * * 0.2.2 Aug 5 2013 + * - Fixed usage of undefined variable with node::BASE64 in NanFromV8String() + * + * * 0.2.1 Aug 5 2013 + * - Fixed 0.8 breakage, node::BUFFER encoding type not available in 0.8 for + * NanFromV8String() + * + * * 0.2.0 Aug 5 2013 + * - Added NAN_PROPERTY_GETTER, NAN_PROPERTY_SETTER, NAN_PROPERTY_ENUMERATOR, + * NAN_PROPERTY_DELETER, NAN_PROPERTY_QUERY + * - Extracted _NAN_METHOD_ARGS, _NAN_GETTER_ARGS, _NAN_SETTER_ARGS, + * _NAN_PROPERTY_GETTER_ARGS, _NAN_PROPERTY_SETTER_ARGS, + * _NAN_PROPERTY_ENUMERATOR_ARGS, _NAN_PROPERTY_DELETER_ARGS, + * _NAN_PROPERTY_QUERY_ARGS + * - Added NanGetInternalFieldPointer, NanSetInternalFieldPointer + * - Added NAN_WEAK_CALLBACK, NAN_WEAK_CALLBACK_OBJECT, + * NAN_WEAK_CALLBACK_DATA, NanMakeWeak + * - Renamed THROW_ERROR to _NAN_THROW_ERROR + * - Added NanNewBufferHandle(char*, size_t, node::smalloc::FreeCallback, void*) + * - Added NanBufferUse(char*, uint32_t) + * - Added NanNewContextHandle(v8::ExtensionConfiguration*, + * v8::Handle, v8::Handle) + * - Fixed broken NanCallback#GetFunction() + * - Added optional encoding and size arguments to NanFromV8String() + * - Added NanGetPointerSafe() and NanSetPointerSafe() + * - Added initial test suite (to be expanded) + * - Allow NanUInt32OptionValue to convert any Number object + * + * * 0.1.0 Jul 21 2013 + * - Added `NAN_GETTER`, `NAN_SETTER` + * - Added `NanThrowError` with single Local argument + * - Added `NanNewBufferHandle` with single uint32_t argument + * - Added `NanHasInstance(Persistent&, Handle)` + * - Added `Local NanCallback#GetFunction()` + * - Added `NanCallback#Call(int, Local[])` + * - Deprecated `NanCallback#Run(int, Local[])` in favour of Call + * + * See https://github.com/rvagg/nan for the latest update to this file + **********************************************************************************/ + +#ifndef NAN_H +#define NAN_H + +#include +#include +#include + +// some generic helpers + +template static inline bool NanSetPointerSafe(T *var, T val) { + if (var) { + *var = val; + return true; + } else { + return false; + } +} + +template static inline T NanGetPointerSafe( + T *var, + T fallback = reinterpret_cast(0)) { + if (var) { + return *var; + } else { + return fallback; + } +} + +#define NanSymbol(value) v8::String::NewSymbol(value) + +static inline bool NanBooleanOptionValue( + v8::Local optionsObj + , v8::Handle opt, bool def) { + + if (def) { + return optionsObj.IsEmpty() + || !optionsObj->Has(opt) + || optionsObj->Get(opt)->BooleanValue(); + } else { + return !optionsObj.IsEmpty() + && optionsObj->Has(opt) + && optionsObj->Get(opt)->BooleanValue(); + } +} + +static inline bool NanBooleanOptionValue( + v8::Local optionsObj + , v8::Handle opt) { + return NanBooleanOptionValue(optionsObj, opt, false); +} + +static inline uint32_t NanUInt32OptionValue( + v8::Local optionsObj + , v8::Handle opt + , uint32_t def) { + + return !optionsObj.IsEmpty() + && optionsObj->Has(opt) + && optionsObj->Get(opt)->IsNumber() + ? optionsObj->Get(opt)->Uint32Value() + : def; +} + +#if (NODE_MODULE_VERSION > 0x000B) +// Node 0.11+ (0.11.3 and below won't compile with these) + +static v8::Isolate* nan_isolate = v8::Isolate::GetCurrent(); + +# define _NAN_METHOD_ARGS const v8::FunctionCallbackInfo& args +# define NAN_METHOD(name) void name(_NAN_METHOD_ARGS) +# define _NAN_GETTER_ARGS const v8::PropertyCallbackInfo& args +# define NAN_GETTER(name) \ + void name(v8::Local property, _NAN_GETTER_ARGS) +# define _NAN_SETTER_ARGS const v8::PropertyCallbackInfo& args +# define NAN_SETTER(name) \ + void name( \ + v8::Local property \ + , v8::Local value \ + , _NAN_SETTER_ARGS) +# define _NAN_PROPERTY_GETTER_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_GETTER(name) \ + void name(v8::Local property \ + , _NAN_PROPERTY_GETTER_ARGS) +# define _NAN_PROPERTY_SETTER_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_SETTER(name) \ + void name(v8::Local property \ + , v8::Local value \ + , _NAN_PROPERTY_SETTER_ARGS) +# define _NAN_PROPERTY_ENUMERATOR_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_ENUMERATOR(name) \ + void name(_NAN_PROPERTY_ENUMERATOR_ARGS) +# define _NAN_PROPERTY_DELETER_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_DELETER(name) \ + void name( \ + v8::Local property \ + , _NAN_PROPERTY_DELETER_ARGS) +# define _NAN_PROPERTY_QUERY_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_QUERY(name) \ + void name(v8::Local property, _NAN_PROPERTY_QUERY_ARGS) +# define NanGetInternalFieldPointer(object, index) \ + object->GetAlignedPointerFromInternalField(index) +# define NanSetInternalFieldPointer(object, index, value) \ + object->SetAlignedPointerInInternalField(index, value) + +# define NAN_WEAK_CALLBACK(type, name) \ + void name( \ + v8::Isolate* isolate, \ + v8::Persistent* object, \ + type data) +# define NAN_WEAK_CALLBACK_OBJECT (*object) +# define NAN_WEAK_CALLBACK_DATA(type) ((type) data) + +# define NanScope() v8::HandleScope scope(nan_isolate) +# define NanLocker() v8::Locker locker(nan_isolate) +# define NanUnlocker() v8::Unlocker unlocker(nan_isolate) +# define NanReturnValue(value) return args.GetReturnValue().Set(value) +# define NanReturnUndefined() return +# define NanReturnNull() return args.GetReturnValue().SetNull() +# define NanReturnEmptyString() return args.GetReturnValue().SetEmptyString() +# define NanAssignPersistent(type, handle, obj) handle.Reset(nan_isolate, obj) +# define NanInitPersistent(type, name, obj) \ + v8::Persistent name(nan_isolate, obj) +# define NanObjectWrapHandle(obj) obj->handle() +# define NanMakeWeak(handle, parameter, callback) \ + handle.MakeWeak(nan_isolate, parameter, callback) + +# define _NAN_THROW_ERROR(fun, errmsg) \ + do { \ + NanScope(); \ + v8::ThrowException(fun(v8::String::New(errmsg))); \ + } while (0); + + inline static void NanThrowError(const char* errmsg) { + _NAN_THROW_ERROR(v8::Exception::Error, errmsg); + } + + inline static void NanThrowError(v8::Handle error) { + NanScope(); + v8::ThrowException(error); + } + + inline static void NanThrowError(const char *msg, const int errorNumber) { + v8::Local err = v8::Exception::Error(v8::String::New(msg)); + v8::Local obj = err.As(); + obj->Set(v8::String::New("code"), v8::Int32::New(errorNumber)); + NanThrowError(err); + } + + inline static void NanThrowTypeError(const char* errmsg) { + _NAN_THROW_ERROR(v8::Exception::TypeError, errmsg); + } + + inline static void NanThrowRangeError(const char* errmsg) { + _NAN_THROW_ERROR(v8::Exception::RangeError, errmsg); + } + + template static inline void NanDispose(v8::Persistent &handle) { + handle.Dispose(nan_isolate); + handle.Clear(); + } + + static inline v8::Local NanNewBufferHandle ( + char *data, + size_t length, + node::smalloc::FreeCallback callback, + void *hint) { + return node::Buffer::New(data, length, callback, hint); + } + + static inline v8::Local NanNewBufferHandle ( + char *data, uint32_t size) { + return node::Buffer::New(data, size); + } + + static inline v8::Local NanNewBufferHandle (uint32_t size) { + return node::Buffer::New(size); + } + + static inline v8::Local NanBufferUse(char* data, uint32_t size) { + return node::Buffer::Use(data, size); + } + + template + inline v8::Local NanPersistentToLocal( + const v8::Persistent& persistent) { + if (persistent.IsWeak()) { + return v8::Local::New(nan_isolate, persistent); + } else { + return *reinterpret_cast*>( + const_cast*>(&persistent)); + } + } + + inline bool NanHasInstance( + v8::Persistent& function_template + , v8::Handle value) { + return NanPersistentToLocal(function_template)->HasInstance(value); + } + + static inline v8::Local NanNewContextHandle( + v8::ExtensionConfiguration* extensions = NULL, + v8::Handle tmpl = v8::Handle(), + v8::Handle obj = v8::Handle()) { + return v8::Local::New(nan_isolate, v8::Context::New( + nan_isolate, extensions, tmpl, obj)); + } + +#else +// Node 0.8 and 0.10 + +# define _NAN_METHOD_ARGS const v8::Arguments& args +# define NAN_METHOD(name) v8::Handle name(_NAN_METHOD_ARGS) +# define _NAN_GETTER_ARGS const v8::AccessorInfo &args +# define NAN_GETTER(name) \ + v8::Handle name(v8::Local property, _NAN_GETTER_ARGS) +# define _NAN_SETTER_ARGS const v8::AccessorInfo &args +# define NAN_SETTER(name) \ + void name( \ + v8::Local property \ + , v8::Local value \ + , _NAN_SETTER_ARGS) +# define _NAN_PROPERTY_GETTER_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_GETTER(name) \ + v8::Handle name(v8::Local property \ + , _NAN_PROPERTY_GETTER_ARGS) +# define _NAN_PROPERTY_SETTER_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_SETTER(name) \ + v8::Handle name(v8::Local property \ + , v8::Local value \ + , _NAN_PROPERTY_SETTER_ARGS) +# define _NAN_PROPERTY_ENUMERATOR_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_ENUMERATOR(name) \ + v8::Handle name(_NAN_PROPERTY_ENUMERATOR_ARGS) +# define _NAN_PROPERTY_DELETER_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_DELETER(name) \ + v8::Handle name( \ + v8::Local property \ + , _NAN_PROPERTY_DELETER_ARGS) +# define _NAN_PROPERTY_QUERY_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_QUERY(name) \ + v8::Handle name( \ + v8::Local property \ + , _NAN_PROPERTY_QUERY_ARGS) + +# define NanGetInternalFieldPointer(object, index) \ + object->GetPointerFromInternalField(index) +# define NanSetInternalFieldPointer(object, index, value) \ + object->SetPointerInInternalField(index, value) +# define NAN_WEAK_CALLBACK(type, name) void name( \ + v8::Persistent object, \ + void *data) +# define NAN_WEAK_CALLBACK_OBJECT object +# define NAN_WEAK_CALLBACK_DATA(type) ((type) data) + +# define NanScope() v8::HandleScope scope +# define NanLocker() v8::Locker locker +# define NanUnlocker() v8::Unlocker unlocker +# define NanReturnValue(value) return scope.Close(value) +# define NanReturnUndefined() return v8::Undefined() +# define NanReturnNull() return v8::Null() +# define NanReturnEmptyString() return v8::String::Empty() +# define NanInitPersistent(type, name, obj) \ + v8::Persistent name = v8::Persistent::New(obj) +# define NanAssignPersistent(type, handle, obj) \ + handle = v8::Persistent::New(obj) +# define NanObjectWrapHandle(obj) obj->handle_ +# define NanMakeWeak(handle, parameters, callback) \ + handle.MakeWeak(parameters, callback) + +# define _NAN_THROW_ERROR(fun, errmsg) \ + do { \ + NanScope(); \ + return v8::ThrowException(fun(v8::String::New(errmsg))); \ + } while (0); + + inline static v8::Handle NanThrowError(const char* errmsg) { + _NAN_THROW_ERROR(v8::Exception::Error, errmsg); + } + + inline static v8::Handle NanThrowError( + v8::Handle error) { + NanScope(); + return v8::ThrowException(error); + } + + inline static v8::Handle NanThrowError( + const char *msg, + const int errorNumber) { + v8::Local err = v8::Exception::Error(v8::String::New(msg)); + v8::Local obj = err.As(); + obj->Set(v8::String::New("code"), v8::Int32::New(errorNumber)); + return NanThrowError(err); + } + + inline static v8::Handle NanThrowTypeError(const char* errmsg) { + _NAN_THROW_ERROR(v8::Exception::TypeError, errmsg); + } + + inline static v8::Handle NanThrowRangeError(const char* errmsg) { + _NAN_THROW_ERROR(v8::Exception::RangeError, errmsg); + } + + template static inline void NanDispose(v8::Persistent &handle) { + handle.Dispose(); + handle.Clear(); + } + + static inline v8::Local NanNewBufferHandle ( + char *data, + size_t length, + node::Buffer::free_callback callback, + void *hint) { + return v8::Local::New( + node::Buffer::New(data, length, callback, hint)->handle_); + } + + static inline v8::Local NanNewBufferHandle ( + char *data, uint32_t size) { + return v8::Local::New(node::Buffer::New(data, size)->handle_); + } + + static inline v8::Local NanNewBufferHandle (uint32_t size) { + return v8::Local::New(node::Buffer::New(size)->handle_); + } + + static inline void FreeData(char *data, void *hint) { + delete[] data; + } + + static inline v8::Local NanBufferUse(char* data, uint32_t size) { + return v8::Local::New( + node::Buffer::New(data, size, FreeData, NULL)->handle_); + } + + template + inline v8::Local NanPersistentToLocal( + const v8::Persistent& persistent) { + if (persistent.IsWeak()) { + return v8::Local::New(persistent); + } else { + return *reinterpret_cast*>( + const_cast*>(&persistent)); + } + } + + inline bool NanHasInstance( + v8::Persistent& function_template + , v8::Handle value) { + return function_template->HasInstance(value); + } + + static inline v8::Local NanNewContextHandle( + v8::ExtensionConfiguration* extensions = NULL + , v8::Handle tmpl = + v8::Handle() + , v8::Handle obj = v8::Handle() + ) { + v8::Persistent ctx = + v8::Context::New(extensions, tmpl, obj); + v8::Local lctx = v8::Local::New(ctx); + ctx.Dispose(); + return lctx; + } + +#endif // node version + +class NanCallback { + public: + NanCallback(const v8::Local &fn) { + NanScope(); + v8::Local obj = v8::Object::New(); + obj->Set(NanSymbol("callback"), fn); + NanAssignPersistent(v8::Object, handle, obj); + } + + ~NanCallback() { + if (handle.IsEmpty()) return; + handle.Dispose(); + handle.Clear(); + } + + inline v8::Local GetFunction () { + return NanPersistentToLocal(handle)->Get(NanSymbol("callback")) + .As(); + } + + // deprecated + void Run(int argc, v8::Local argv[]) { + Call(argc, argv); + } + + void Call(int argc, v8::Local argv[]) { + NanScope(); + + v8::Local callback = NanPersistentToLocal(handle)-> + Get(NanSymbol("callback")).As(); + v8::TryCatch try_catch; + callback->Call(v8::Context::GetCurrent()->Global(), argc, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } + } + + private: + v8::Persistent handle; +}; + +/* abstract */ class NanAsyncWorker { +public: + NanAsyncWorker (NanCallback *callback) : callback(callback) { + request.data = this; + errmsg = NULL; + } + + virtual ~NanAsyncWorker () { + NanScope(); + + if (!persistentHandle.IsEmpty()) + NanDispose(persistentHandle); + if (callback) + delete callback; + if (errmsg) + delete errmsg; + } + + virtual void WorkComplete () { + NanScope(); + + if (errmsg == NULL) + HandleOKCallback(); + else + HandleErrorCallback(); + delete callback; + callback = NULL; + } + + virtual void Execute () =0; + + uv_work_t request; + +protected: + v8::Persistent persistentHandle; + NanCallback *callback; + const char *errmsg; + + void SavePersistent(const char *key, v8::Local &obj) { + v8::Local handle = NanPersistentToLocal(persistentHandle); + handle->Set(NanSymbol(key), obj); + } + + v8::Local GetFromPersistent(const char *key) { + v8::Local handle = NanPersistentToLocal(persistentHandle); + return handle->Get(NanSymbol(key)).As(); + } + + virtual void HandleOKCallback () { + NanScope(); + + callback->Call(0, NULL); + }; + + virtual void HandleErrorCallback () { + NanScope(); + + v8::Local argv[] = { + v8::Exception::Error(v8::String::New(errmsg)) + }; + callback->Call(1, argv); + } +}; + +inline void NanAsyncExecute (uv_work_t* req) { + NanAsyncWorker *worker = static_cast(req->data); + worker->Execute(); +} + +inline void NanAsyncExecuteComplete (uv_work_t* req) { + NanAsyncWorker* worker = static_cast(req->data); + worker->WorkComplete(); + delete worker; +} + +inline void NanAsyncQueueWorker (NanAsyncWorker* worker) { + uv_queue_work( + uv_default_loop() + , &worker->request + , NanAsyncExecute + , (uv_after_work_cb)NanAsyncExecuteComplete + ); +} + +//// Base 64 //// + +#define _nan_base64_encoded_size(size) ((size + 2 - ((size + 2) % 3)) / 3 * 4) + + +// Doesn't check for padding at the end. Can be 1-2 bytes over. +static inline size_t _nan_base64_decoded_size_fast(size_t size) { + size_t remainder = size % 4; + + size = (size / 4) * 3; + if (remainder) { + if (size == 0 && remainder == 1) { + // special case: 1-byte input cannot be decoded + size = 0; + } else { + // non-padded input, add 1 or 2 extra bytes + size += 1 + (remainder == 3); + } + } + + return size; +} + +template +static size_t _nan_base64_decoded_size(const TypeName* src, size_t size) { + if (size == 0) + return 0; + + if (src[size - 1] == '=') + size--; + if (size > 0 && src[size - 1] == '=') + size--; + + return _nan_base64_decoded_size_fast(size); +} + + +// supports regular and URL-safe base64 +static const int _nan_unbase64_table[] = + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, -2, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 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, -1, -1, -1, -1, 63, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + +#define _nan_unbase64(x) _nan_unbase64_table[(uint8_t)(x)] + + +template +static size_t _nan_base64_decode(char* buf, + size_t len, + const TypeName* src, + const size_t srcLen) { + char a, b, c, d; + char* dst = buf; + char* dstEnd = buf + len; + const TypeName* srcEnd = src + srcLen; + + while (src < srcEnd && dst < dstEnd) { + int remaining = srcEnd - src; + + while (_nan_unbase64(*src) < 0 && src < srcEnd) src++, remaining--; + if (remaining == 0 || *src == '=') break; + a = _nan_unbase64(*src++); + + while (_nan_unbase64(*src) < 0 && src < srcEnd) src++, remaining--; + if (remaining <= 1 || *src == '=') break; + b = _nan_unbase64(*src++); + + *dst++ = (a << 2) | ((b & 0x30) >> 4); + if (dst == dstEnd) break; + + while (_nan_unbase64(*src) < 0 && src < srcEnd) src++, remaining--; + if (remaining <= 2 || *src == '=') break; + c = _nan_unbase64(*src++); + + *dst++ = ((b & 0x0F) << 4) | ((c & 0x3C) >> 2); + if (dst == dstEnd) break; + + while (_nan_unbase64(*src) < 0 && src < srcEnd) src++, remaining--; + if (remaining <= 3 || *src == '=') break; + d = _nan_unbase64(*src++); + + *dst++ = ((c & 0x03) << 6) | (d & 0x3F); + } + + return dst - buf; +} + +//// HEX //// + +template +unsigned _nan_hex2bin(TypeName c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return 10 + (c - 'A'); + if (c >= 'a' && c <= 'f') return 10 + (c - 'a'); + return static_cast(-1); +} + + +template +static size_t _nan_hex_decode(char* buf, + size_t len, + const TypeName* src, + const size_t srcLen) { + size_t i; + for (i = 0; i < len && i * 2 + 1 < srcLen; ++i) { + unsigned a = _nan_hex2bin(src[i * 2 + 0]); + unsigned b = _nan_hex2bin(src[i * 2 + 1]); + if (!~a || !~b) return i; + buf[i] = a * 16 + b; + } + + return i; +} + +static bool _NanGetExternalParts( + v8::Handle val + , const char** data + , size_t* len) { + + if (node::Buffer::HasInstance(val)) { + *data = node::Buffer::Data(val.As()); + *len = node::Buffer::Length(val.As()); + return true; + + } + + assert(val->IsString()); + v8::Local str = v8::Local::New(val.As()); + + if (str->IsExternalAscii()) { + const v8::String::ExternalAsciiStringResource* ext; + ext = str->GetExternalAsciiStringResource(); + *data = ext->data(); + *len = ext->length(); + return true; + + } else if (str->IsExternal()) { + const v8::String::ExternalStringResource* ext; + ext = str->GetExternalStringResource(); + *data = reinterpret_cast(ext->data()); + *len = ext->length(); + return true; + } + + return false; +} + +namespace Nan { + enum Encoding {ASCII, UTF8, BASE64, UCS2, BINARY, HEX, BUFFER}; +} + +static inline char* NanFromV8String( + v8::Handle from + , enum Nan::Encoding encoding = Nan::UTF8 + , size_t *datalen = NULL + , char *buf = NULL + , size_t buflen = 0 + , int flags = v8::String::NO_NULL_TERMINATION + | v8::String::HINT_MANY_WRITES_EXPECTED) { + + NanScope(); + + size_t sz_; + size_t term_len = !(flags & v8::String::NO_NULL_TERMINATION); + char *data = NULL; + size_t len; + bool is_extern = _NanGetExternalParts( + from + , const_cast(&data) + , &len); + + if (is_extern && !term_len) { + NanSetPointerSafe(datalen, len); + return data; + } + + v8::Local toStr = from->ToString(); + + char *to = buf; + + v8::String::AsciiValue value(toStr); + switch(encoding) { + case Nan::ASCII: +#if NODE_MODULE_VERSION < 0x0C + sz_ = toStr->Length(); + if (to == NULL) { + to = new char[sz_ + term_len]; + } else { + assert(buflen >= sz_ + term_len && "too small buffer"); + } + NanSetPointerSafe( + datalen + , toStr->WriteAscii(to, 0, sz_ + term_len, flags)); + return to; +#endif + case Nan::BINARY: + case Nan::BUFFER: + sz_ = toStr->Length(); + if (to == NULL) { + to = new char[sz_ + term_len]; + } else { + assert(buflen >= sz_ + term_len && "too small buffer"); + } +#if NODE_MODULE_VERSION < 0x0C + // TODO(isaacs): THIS IS AWFUL!!! + // AGREE(kkoopa) + { + uint16_t* twobytebuf = new uint16_t[sz_ + term_len]; + + size_t len = toStr->Write(twobytebuf, 0, sz_ + term_len, flags); + + for (size_t i = 0; i < sz_ + term_len && i < len + term_len; i++) { + unsigned char *b = reinterpret_cast(&twobytebuf[i]); + to[i] = *b; + } + + NanSetPointerSafe(datalen, len); + + delete[] twobytebuf; + return to; + } +#else + NanSetPointerSafe( + datalen, + toStr->WriteOneByte( + reinterpret_cast(to) + , 0 + , sz_ + term_len + , flags)); + return to; +#endif + case Nan::UTF8: + sz_ = toStr->Utf8Length(); + if (to == NULL) { + to = new char[sz_ + term_len]; + } else { + assert(buflen >= sz_ + term_len && "too small buffer"); + } + NanSetPointerSafe( + datalen + , toStr->WriteUtf8(to, sz_ + term_len, NULL, flags) - term_len); + return to; + case Nan::BASE64: + sz_ = _nan_base64_decoded_size(*value, toStr->Length()); + if (to == NULL) { + to = new char[sz_ + term_len]; + } else { + assert(buflen >= sz_ + term_len); + } + NanSetPointerSafe( + datalen + , _nan_base64_decode(to, sz_, *value, value.length())); + if (term_len) { + to[sz_] = '\0'; + } + return to; + case Nan::UCS2: + { + sz_ = toStr->Length(); + if (to == NULL) { + to = new char[(sz_ + term_len) * 2]; + } else { + assert(buflen >= (sz_ + term_len) * 2 && "too small buffer"); + } + + int bc = 2 * toStr->Write( + reinterpret_cast(to) + , 0 + , sz_ + term_len + , flags); + NanSetPointerSafe(datalen, bc); + return to; + } + case Nan::HEX: + sz_ = toStr->Length(); + assert(!(sz_ & 1) && "bad hex data"); + if (to == NULL) { + to = new char[sz_ / 2 + term_len]; + } else { + assert(buflen >= sz_ / 2 + term_len && "too small buffer"); + } + + NanSetPointerSafe( + datalen + , _nan_hex_decode(to, sz_ / 2, *value, value.length())); + if (term_len) { + to[sz_ / 2] = '\0'; + } + return to; + default: + assert(0 && "unknown encoding"); + } + return to; +} + +#endif diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/package.json b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/package.json new file mode 100644 index 0000000..eed6b4a --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/nan/package.json @@ -0,0 +1,39 @@ +{ + "name": "nan", + "version": "0.3.1", + "description": "Native Abstractions for Node.js: C++ header for Node 0.8->0.12 compatibility", + "main": ".index.js", + "repository": { + "type": "git", + "url": "git://github.com/rvagg/nan.git" + }, + "contributors": [ + { + "name": "Rod Vagg", + "email": "r@va.gg", + "url": "https://github.com/rvagg" + }, + { + "name": "Benjamin Byholm", + "email": "bbyholm@abo.fi", + "url": "https://github.com/kkoopa/" + }, + { + "name": "Trevor Norris", + "email": "trev.norris@gmail.com", + "url": "https://github.com/trevnorris" + } + ], + "license": "MIT", + "readme": "Native Abstractions for Node.js\n===============================\n\n**A header file filled with macro and utility goodness for making addon development for Node.js easier across versions 0.8, 0.10 and 0.11, and eventually 0.12.**\n\n***Current version: 0.3.1*** *(See [nan.h](https://github.com/rvagg/nan/blob/master/nan.h) for changelog)*\n\nThanks to the crazy changes in V8 (and some in Node core), keeping native addons compiling happily across versions, particularly 0.10 to 0.11/0.12, is a minor nightmare. The goal of this project is to store all logic necessary to develop native Node.js addons without having to inspect `NODE_MODULE_VERSION` and get yourself into a macro-tangle.\n\nThis project also contains some helper utilities that make addon development a bit more pleasant.\n\n * **[Usage](#usage)**\n * **[Example](#example)**\n * **[API](#api)**\n\n\n## Usage\n\nSimply add **NAN** as a dependency in the *package.json* of your Node addon:\n\n```js\n\"dependencies\": {\n ...\n \"nan\" : \"~0.3.1\"\n ...\n}\n```\n\nPull in the path to **NAN** in your *binding.gyp* so that you can use `#include \"nan.h\"` in your *.cpp*:\n\n```js\n\"include_dirs\" : [\n ...\n \"` when compiling your addon.\n\n\n## Example\n\nSee **[LevelDOWN](https://github.com/rvagg/node-leveldown/pull/48)** for a full example of **NAN** in use.\n\nFor a simpler example, see the **[async pi estimation example](https://github.com/rvagg/nan/tree/master/examples/async_pi_estimate)** in the examples directory for full code and an explanation of what this Monte Carlo Pi estimation example does. Below are just some parts of the full example that illustrate the use of **NAN**.\n\nCompare to the current 0.10 version of this example, found in the [node-addon-examples](https://github.com/rvagg/node-addon-examples/tree/master/9_async_work) repository and also a 0.11 version of the same found [here](https://github.com/kkoopa/node-addon-examples/tree/5c01f58fc993377a567812597e54a83af69686d7/9_async_work).\n\nNote that there is no embedded version sniffing going on here and also the async work is made much simpler, see below for details on the `NanAsyncWorker` class.\n\n```c++\n// addon.cc\n#include \n#include \"nan.h\"\n// ...\n\nusing namespace v8;\n\nvoid InitAll(Handle exports) {\n exports->Set(NanSymbol(\"calculateSync\"),\n FunctionTemplate::New(CalculateSync)->GetFunction());\n\n exports->Set(NanSymbol(\"calculateAsync\"),\n FunctionTemplate::New(CalculateAsync)->GetFunction());\n}\n\nNODE_MODULE(addon, InitAll)\n```\n\n```c++\n// sync.h\n#include \n#include \"nan.h\"\n\nNAN_METHOD(CalculateSync);\n```\n\n```c++\n// sync.cc\n#include \n#include \"nan.h\"\n#include \"sync.h\"\n// ...\n\nusing namespace v8;\n\n// Simple synchronous access to the `Estimate()` function\nNAN_METHOD(CalculateSync) {\n NanScope();\n\n // expect a number as the first argument\n int points = args[0]->Uint32Value();\n double est = Estimate(points);\n\n NanReturnValue(Number::New(est));\n}\n```\n\n```c++\n// async.cc\n#include \n#include \"nan.h\"\n#include \"async.h\"\n\n// ...\n\nusing namespace v8;\n\nclass PiWorker : public NanAsyncWorker {\n public:\n PiWorker(NanCallback *callback, int points)\n : NanAsyncWorker(callback), points(points) {}\n ~PiWorker() {}\n\n // Executed inside the worker-thread.\n // It is not safe to access V8, or V8 data structures\n // here, so everything we need for input and output\n // should go on `this`.\n void Execute () {\n estimate = Estimate(points);\n }\n\n // Executed when the async work is complete\n // this function will be run inside the main event loop\n // so it is safe to use V8 again\n void HandleOKCallback () {\n NanScope();\n\n Local argv[] = {\n Local::New(Null())\n , Number::New(estimate)\n };\n\n callback->Call(2, argv);\n };\n\n private:\n int points;\n double estimate;\n};\n\n// Asynchronous access to the `Estimate()` function\nNAN_METHOD(CalculateAsync) {\n NanScope();\n\n int points = args[0]->Uint32Value();\n NanCallback *callback = new NanCallback(args[1].As());\n\n NanAsyncQueueWorker(new PiWorker(callback, points));\n NanReturnUndefined();\n}\n```\n\n\n## API\n\n * NAN_METHOD\n * NAN_GETTER\n * NAN_SETTER\n * NAN_PROPERTY_GETTER\n * NAN_PROPERTY_SETTER\n * NAN_PROPERTY_ENUMERATOR\n * NAN_PROPERTY_DELETER\n * NAN_PROPERTY_QUERY\n * NAN_WEAK_CALLBACK\n * NanReturnValue\n * NanReturnUndefined\n * NanReturnNull\n * NanReturnEmptyString\n * NanScope\n * NanLocker\n * NanUnlocker\n * NanGetInternalFieldPointer\n * NanSetInternalFieldPointer\n * NanObjectWrapHandle\n * NanMakeWeak\n * NanSymbol\n * NanGetPointerSafe\n * NanSetPointerSafe\n * NanFromV8String\n * NanBooleanOptionValue\n * NanUInt32OptionValue\n * NanThrowError, NanThrowTypeError, NanThrowRangeError, NanThrowError(Handle), NanThrowError(Handle, int)\n * NanNewBufferHandle(char *, size_t, FreeCallback, void *), NanNewBufferHandle(char *, uint32_t), NanNewBufferHandle(uint32_t)\n * NanBufferUse(char *, uint32_t)\n * NanNewContextHandle\n * NanHasInstance\n * NanPersistentToLocal\n * NanDispose\n * NanAssignPersistent\n * NanInitPersistent\n * NanCallback\n * NanAsyncWorker\n * NanAsyncQueueWorker\n\n\n### NAN_METHOD(methodname)\n\nUse `NAN_METHOD` to define your V8 accessible methods:\n\n```c++\n// .h:\nclass Foo : public node::ObjectWrap {\n ...\n\n static NAN_METHOD(Bar);\n static NAN_METHOD(Baz);\n}\n\n\n// .cc:\nNAN_METHOD(Foo::Bar) {\n ...\n}\n\nNAN_METHOD(Foo::Baz) {\n ...\n}\n```\n\nThe reason for this macro is because of the method signature change in 0.11:\n\n```c++\n// 0.10 and below:\nHandle name(const Arguments& args)\n\n// 0.11 and above\nvoid name(const FunctionCallbackInfo& args)\n```\n\nThe introduction of `FunctionCallbackInfo` brings additional complications:\n\n\n### NAN_GETTER(methodname)\n\nUse `NAN_GETTER` to declare your V8 accessible getters. You get a `Local` `property` and an appropriately typed `args` object that can act like the `args` argument to a `NAN_METHOD` call.\n\nYou can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_GETTER`.\n\n\n### NAN_SETTER(methodname)\n\nUse `NAN_SETTER` to declare your V8 accessible setters. Same as `NAN_GETTER` but you also get a `Local` `value` object to work with.\n\nYou can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_SETTER`.\n\n\n### NAN_PROPERTY_GETTER(cbname)\nUse `NAN_PROPERTY_GETTER` to declare your V8 accessible property getters. You get a `Local` `property` and an appropriately typed `args` object that can act similar to the `args` argument to a `NAN_METHOD` call.\n\nYou can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_GETTER`.\n\n\n### NAN_PROPERTY_SETTER(cbname)\nUse `NAN_PROPERTY_SETTER` to declare your V8 accessible property setters. Same as `NAN_PROPERTY_GETTER` but you also get a `Local` `value` object to work with.\n\nYou can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_SETTER`.\n\n\n### NAN_PROPERTY_ENUMERATOR(cbname)\nUse `NAN_PROPERTY_ENUMERATOR` to declare your V8 accessible property enumerators. You get an appropriately typed `args` object like the `args` argument to a `NAN_PROPERTY_GETTER` call.\n\nYou can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_ENUMERATOR`.\n\n\n### NAN_PROPERTY_DELETER(cbname)\nUse `NAN_PROPERTY_DELETER` to declare your V8 accessible property deleters. Same as `NAN_PROPERTY_GETTER`.\n\nYou can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_DELETER`.\n\n\n### NAN_PROPERTY_QUERY(cbname)\nUse `NAN_PROPERTY_QUERY` to declare your V8 accessible property queries. Same as `NAN_PROPERTY_GETTER`.\n\nYou can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_QUERY`.\n\n\n### NAN_WEAK_CALLBACK(type, cbname)\n\nUse `NAN_WEAK_CALLBACK` to declare your V8 WeakReference callbacks. There is an object argument accessible through `NAN_WEAK_CALLBACK_OBJECT`. The `type` argument gives the type of the `data` argument, accessible through `NAN_WEAK_CALLBACK_DATA(type)`.\n\n```c++\nstatic NAN_WEAK_CALLBACK(BufferReference*, WeakCheck) {\n if (NAN_WEAK_CALLBACK_DATA(BufferReference*)->noLongerNeeded_) {\n delete NAN_WEAK_CALLBACK_DATA(BufferReference*);\n } else {\n // Still in use, revive, prevent GC\n NanMakeWeak(NAN_WEAK_CALLBACK_OBJECT, NAN_WEAK_CALLBACK_DATA(BufferReference*), &WeakCheck);\n }\n}\n\n```\n\n### NanReturnValue(Handle<Value>)\n\nUse `NanReturnValue` when you want to return a value from your V8 accessible method:\n\n```c++\nNAN_METHOD(Foo::Bar) {\n ...\n\n NanReturnValue(String::New(\"FooBar!\"));\n}\n```\n\nNo `return` statement required.\n\n\n### NanReturnUndefined()\n\nUse `NanReturnUndefined` when you don't want to return anything from your V8 accessible method:\n\n```c++\nNAN_METHOD(Foo::Baz) {\n ...\n\n NanReturnUndefined();\n}\n```\n\n\n### NanReturnNull()\n\nUse `NanReturnNull` when you want to return `Null` from your V8 accessible method:\n\n```c++\nNAN_METHOD(Foo::Baz) {\n ...\n\n NanReturnNull();\n}\n```\n\n\n### NanReturnEmptyString()\n\nUse `NanReturnEmptyString` when you want to return an empty `String` from your V8 accessible method:\n\n```c++\nNAN_METHOD(Foo::Baz) {\n ...\n\n NanReturnEmptyString();\n}\n```\n\n\n### NanScope()\n\nThe introduction of `isolate` references for many V8 calls in Node 0.11 makes `NanScope()` necessary, use it in place of `HandleScope scope`:\n\n```c++\nNAN_METHOD(Foo::Bar) {\n NanScope();\n\n NanReturnValue(String::New(\"FooBar!\"));\n}\n```\n\n\n### NanLocker()\n\nThe introduction of `isolate` references for many V8 calls in Node 0.11 makes `NanLocker()` necessary, use it in place of `Locker locker`:\n\n```c++\nNAN_METHOD(Foo::Bar) {\n NanLocker();\n ...\n NanUnlocker();\n}\n```\n\n\n### NanUnlocker()\n\nThe introduction of `isolate` references for many V8 calls in Node 0.11 makes `NanUnlocker()` necessary, use it in place of `Unlocker unlocker`:\n\n```c++\nNAN_METHOD(Foo::Bar) {\n NanLocker();\n ...\n NanUnlocker();\n}\n```\n\n\n### void * NanGetInternalFieldPointer(Handle<Object>, int)\n\nGets a pointer to the internal field with at `index` from a V8 `Object` handle.\n\n```c++\nLocal obj;\n...\nNanGetInternalFieldPointer(obj, 0);\n```\n\n### void NanSetInternalFieldPointer(Handle<Object>, int, void *)\n\nSets the value of the internal field at `index` on a V8 `Object` handle.\n\n```c++\nstatic Persistent dataWrapperCtor;\n...\nLocal wrapper = NanPersistentToLocal(dataWrapperCtor)->NewInstance();\nNanSetInternalFieldPointer(wrapper, 0, this);\n```\n\n\n### Local<Object> NanObjectWrapHandle(Object)\n\nWhen you want to fetch the V8 object handle from a native object you've wrapped with Node's `ObjectWrap`, you should use `NanObjectWrapHandle`:\n\n```c++\nNanObjectWrapHandle(iterator)->Get(String::NewSymbol(\"end\"))\n```\n\n\n### NanMakeWeak(Persistent<T>, parameter, callback)\n\nMake a persistent reference weak.\n\n\n### String NanSymbol(char *)\n\nThis isn't strictly about compatibility, it's just an easier way to create string symbol objects (i.e. `String::NewSymbol(x)`), for getting and setting object properties, or names of objects.\n\n```c++\nbool foo = false;\nif (obj->Has(NanSymbol(\"foo\")))\n foo = optionsObj->Get(NanSymbol(\"foo\"))->BooleanValue()\n```\n\n\n### Type NanGetPointerSafe(Type *[, Type])\n\nA helper for getting values from optional pointers. If the pointer is `NULL`, the function returns the optional default value, which defaults to `0`. Otherwise, the function returns the value the pointer points to.\n\n```c++\nchar *plugh(uint32_t *optional) {\n char res[] = \"xyzzy\";\n uint32_t param = NanGetPointerSafe(optional, 0x1337);\n switch (param) {\n ...\n }\n NanSetPointerSafe(optional, 0xDEADBEEF);\n} \n```\n\n\n### bool NanSetPointerSafe(Type *, Type)\n\nA helper for setting optional argument pointers. If the pointer is `NULL`, the function simply return `false`. Otherwise, the value is assigned to the variable the pointer points to.\n\n```c++\nconst char *plugh(size_t *outputsize) {\n char res[] = \"xyzzy\";\n if !(NanSetPointerSafe(outputsize, strlen(res) + 1)) {\n ...\n }\n\n ...\n}\n```\n\n\n### char* NanFromV8String(Handle<Value>[, enum Nan::Encoding, size_t *, char *, size_t, int])\n\nWhen you want to convert a V8 `String` to a `char*` use `NanFromV8String`. It is possible to define an encoding that defaults to `Nan::UTF8` as well as a pointer to a variable that will be assigned the number of bytes in the returned string. It is also possible to supply a buffer and its length to the function in order not to have a new buffer allocated. The final argument allows optionally setting `String::WriteOptions`, which default to `String::HINT_MANY_WRITES_EXPECTED | String::NO_NULL_TERMINATION`.\nJust remember that you'll end up with an object that you'll need to `delete[]` at some point unless you supply your own buffer:\n\n```c++\nsize_t count;\nchar* name = NanFromV8String(args[0]);\nchar* decoded = NanFromV8String(args[1], Nan::BASE64, &count, NULL, 0, String::HINT_MANY_WRITES_EXPECTED);\nchar param_copy[count];\nmemcpy(param_copy, decoded, count);\ndelete[] decoded;\n```\n\n\n### bool NanBooleanOptionValue(Handle<Value>, Handle<String>[, bool])\n\nWhen you have an \"options\" object that you need to fetch properties from, boolean options can be fetched with this pair. They check first if the object exists (`IsEmpty`), then if the object has the given property (`Has`) then they get and convert/coerce the property to a `bool`.\n\nThe optional last parameter is the *default* value, which is `false` if left off:\n\n```c++\n// `foo` is false unless the user supplies a truthy value for it\nbool foo = NanBooleanOptionValue(optionsObj, NanSymbol(\"foo\"));\n// `bar` is true unless the user supplies a falsy value for it\nbool bar = NanBooleanOptionValueDefTrue(optionsObj, NanSymbol(\"bar\"), true);\n```\n\n\n### uint32_t NanUInt32OptionValue(Handle<Value>, Handle<String>, uint32_t)\n\nSimilar to `NanBooleanOptionValue`, use `NanUInt32OptionValue` to fetch an integer option from your options object. Can be any kind of JavaScript `Number` and it will be coerced to an unsigned 32-bit integer.\n\nRequires all 3 arguments as a default is not optional:\n\n```c++\nuint32_t count = NanUInt32OptionValue(optionsObj, NanSymbol(\"count\"), 1024);\n```\n\n\n### NanThrowError(message), NanThrowTypeError(message), NanThrowRangeError(message), NanThrowError(Local<Value>), NanThrowError(Local<Value>, int)\n\nFor throwing `Error`, `TypeError` and `RangeError` objects. You should `return` this call:\n\n```c++\nreturn NanThrowError(\"you must supply a callback argument\");\n```\n\nCan also handle any custom object you may want to throw. If used with the error code argument, it will add the supplied error code to the error object as a property called `code`.\n\n\n### Local<Object> NanNewBufferHandle(char *, uint32_t), Local<Object> NanNewBufferHandle(uint32_t)\n\nThe `Buffer` API has changed a little in Node 0.11, this helper provides consistent access to `Buffer` creation:\n\n```c++\nNanNewBufferHandle((char*)value.data(), value.size());\n```\n\nCan also be used to initialize a `Buffer` with just a `size` argument.\n\nCan also be supplied with a `NAN_WEAK_CALLBACK` and a hint for the garbage collector, when dealing with weak references.\n\n\n### Local<Object> NanBufferUse(char*, uint32_t)\n\n`Buffer::New(char*, uint32_t)` prior to 0.11 would make a copy of the data.\nWhile it was possible to get around this, it required a shim by passing a\ncallback. So the new API `Buffer::Use(char*, uint32_t)` was introduced to remove\nneeding to use this shim.\n\n`NanBufferUse` uses the `char*` passed as the backing data, and will free the\nmemory automatically when the weak callback is called. Keep this in mind, as\ncareless use can lead to \"double free or corruption\" and other cryptic failures.\n\n\n### bool NanHasInstance(Persistent<FunctionTemplate>&, Handle<Value>)\n\nCan be used to check the type of an object to determine it is of a particular class you have already defined and have a `Persistent` handle for.\n\n\n### Local<Type> NanPersistentToLocal(Persistent<Type>&)\n\nAside from `FunctionCallbackInfo`, the biggest and most painful change to V8 in Node 0.11 is the many restrictions now placed on `Persistent` handles. They are difficult to assign and difficult to fetch the original value out of.\n\nUse `NanPersistentToLocal` to convert a `Persistent` handle back to a `Local` handle.\n\n```c++\nLocal handle = NanPersistentToLocal(persistentHandle);\n```\n\n\n### Local<Context> NanNewContextHandle([ExtensionConfiguration*, Handle<ObjectTemplate>, Handle<Value>])\nCreates a new `Local` handle.\n\n```c++\nLocal ftmpl = FunctionTemplate::New();\nLocal otmpl = ftmpl->InstanceTemplate();\nLocal ctx = NanNewContextHandle(NULL, otmpl);\n```\n\n\n### void NanDispose(Persistent<T> &)\n\nUse `NanDispose` to dispose a `Persistent` handle.\n\n```c++\nNanDispose(persistentHandle);\n```\n\n\n### NanAssignPersistent(type, handle, object)\n\nUse `NanAssignPersistent` to assign a non-`Persistent` handle to a `Persistent` one. You can no longer just declare a `Persistent` handle and assign directly to it later, you have to `Reset` it in Node 0.11, so this makes it easier.\n\nIn general it is now better to place anything you want to protect from V8's garbage collector as properties of a generic `Object` and then assign that to a `Persistent`. This works in older versions of Node also if you use `NanAssignPersistent`:\n\n```c++\nPersistent persistentHandle;\n\n...\n\nLocal obj = Object::New();\nobj->Set(NanSymbol(\"key\"), keyHandle); // where keyHandle might be a Local\nNanAssignPersistent(Object, persistentHandle, obj)\n```\n\n\n### NanInitPersistent(type, name, object)\n\nUser `NanInitPersistent` to declare and initialize a new `Persistent` with the supplied object. The assignment operator for `Persistent` is no longer public in Node 0.11, so this macro makes it easier to declare and initializing a new `Persistent`. See NanAssignPersistent for more information.\n\n```c++\nLocal obj = Object::New();\nobj->Set(NanSymbol(\"key\"), keyHandle); // where keyHandle might be a Local\nNanInitPersistent(Object, persistentHandle, obj);\n```\n\n\n### NanCallback\n\nBecause of the difficulties imposed by the changes to `Persistent` handles in V8 in Node 0.11, creating `Persistent` versions of your `Local` handles is annoyingly tricky. `NanCallback` makes it easier by taking your `Local` handle, making it persistent until the `NanCallback` is deleted and even providing a handy `Call()` method to fetch and execute the callback `Function`.\n\n```c++\nLocal callbackHandle = callback = args[0].As();\nNanCallback *callback = new NanCallback(callbackHandle);\n// pass `callback` around and it's safe from GC until you:\ndelete callback;\n```\n\nYou can execute the callback like so:\n\n```c++\n// no arguments:\ncallback->Call(0, NULL);\n\n// an error argument:\nLocal argv[] = {\n Exception::Error(String::New(\"fail!\"))\n};\ncallback->Call(1, argv);\n\n// a success argument:\nLocal argv[] = {\n Local::New(Null()),\n String::New(\"w00t!\")\n};\ncallback->Call(2, argv);\n```\n\n`NanCallback` also has a `Local GetCallback()` method that you can use to fetch a local handle to the underlying callback function if you need it.\n\n\n### NanAsyncWorker\n\n`NanAsyncWorker` is an abstract class that you can subclass to have much of the annoying async queuing and handling taken care of for you. It can even store arbitrary V8 objects for you and have them persist while the async work is in progress.\n\nSee a rough outline of the implementation:\n\n```c++\nclass NanAsyncWorker {\npublic:\n NanAsyncWorker (NanCallback *callback);\n\n // Clean up persistent handles and delete the *callback\n virtual ~NanAsyncWorker ();\n\n // Check the `char *errmsg` property and call HandleOKCallback()\n // or HandleErrorCallback depending on whether it has been set or not\n virtual void WorkComplete ();\n\n // You must implement this to do some async work. If there is an\n // error then allocate `errmsg` to to a message and the callback will\n // be passed that string in an Error object\n virtual void Execute ();\n\nprotected:\n // Set this if there is an error, otherwise it's NULL\n const char *errmsg;\n\n // Save a V8 object in a Persistent handle to protect it from GC\n void SavePersistent(const char *key, Local &obj);\n\n // Fetch a stored V8 object (don't call from within `Execute()`)\n Local GetFromPersistent(const char *key);\n\n // Default implementation calls the callback function with no arguments.\n // Override this to return meaningful data\n virtual void HandleOKCallback ();\n\n // Default implementation calls the callback function with an Error object\n // wrapping the `errmsg` string\n virtual void HandleErrorCallback ();\n};\n```\n\n\n### NanAsyncQueueWorker(NanAsyncWorker *)\n\n`NanAsyncQueueWorker` will run a `NanAsyncWorker` asynchronously via libuv. Both the *execute* and *after_work* steps are taken care of for you—most of the logic for this is embedded in `NanAsyncWorker`.\n\n### Contributors\n\nNAN is only possible due to the excellent work of the following contributors:\n\n\n\n\n\n
Rod VaggGitHub/rvaggTwitter/@rvagg
Benjamin ByholmGitHub/kkoopa
Trevor NorrisGitHub/trevnorrisTwitter/@trevnorris
\n\nLicence & copyright\n-----------------------\n\nCopyright (c) 2013 Rod Vagg & NAN contributors (listed above).\n\nNative Abstractions for Node.js is licensed under an MIT +no-false-attribs license. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE file for more details.\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/rvagg/nan/issues" + }, + "_id": "nan@0.3.1", + "dist": { + "shasum": "a7ce60fff5c6779985fd1defcaeecd9de2dfaf04" + }, + "_from": "nan@~0.3.0", + "_resolved": "https://registry.npmjs.org/nan/-/nan-0.3.1.tgz" +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/.npmignore b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/.npmignore new file mode 100644 index 0000000..6bfffbb --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/.npmignore @@ -0,0 +1,5 @@ +npm-debug.log +node_modules +.*.swp +.lock-* +build/ diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/Makefile b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/Makefile new file mode 100644 index 0000000..7496b6f --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/Makefile @@ -0,0 +1,12 @@ +ALL_TESTS = $(shell find test/ -name '*.test.js') + +run-tests: + @./node_modules/.bin/mocha \ + -t 2000 \ + $(TESTFLAGS) \ + $(TESTS) + +test: + @$(MAKE) NODE_PATH=lib TESTS="$(ALL_TESTS)" run-tests + +.PHONY: test diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/README.md b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/README.md new file mode 100644 index 0000000..4b39a2a --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/README.md @@ -0,0 +1,28 @@ +# options.js # + +A very light-weight in-code option parsers for node.js. + +## License ## + +(The MIT License) + +Copyright (c) 2012 Einar Otto Stangvik <einaros@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/lib/options.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/lib/options.js new file mode 100644 index 0000000..4fc45e9 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/lib/options.js @@ -0,0 +1,86 @@ +/*! + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var fs = require('fs'); + +function Options(defaults) { + var internalValues = {}; + var values = this.value = {}; + Object.keys(defaults).forEach(function(key) { + internalValues[key] = defaults[key]; + Object.defineProperty(values, key, { + get: function() { return internalValues[key]; }, + configurable: false, + enumerable: true + }); + }); + this.reset = function() { + Object.keys(defaults).forEach(function(key) { + internalValues[key] = defaults[key]; + }); + return this; + }; + this.merge = function(options, required) { + options = options || {}; + if (Object.prototype.toString.call(required) === '[object Array]') { + var missing = []; + for (var i = 0, l = required.length; i < l; ++i) { + var key = required[i]; + if (!(key in options)) { + missing.push(key); + } + } + if (missing.length > 0) { + if (missing.length > 1) { + throw new Error('options ' + + missing.slice(0, missing.length - 1).join(', ') + ' and ' + + missing[missing.length - 1] + ' must be defined'); + } + else throw new Error('option ' + missing[0] + ' must be defined'); + } + } + Object.keys(options).forEach(function(key) { + if (key in internalValues) { + internalValues[key] = options[key]; + } + }); + return this; + }; + this.copy = function(keys) { + var obj = {}; + Object.keys(defaults).forEach(function(key) { + if (keys.indexOf(key) !== -1) { + obj[key] = values[key]; + } + }); + return obj; + }; + this.read = function(filename, cb) { + if (typeof cb == 'function') { + var self = this; + fs.readFile(filename, function(error, data) { + if (error) return cb(error); + var conf = JSON.parse(data); + self.merge(conf); + cb(); + }); + } + else { + var conf = JSON.parse(fs.readFileSync(filename)); + this.merge(conf); + } + return this; + }; + this.isDefined = function(key) { + return typeof values[key] != 'undefined'; + }; + this.isDefinedAndNonNull = function(key) { + return typeof values[key] != 'undefined' && values[key] !== null; + }; + Object.freeze(values); + Object.freeze(this); +} + +module.exports = Options; diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/package.json b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/package.json new file mode 100644 index 0000000..04e8978 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/package.json @@ -0,0 +1,32 @@ +{ + "author": { + "name": "Einar Otto Stangvik", + "email": "einaros@gmail.com", + "url": "http://2x.io" + }, + "name": "options", + "description": "A very light-weight in-code option parsers for node.js.", + "version": "0.0.5", + "repository": { + "type": "git", + "url": "git://github.com/einaros/options.js.git" + }, + "main": "lib/options", + "scripts": { + "test": "make test" + }, + "engines": { + "node": ">=0.4.0" + }, + "dependencies": {}, + "devDependencies": { + "mocha": "latest" + }, + "readme": "# options.js #\n\nA very light-weight in-code option parsers for node.js.\n\n## License ##\n\n(The MIT License)\n\nCopyright (c) 2012 Einar Otto Stangvik <einaros@gmail.com>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/einaros/options.js/issues" + }, + "_id": "options@0.0.5", + "_from": "options@>=0.0.5" +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/test/fixtures/test.conf b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/test/fixtures/test.conf new file mode 100644 index 0000000..6e62444 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/test/fixtures/test.conf @@ -0,0 +1,4 @@ +{ + "a": "foobar", + "b": false +} \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/test/options.test.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/test/options.test.js new file mode 100644 index 0000000..6a1d9f5 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/options/test/options.test.js @@ -0,0 +1,140 @@ +var Options = require('options') + , assert = require('assert'); + +describe('Options', function() { + describe('#ctor', function() { + it('initializes options', function() { + var option = new Options({a: true, b: false}); + assert.strictEqual(true, option.value.a); + assert.strictEqual(false, option.value.b); + }); + }); + + describe('#merge', function() { + it('merges options from another object', function() { + var option = new Options({a: true, b: false}); + option.merge({b: true}); + assert.strictEqual(true, option.value.a); + assert.strictEqual(true, option.value.b); + }); + it('does nothing when arguments are undefined', function() { + var option = new Options({a: true, b: false}); + option.merge(undefined); + assert.strictEqual(true, option.value.a); + assert.strictEqual(false, option.value.b); + }); + it('cannot set values that werent already there', function() { + var option = new Options({a: true, b: false}); + option.merge({c: true}); + assert.strictEqual('undefined', typeof option.value.c); + }); + it('can require certain options to be defined', function() { + var option = new Options({a: true, b: false, c: 3}); + var caughtException = false; + try { + option.merge({}, ['a', 'b', 'c']); + } + catch (e) { + caughtException = e.toString() == 'Error: options a, b and c must be defined'; + } + assert.strictEqual(true, caughtException); + }); + it('can require certain options to be defined, when options are undefined', function() { + var option = new Options({a: true, b: false, c: 3}); + var caughtException = false; + try { + option.merge(undefined, ['a', 'b', 'c']); + } + catch (e) { + caughtException = e.toString() == 'Error: options a, b and c must be defined'; + } + assert.strictEqual(true, caughtException); + }); + it('returns "this"', function() { + var option = new Options({a: true, b: false, c: 3}); + assert.strictEqual(option, option.merge()); + }); + }); + + describe('#copy', function() { + it('returns a new object with the indicated options', function() { + var option = new Options({a: true, b: false, c: 3}); + option.merge({c: 4}); + var obj = option.copy(['a', 'c']); + assert.strictEqual(true, obj.a); + assert.strictEqual(4, obj.c); + assert.strictEqual('undefined', typeof obj.b); + }); + }); + + describe('#value', function() { + it('can be enumerated', function() { + var option = new Options({a: true, b: false}); + assert.strictEqual(2, Object.keys(option.value).length); + }); + it('can not be used to set values', function() { + var option = new Options({a: true, b: false}); + option.value.b = true; + assert.strictEqual(false, option.value.b); + }); + it('can not be used to add values', function() { + var option = new Options({a: true, b: false}); + option.value.c = 3; + assert.strictEqual('undefined', typeof option.value.c); + }); + }); + + describe('#isDefined', function() { + it('returns true if the named value is defined', function() { + var option = new Options({a: undefined}); + assert.strictEqual(false, option.isDefined('a')); + option.merge({a: false}); + assert.strictEqual(true, option.isDefined('a')); + }); + }); + + describe('#isDefinedAndNonNull', function() { + it('returns true if the named value is defined and non-null', function() { + var option = new Options({a: undefined}); + assert.strictEqual(false, option.isDefinedAndNonNull('a')); + option.merge({a: null}); + assert.strictEqual(false, option.isDefinedAndNonNull('a')); + option.merge({a: 2}); + assert.strictEqual(true, option.isDefinedAndNonNull('a')); + }); + }); + + describe('#read', function() { + it('reads and merges config from a file', function() { + var option = new Options({a: true, b: true}); + option.read(__dirname + '/fixtures/test.conf'); + assert.strictEqual('foobar', option.value.a); + assert.strictEqual(false, option.value.b); + }); + + it('asynchronously reads and merges config from a file when a callback is passed', function(done) { + var option = new Options({a: true, b: true}); + option.read(__dirname + '/fixtures/test.conf', function(error) { + assert.strictEqual('foobar', option.value.a); + assert.strictEqual(false, option.value.b); + done(); + }); + }); + }); + + describe('#reset', function() { + it('resets options to defaults', function() { + var option = new Options({a: true, b: false}); + option.merge({b: true}); + assert.strictEqual(true, option.value.b); + option.reset(); + assert.strictEqual(false, option.value.b); + }); + }); + + it('is immutable', function() { + var option = new Options({a: true, b: false}); + option.foo = 2; + assert.strictEqual('undefined', typeof option.foo); + }); +}); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/.npmignore b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/.npmignore new file mode 100644 index 0000000..6bfffbb --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/.npmignore @@ -0,0 +1,5 @@ +npm-debug.log +node_modules +.*.swp +.lock-* +build/ diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/README.md b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/README.md new file mode 100644 index 0000000..55eb3c1 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/README.md @@ -0,0 +1,3 @@ +# tinycolor # + +This is a no-fuzz, barebone, zero muppetry color module for node.js. \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/example.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/example.js new file mode 100644 index 0000000..f754046 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/example.js @@ -0,0 +1,3 @@ +require('./tinycolor'); +console.log('this should be red and have an underline!'.grey.underline); +console.log('this should have a blue background!'.bgBlue); \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/package.json b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/package.json new file mode 100644 index 0000000..8c56dce --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/package.json @@ -0,0 +1,27 @@ +{ + "author": { + "name": "Einar Otto Stangvik", + "email": "einaros@gmail.com", + "url": "http://2x.io" + }, + "name": "tinycolor", + "description": "a to-the-point color module for node", + "version": "0.0.1", + "repository": { + "type": "git", + "url": "git://github.com/einaros/tinycolor.git" + }, + "engines": { + "node": ">=0.4.0" + }, + "dependencies": {}, + "devDependencies": {}, + "main": "tinycolor", + "readme": "# tinycolor #\n\nThis is a no-fuzz, barebone, zero muppetry color module for node.js.", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/einaros/tinycolor/issues" + }, + "_id": "tinycolor@0.0.1", + "_from": "tinycolor@0.x" +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/tinycolor.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/tinycolor.js new file mode 100644 index 0000000..36e552c --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/node_modules/tinycolor/tinycolor.js @@ -0,0 +1,31 @@ +var styles = { + 'bold': ['\033[1m', '\033[22m'], + 'italic': ['\033[3m', '\033[23m'], + 'underline': ['\033[4m', '\033[24m'], + 'inverse': ['\033[7m', '\033[27m'], + 'black': ['\033[30m', '\033[39m'], + 'red': ['\033[31m', '\033[39m'], + 'green': ['\033[32m', '\033[39m'], + 'yellow': ['\033[33m', '\033[39m'], + 'blue': ['\033[34m', '\033[39m'], + 'magenta': ['\033[35m', '\033[39m'], + 'cyan': ['\033[36m', '\033[39m'], + 'white': ['\033[37m', '\033[39m'], + 'default': ['\033[39m', '\033[39m'], + 'grey': ['\033[90m', '\033[39m'], + 'bgBlack': ['\033[40m', '\033[49m'], + 'bgRed': ['\033[41m', '\033[49m'], + 'bgGreen': ['\033[42m', '\033[49m'], + 'bgYellow': ['\033[43m', '\033[49m'], + 'bgBlue': ['\033[44m', '\033[49m'], + 'bgMagenta': ['\033[45m', '\033[49m'], + 'bgCyan': ['\033[46m', '\033[49m'], + 'bgWhite': ['\033[47m', '\033[49m'], + 'bgDefault': ['\033[49m', '\033[49m'] +} +Object.keys(styles).forEach(function(style) { + Object.defineProperty(String.prototype, style, { + get: function() { return styles[style][0] + this + styles[style][1]; }, + enumerable: false + }); +}); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/package.json b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/package.json new file mode 100644 index 0000000..aa162a6 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/package.json @@ -0,0 +1,59 @@ +{ + "author": { + "name": "Einar Otto Stangvik", + "email": "einaros@gmail.com", + "url": "http://2x.io" + }, + "name": "ws", + "description": "simple to use, blazing fast and thoroughly tested websocket client, server and console for node.js, up-to-date against RFC-6455", + "version": "0.4.29", + "keywords": [ + "Hixie", + "HyBi", + "Push", + "RFC-6455", + "WebSocket", + "WebSockets", + "real-time" + ], + "repository": { + "type": "git", + "url": "git://github.com/einaros/ws.git" + }, + "bin": { + "wscat": "./bin/wscat" + }, + "scripts": { + "test": "make test", + "install": "(node-gyp rebuild 2> builderror.log) || (exit 0)" + }, + "engines": { + "node": ">=0.4.0" + }, + "dependencies": { + "commander": "~0.6.1", + "nan": "~0.3.0", + "tinycolor": "0.x", + "options": ">=0.0.5" + }, + "devDependencies": { + "mocha": "1.12.0", + "should": "1.2.x", + "expect.js": "0.2.x", + "benchmark": "0.3.x", + "ansi": "latest" + }, + "browser": "./lib/browser.js", + "gypfile": true, + "readme": "[![Build Status](https://secure.travis-ci.org/einaros/ws.png)](http://travis-ci.org/einaros/ws)\n\n# ws: a node.js websocket library #\n\n`ws` is a simple to use websocket implementation, up-to-date against RFC-6455, and [probably the fastest WebSocket library for node.js](http://web.archive.org/web/20130314230536/http://hobbycoding.posterous.com/the-fastest-websocket-module-for-nodejs).\n\nPasses the quite extensive Autobahn test suite. See http://einaros.github.com/ws for the full reports.\n\nComes with a command line utility, `wscat`, which can either act as a server (--listen), or client (--connect); Use it to debug simple websocket services.\n\n## Protocol support ##\n\n* **Hixie draft 76** (Old and deprecated, but still in use by Safari and Opera. Added to ws version 0.4.2, but server only. Can be disabled by setting the `disableHixie` option to true.)\n* **HyBi drafts 07-12** (Use the option `protocolVersion: 8`, or argument `-p 8` for wscat)\n* **HyBi drafts 13-17** (Current default, alternatively option `protocolVersion: 13`, or argument `-p 13` for wscat)\n\n_See the echo.websocket.org example below for how to use the `protocolVersion` option._\n\n## Usage ##\n\n### Installing ###\n\n`npm install ws`\n\n### Sending and receiving text data ###\n\n```js\nvar WebSocket = require('ws');\nvar ws = new WebSocket('ws://www.host.com/path');\nws.on('open', function() {\n ws.send('something');\n});\nws.on('message', function(data, flags) {\n // flags.binary will be set if a binary data is received\n // flags.masked will be set if the data was masked\n});\n```\n\n### Sending binary data ###\n\n```js\nvar WebSocket = require('ws');\nvar ws = new WebSocket('ws://www.host.com/path');\nws.on('open', function() {\n var array = new Float32Array(5);\n for (var i = 0; i < array.length; ++i) array[i] = i / 2;\n ws.send(array, {binary: true, mask: true});\n});\n```\n\nSetting `mask`, as done for the send options above, will cause the data to be masked according to the websocket protocol. The same option applies for text data.\n\n### Server example ###\n\n```js\nvar WebSocketServer = require('ws').Server\n , wss = new WebSocketServer({port: 8080});\nwss.on('connection', function(ws) {\n ws.on('message', function(message) {\n console.log('received: %s', message);\n });\n ws.send('something');\n});\n```\n\n### Error handling best practices ###\n\n```js\n// If the WebSocket is closed before the following send is attempted\nws.send('something');\n\n// Errors (both immediate and async write errors) can be detected in an optional callback.\n// The callback is also the only way of being notified that data has actually been sent.\nws.send('something', function(error) {\n // if error is null, the send has been completed,\n // otherwise the error object will indicate what failed.\n});\n\n// Immediate errors can also be handled with try/catch-blocks, but **note**\n// that since sends are inherently asynchronous, socket write failures will *not*\n// be captured when this technique is used.\ntry {\n ws.send('something');\n}\ncatch (e) {\n // handle error\n}\n```\n\n### echo.websocket.org demo ###\n\n```js\nvar WebSocket = require('ws');\nvar ws = new WebSocket('ws://echo.websocket.org/', {protocolVersion: 8, origin: 'http://websocket.org'});\nws.on('open', function() {\n console.log('connected');\n ws.send(Date.now().toString(), {mask: true});\n});\nws.on('close', function() {\n console.log('disconnected');\n});\nws.on('message', function(data, flags) {\n console.log('Roundtrip time: ' + (Date.now() - parseInt(data)) + 'ms', flags);\n setTimeout(function() {\n ws.send(Date.now().toString(), {mask: true});\n }, 500);\n});\n```\n\n### wscat against echo.websocket.org ###\n\n $ npm install -g ws\n $ wscat -c ws://echo.websocket.org -p 8\n connected (press CTRL+C to quit)\n > hi there\n < hi there\n > are you a happy parrot?\n < are you a happy parrot?\n\n### Other examples ###\n\nFor a full example with a browser client communicating with a ws server, see the examples folder.\n\nNote that the usage together with Express 3.0 is quite different from Express 2.x. The difference is expressed in the two different serverstats-examples.\n\nOtherwise, see the test cases.\n\n### Running the tests ###\n\n`make test`\n\n## API Docs ##\n\nSee the doc/ directory for Node.js-like docs for the ws classes.\n\n## License ##\n\n(The MIT License)\n\nCopyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/einaros/ws/issues" + }, + "_id": "ws@0.4.29", + "dist": { + "shasum": "5533bd0ec76c687397679f329bb81d2371c2eb2d" + }, + "_from": "ws@0.4.x", + "_resolved": "https://registry.npmjs.org/ws/-/ws-0.4.29.tgz" +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/src/bufferutil.cc b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/src/bufferutil.cc new file mode 100644 index 0000000..f06777f --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/src/bufferutil.cc @@ -0,0 +1,117 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "nan.h" + +using namespace v8; +using namespace node; + +class BufferUtil : public ObjectWrap +{ +public: + + static void Initialize(v8::Handle target) + { + NanScope(); + Local t = FunctionTemplate::New(New); + t->InstanceTemplate()->SetInternalFieldCount(1); + NODE_SET_METHOD(t, "unmask", BufferUtil::Unmask); + NODE_SET_METHOD(t, "mask", BufferUtil::Mask); + NODE_SET_METHOD(t, "merge", BufferUtil::Merge); + target->Set(String::NewSymbol("BufferUtil"), t->GetFunction()); + } + +protected: + + static NAN_METHOD(New) + { + NanScope(); + BufferUtil* bufferUtil = new BufferUtil(); + bufferUtil->Wrap(args.This()); + NanReturnValue(args.This()); + } + + static NAN_METHOD(Merge) + { + NanScope(); + Local bufferObj = args[0]->ToObject(); + char* buffer = Buffer::Data(bufferObj); + Local array = Local::Cast(args[1]); + unsigned int arrayLength = array->Length(); + size_t offset = 0; + unsigned int i; + for (i = 0; i < arrayLength; ++i) { + Local src = array->Get(i)->ToObject(); + size_t length = Buffer::Length(src); + memcpy(buffer + offset, Buffer::Data(src), length); + offset += length; + } + NanReturnValue(True()); + } + + static NAN_METHOD(Unmask) + { + NanScope(); + Local buffer_obj = args[0]->ToObject(); + size_t length = Buffer::Length(buffer_obj); + Local mask_obj = args[1]->ToObject(); + unsigned int *mask = (unsigned int*)Buffer::Data(mask_obj); + unsigned int* from = (unsigned int*)Buffer::Data(buffer_obj); + size_t len32 = length / 4; + unsigned int i; + for (i = 0; i < len32; ++i) *(from + i) ^= *mask; + from += i; + switch (length % 4) { + case 3: *((unsigned char*)from+2) = *((unsigned char*)from+2) ^ ((unsigned char*)mask)[2]; + case 2: *((unsigned char*)from+1) = *((unsigned char*)from+1) ^ ((unsigned char*)mask)[1]; + case 1: *((unsigned char*)from ) = *((unsigned char*)from ) ^ ((unsigned char*)mask)[0]; + case 0:; + } + NanReturnValue(True()); + } + + static NAN_METHOD(Mask) + { + NanScope(); + Local buffer_obj = args[0]->ToObject(); + Local mask_obj = args[1]->ToObject(); + unsigned int *mask = (unsigned int*)Buffer::Data(mask_obj); + Local output_obj = args[2]->ToObject(); + unsigned int dataOffset = args[3]->Int32Value(); + unsigned int length = args[4]->Int32Value(); + unsigned int* to = (unsigned int*)(Buffer::Data(output_obj) + dataOffset); + unsigned int* from = (unsigned int*)Buffer::Data(buffer_obj); + unsigned int len32 = length / 4; + unsigned int i; + for (i = 0; i < len32; ++i) *(to + i) = *(from + i) ^ *mask; + to += i; + from += i; + switch (length % 4) { + case 3: *((unsigned char*)to+2) = *((unsigned char*)from+2) ^ *((unsigned char*)mask+2); + case 2: *((unsigned char*)to+1) = *((unsigned char*)from+1) ^ *((unsigned char*)mask+1); + case 1: *((unsigned char*)to ) = *((unsigned char*)from ) ^ *((unsigned char*)mask); + case 0:; + } + NanReturnValue(True()); + } +}; + +extern "C" void init (Handle target) +{ + NanScope(); + BufferUtil::Initialize(target); +} + +NODE_MODULE(bufferutil, init) + diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/src/validation.cc b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/src/validation.cc new file mode 100644 index 0000000..528eda1 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/src/validation.cc @@ -0,0 +1,145 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +#include +#include +#include +#include +#include +#include +#include +#include "nan.h" + +using namespace v8; +using namespace node; + +#define UNI_SUR_HIGH_START (uint32_t) 0xD800 +#define UNI_SUR_LOW_END (uint32_t) 0xDFFF +#define UNI_REPLACEMENT_CHAR (uint32_t) 0x0000FFFD +#define UNI_MAX_LEGAL_UTF32 (uint32_t) 0x0010FFFF + +static const uint8_t trailingBytesForUTF8[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 +}; + +static const uint32_t offsetsFromUTF8[6] = { + 0x00000000, 0x00003080, 0x000E2080, + 0x03C82080, 0xFA082080, 0x82082080 +}; + +static int isLegalUTF8(const uint8_t *source, const int length) +{ + uint8_t a; + const uint8_t *srcptr = source+length; + switch (length) { + default: return 0; + /* Everything else falls through when "true"... */ + /* RFC3629 makes 5 & 6 bytes UTF-8 illegal + case 6: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; + case 5: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; */ + case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; + case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; + case 2: if ((a = (*--srcptr)) > 0xBF) return 0; + switch (*source) { + /* no fall-through in this inner switch */ + case 0xE0: if (a < 0xA0) return 0; break; + case 0xED: if (a > 0x9F) return 0; break; + case 0xF0: if (a < 0x90) return 0; break; + case 0xF4: if (a > 0x8F) return 0; break; + default: if (a < 0x80) return 0; + } + + case 1: if (*source >= 0x80 && *source < 0xC2) return 0; + } + if (*source > 0xF4) return 0; + return 1; +} + +int is_valid_utf8 (size_t len, char *value) +{ + /* is the string valid UTF-8? */ + for (unsigned int i = 0; i < len; i++) { + uint32_t ch = 0; + uint8_t extrabytes = trailingBytesForUTF8[(uint8_t) value[i]]; + + if (extrabytes + i >= len) + return 0; + + if (isLegalUTF8 ((uint8_t *) (value + i), extrabytes + 1) == 0) return 0; + + switch (extrabytes) { + case 5 : ch += (uint8_t) value[i++]; ch <<= 6; + case 4 : ch += (uint8_t) value[i++]; ch <<= 6; + case 3 : ch += (uint8_t) value[i++]; ch <<= 6; + case 2 : ch += (uint8_t) value[i++]; ch <<= 6; + case 1 : ch += (uint8_t) value[i++]; ch <<= 6; + case 0 : ch += (uint8_t) value[i]; + } + + ch -= offsetsFromUTF8[extrabytes]; + + if (ch <= UNI_MAX_LEGAL_UTF32) { + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) + return 0; + } else { + return 0; + } + } + + return 1; +} + +class Validation : public ObjectWrap +{ +public: + + static void Initialize(v8::Handle target) + { + HandleScope scope; + Local t = FunctionTemplate::New(New); + t->InstanceTemplate()->SetInternalFieldCount(1); + NODE_SET_METHOD(t, "isValidUTF8", Validation::IsValidUTF8); + target->Set(String::NewSymbol("Validation"), t->GetFunction()); + } + +protected: + + static NAN_METHOD(New) + { + NanScope(); + Validation* validation = new Validation(); + validation->Wrap(args.This()); + NanReturnValue(args.This()); + } + + static NAN_METHOD(IsValidUTF8) + { + NanScope(); + if (!Buffer::HasInstance(args[0])) { + return NanThrowTypeError("First argument needs to be a buffer"); + } + Local buffer_obj = args[0]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); + NanReturnValue(is_valid_utf8(buffer_length, buffer_data) == 1 ? True() : False()); + } +}; + +extern "C" void init (Handle target) +{ + NanScope(); + Validation::Initialize(target); +} + +NODE_MODULE(validation, init) + diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/BufferPool.test.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/BufferPool.test.js new file mode 100644 index 0000000..1ee7ff0 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/BufferPool.test.js @@ -0,0 +1,63 @@ +var BufferPool = require('../lib/BufferPool'); +require('should'); + +describe('BufferPool', function() { + describe('#ctor', function() { + it('allocates pool', function() { + var db = new BufferPool(1000); + db.size.should.eql(1000); + }); + }); + describe('#get', function() { + it('grows the pool if necessary', function() { + var db = new BufferPool(1000); + var buf = db.get(2000); + db.size.should.be.above(1000); + db.used.should.eql(2000); + buf.length.should.eql(2000); + }); + it('grows the pool after the first call, if necessary', function() { + var db = new BufferPool(1000); + var buf = db.get(1000); + db.used.should.eql(1000); + db.size.should.eql(1000); + buf.length.should.eql(1000); + var buf2 = db.get(1000); + db.used.should.eql(2000); + db.size.should.be.above(1000); + buf2.length.should.eql(1000); + }); + it('grows the pool according to the growStrategy if necessary', function() { + var db = new BufferPool(1000, function(db, length) { + return db.size + 2345; + }); + var buf = db.get(2000); + db.size.should.eql(3345); + buf.length.should.eql(2000); + }); + it('doesnt grow the pool if theres enough room available', function() { + var db = new BufferPool(1000); + var buf = db.get(1000); + db.size.should.eql(1000); + buf.length.should.eql(1000); + }); + }); + describe('#reset', function() { + it('shinks the pool', function() { + var db = new BufferPool(1000); + var buf = db.get(2000); + db.reset(true); + db.size.should.eql(1000); + }); + it('shrinks the pool according to the shrinkStrategy', function() { + var db = new BufferPool(1000, function(db, length) { + return db.used + length; + }, function(db) { + return 0; + }); + var buf = db.get(2000); + db.reset(true); + db.size.should.eql(0); + }); + }); +}); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Receiver.hixie.test.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Receiver.hixie.test.js new file mode 100644 index 0000000..043d3bc --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Receiver.hixie.test.js @@ -0,0 +1,158 @@ +var assert = require('assert') + , expect = require('expect.js') + , Receiver = require('../lib/Receiver.hixie'); +require('./hybi-common'); + +describe('Receiver', function() { + it('can parse text message', function() { + var p = new Receiver(); + var packet = '00 48 65 6c 6c 6f ff'; + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal('Hello', data); + }; + + p.add(getBufferFromHexString(packet)); + expect(gotData).to.equal(true); + }); + + it('can parse multiple text messages', function() { + var p = new Receiver(); + var packet = '00 48 65 6c 6c 6f ff 00 48 65 6c 6c 6f ff'; + + var gotData = false; + var messages = []; + p.ontext = function(data) { + gotData = true; + messages.push(data); + }; + + p.add(getBufferFromHexString(packet)); + expect(gotData).to.equal(true); + for (var i = 0; i < 2; ++i) { + expect(messages[i]).to.equal('Hello'); + } + }); + + it('can parse empty message', function() { + var p = new Receiver(); + var packet = '00 ff'; + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal('', data); + }; + + p.add(getBufferFromHexString(packet)); + expect(gotData).to.equal(true); + }); + + it('can parse text messages delivered over multiple frames', function() { + var p = new Receiver(); + var packets = [ + '00 48', + '65 6c 6c', + '6f ff 00 48', + '65', + '6c 6c 6f', + 'ff' + ]; + + var gotData = false; + var messages = []; + p.ontext = function(data) { + gotData = true; + messages.push(data); + }; + + for (var i = 0; i < packets.length; ++i) { + p.add(getBufferFromHexString(packets[i])); + } + expect(gotData).to.equal(true); + for (var i = 0; i < 2; ++i) { + expect(messages[i]).to.equal('Hello'); + } + }); + + it('emits an error if a payload doesnt start with 0x00', function() { + var p = new Receiver(); + var packets = [ + '00 6c ff', + '00 6c ff ff', + 'ff 00 6c ff 00 6c ff', + '00', + '6c 6c 6f', + 'ff' + ]; + + var gotData = false; + var gotError = false; + var messages = []; + p.ontext = function(data) { + gotData = true; + messages.push(data); + }; + p.onerror = function(reason, code) { + gotError = code == true; + }; + + for (var i = 0; i < packets.length && !gotError; ++i) { + p.add(getBufferFromHexString(packets[i])); + } + expect(gotError).to.equal(true); + expect(messages[0]).to.equal('l'); + expect(messages[1]).to.equal('l'); + expect(messages.length).to.equal(2); + }); + + it('can parse close messages', function() { + var p = new Receiver(); + var packets = [ + 'ff 00' + ]; + + var gotClose = false; + var gotError = false; + p.onclose = function() { + gotClose = true; + }; + p.onerror = function(reason, code) { + gotError = code == true; + }; + + for (var i = 0; i < packets.length && !gotError; ++i) { + p.add(getBufferFromHexString(packets[i])); + } + expect(gotClose).to.equal(true); + expect(gotError).to.equal(false); + }); + + it('can parse binary messages delivered over multiple frames', function() { + var p = new Receiver(); + var packets = [ + '80 05 48', + '65 6c 6c', + '6f 80 80 05 48', + '65', + '6c 6c 6f' + ]; + + var gotData = false; + var messages = []; + p.ontext = function(data) { + gotData = true; + messages.push(data); + }; + + for (var i = 0; i < packets.length; ++i) { + p.add(getBufferFromHexString(packets[i])); + } + expect(gotData).to.equal(true); + for (var i = 0; i < 2; ++i) { + expect(messages[i]).to.equal('Hello'); + } + }); +}); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Receiver.test.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Receiver.test.js new file mode 100644 index 0000000..b0b5c0a --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Receiver.test.js @@ -0,0 +1,255 @@ +var assert = require('assert') + , Receiver = require('../lib/Receiver'); +require('should'); +require('./hybi-common'); + +describe('Receiver', function() { + it('can parse unmasked text message', function() { + var p = new Receiver(); + var packet = '81 05 48 65 6c 6c 6f'; + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal('Hello', data); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse close message', function() { + var p = new Receiver(); + var packet = '88 00'; + + var gotClose = false; + p.onclose = function(data) { + gotClose = true; + }; + + p.add(getBufferFromHexString(packet)); + gotClose.should.be.ok; + }); + it('can parse masked text message', function() { + var p = new Receiver(); + var packet = '81 93 34 83 a8 68 01 b9 92 52 4f a1 c6 09 59 e6 8a 52 16 e6 cb 00 5b a1 d5'; + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal('5:::{"name":"echo"}', data); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse a masked text message longer than 125 bytes', function() { + var p = new Receiver(); + var message = 'A'; + for (var i = 0; i < 300; ++i) message += (i % 5).toString(); + var packet = '81 FE ' + pack(4, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal(message, data); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse a really long masked text message', function() { + var p = new Receiver(); + var message = 'A'; + for (var i = 0; i < 64*1024; ++i) message += (i % 5).toString(); + var packet = '81 FF ' + pack(16, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal(message, data); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse a fragmented masked text message of 300 bytes', function() { + var p = new Receiver(); + var message = 'A'; + for (var i = 0; i < 300; ++i) message += (i % 5).toString(); + var msgpiece1 = message.substr(0, 150); + var msgpiece2 = message.substr(150); + var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68')); + var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68')); + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal(message, data); + }; + + p.add(getBufferFromHexString(packet1)); + p.add(getBufferFromHexString(packet2)); + gotData.should.be.ok; + }); + it('can parse a ping message', function() { + var p = new Receiver(); + var message = 'Hello'; + var packet = '89 ' + getHybiLengthAsHexString(message.length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); + + var gotPing = false; + p.onping = function(data) { + gotPing = true; + assert.equal(message, data); + }; + + p.add(getBufferFromHexString(packet)); + gotPing.should.be.ok; + }); + it('can parse a ping with no data', function() { + var p = new Receiver(); + var packet = '89 00'; + + var gotPing = false; + p.onping = function(data) { + gotPing = true; + }; + + p.add(getBufferFromHexString(packet)); + gotPing.should.be.ok; + }); + it('can parse a fragmented masked text message of 300 bytes with a ping in the middle', function() { + var p = new Receiver(); + var message = 'A'; + for (var i = 0; i < 300; ++i) message += (i % 5).toString(); + + var msgpiece1 = message.substr(0, 150); + var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68')); + + var pingMessage = 'Hello'; + var pingPacket = '89 ' + getHybiLengthAsHexString(pingMessage.length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68')); + + var msgpiece2 = message.substr(150); + var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68')); + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal(message, data); + }; + var gotPing = false; + p.onping = function(data) { + gotPing = true; + assert.equal(pingMessage, data); + }; + + p.add(getBufferFromHexString(packet1)); + p.add(getBufferFromHexString(pingPacket)); + p.add(getBufferFromHexString(packet2)); + gotData.should.be.ok; + gotPing.should.be.ok; + }); + it('can parse a fragmented masked text message of 300 bytes with a ping in the middle, which is delievered over sevaral tcp packets', function() { + var p = new Receiver(); + var message = 'A'; + for (var i = 0; i < 300; ++i) message += (i % 5).toString(); + + var msgpiece1 = message.substr(0, 150); + var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68')); + + var pingMessage = 'Hello'; + var pingPacket = '89 ' + getHybiLengthAsHexString(pingMessage.length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68')); + + var msgpiece2 = message.substr(150); + var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68')); + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal(message, data); + }; + var gotPing = false; + p.onping = function(data) { + gotPing = true; + assert.equal(pingMessage, data); + }; + + var buffers = []; + buffers = buffers.concat(splitBuffer(getBufferFromHexString(packet1))); + buffers = buffers.concat(splitBuffer(getBufferFromHexString(pingPacket))); + buffers = buffers.concat(splitBuffer(getBufferFromHexString(packet2))); + for (var i = 0; i < buffers.length; ++i) { + p.add(buffers[i]); + } + gotData.should.be.ok; + gotPing.should.be.ok; + }); + it('can parse a 100 byte long masked binary message', function() { + var p = new Receiver(); + var length = 100; + var message = new Buffer(length); + for (var i = 0; i < length; ++i) message[i] = i % 256; + var originalMessage = getHexStringFromBuffer(message); + var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); + + var gotData = false; + p.onbinary = function(data) { + gotData = true; + assert.equal(originalMessage, getHexStringFromBuffer(data)); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse a 256 byte long masked binary message', function() { + var p = new Receiver(); + var length = 256; + var message = new Buffer(length); + for (var i = 0; i < length; ++i) message[i] = i % 256; + var originalMessage = getHexStringFromBuffer(message); + var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); + + var gotData = false; + p.onbinary = function(data) { + gotData = true; + assert.equal(originalMessage, getHexStringFromBuffer(data)); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse a 200kb long masked binary message', function() { + var p = new Receiver(); + var length = 200 * 1024; + var message = new Buffer(length); + for (var i = 0; i < length; ++i) message[i] = i % 256; + var originalMessage = getHexStringFromBuffer(message); + var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); + + var gotData = false; + p.onbinary = function(data) { + gotData = true; + assert.equal(originalMessage, getHexStringFromBuffer(data)); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse a 200kb long unmasked binary message', function() { + var p = new Receiver(); + var length = 200 * 1024; + var message = new Buffer(length); + for (var i = 0; i < length; ++i) message[i] = i % 256; + var originalMessage = getHexStringFromBuffer(message); + var packet = '82 ' + getHybiLengthAsHexString(length, false) + ' ' + getHexStringFromBuffer(message); + + var gotData = false; + p.onbinary = function(data) { + gotData = true; + assert.equal(originalMessage, getHexStringFromBuffer(data)); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); +}); + diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Sender.hixie.test.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Sender.hixie.test.js new file mode 100644 index 0000000..783f892 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Sender.hixie.test.js @@ -0,0 +1,134 @@ +var assert = require('assert') + , Sender = require('../lib/Sender.hixie'); +require('should'); +require('./hybi-common'); + +describe('Sender', function() { + describe('#send', function() { + it('frames and sends a text message', function(done) { + var message = 'Hello world'; + var received; + var socket = { + write: function(data, encoding, cb) { + received = data; + process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.send(message, {}, function() { + received.toString('utf8').should.eql('\u0000' + message + '\ufffd'); + done(); + }); + }); + + it('frames and sends an empty message', function(done) { + var socket = { + write: function(data, encoding, cb) { + done(); + } + }; + var sender = new Sender(socket, {}); + sender.send('', {}, function() {}); + }); + + it('frames and sends a buffer', function(done) { + var received; + var socket = { + write: function(data, encoding, cb) { + received = data; + process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.send(new Buffer('foobar'), {}, function() { + received.toString('utf8').should.eql('\u0000foobar\ufffd'); + done(); + }); + }); + + it('frames and sends a binary message', function(done) { + var message = 'Hello world'; + var received; + var socket = { + write: function(data, encoding, cb) { + received = data; + process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.send(message, {binary: true}, function() { + received.toString('hex').should.eql( + // 0x80 0x0b H e l l o w o r l d + '800b48656c6c6f20776f726c64'); + done(); + }); + }); +/* + it('throws an exception for binary data', function(done) { + var socket = { + write: function(data, encoding, cb) { + process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.on('error', function() { + done(); + }); + sender.send(new Buffer(100), {binary: true}, function() {}); + }); +*/ + it('can fauxe stream data', function(done) { + var received = []; + var socket = { + write: function(data, encoding, cb) { + received.push(data); + process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.send(new Buffer('foobar'), { fin: false }, function() {}); + sender.send('bazbar', { fin: false }, function() {}); + sender.send(new Buffer('end'), { fin: true }, function() { + received[0].toString('utf8').should.eql('\u0000foobar'); + received[1].toString('utf8').should.eql('bazbar'); + received[2].toString('utf8').should.eql('end\ufffd'); + done(); + }); + }); + }); + + describe('#close', function() { + it('sends a hixie close frame', function(done) { + var received; + var socket = { + write: function(data, encoding, cb) { + received = data; + process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.close(null, null, null, function() { + received.toString('utf8').should.eql('\ufffd\u0000'); + done(); + }); + }); + + it('sends a message end marker if fauxe streaming has started, before hixie close frame', function(done) { + var received = []; + var socket = { + write: function(data, encoding, cb) { + received.push(data); + if (cb) process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.send(new Buffer('foobar'), { fin: false }, function() {}); + sender.close(null, null, null, function() { + received[0].toString('utf8').should.eql('\u0000foobar'); + received[1].toString('utf8').should.eql('\ufffd'); + received[2].toString('utf8').should.eql('\ufffd\u0000'); + done(); + }); + }); + }); +}); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Sender.test.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Sender.test.js new file mode 100644 index 0000000..43b4864 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Sender.test.js @@ -0,0 +1,24 @@ +var Sender = require('../lib/Sender'); +require('should'); + +describe('Sender', function() { + describe('#frameAndSend', function() { + it('does not modify a masked binary buffer', function() { + var sender = new Sender({ write: function() {} }); + var buf = new Buffer([1, 2, 3, 4, 5]); + sender.frameAndSend(2, buf, true, true); + buf[0].should.eql(1); + buf[1].should.eql(2); + buf[2].should.eql(3); + buf[3].should.eql(4); + buf[4].should.eql(5); + }); + + it('does not modify a masked text buffer', function() { + var sender = new Sender({ write: function() {} }); + var text = 'hi there'; + sender.frameAndSend(1, text, true, true); + text.should.eql('hi there'); + }); + }); +}); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Validation.test.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Validation.test.js new file mode 100644 index 0000000..37c3399 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/Validation.test.js @@ -0,0 +1,23 @@ +var Validation = require('../lib/Validation').Validation; +require('should'); + +describe('Validation', function() { + describe('isValidUTF8', function() { + it('should return true for a valid utf8 string', function() { + var validBuffer = new Buffer('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque gravida mattis rhoncus. Donec iaculis, metus quis varius accumsan, erat mauris condimentum diam, et egestas erat enim ut ligula. Praesent sollicitudin tellus eget dolor euismod euismod. Nullam ac augue nec neque varius luctus. Curabitur elit mi, consequat ultricies adipiscing mollis, scelerisque in erat. Phasellus facilisis fermentum ullamcorper. Nulla et sem eu arcu pharetra pellentesque. Praesent consectetur tempor justo, vel iaculis dui ullamcorper sit amet. Integer tristique viverra ullamcorper. Vivamus laoreet, nulla eget suscipit eleifend, lacus lectus feugiat libero, non fermentum erat nisi at risus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut pulvinar dignissim tellus, eu dignissim lorem vulputate quis. Morbi ut pulvinar augue.'); + Validation.isValidUTF8(validBuffer).should.be.ok; + }); + it('should return false for an erroneous string', function() { + var invalidBuffer = new Buffer([0xce, 0xba, 0xe1, 0xbd, 0xb9, 0xcf, 0x83, 0xce, 0xbc, 0xce, 0xb5, 0xed, 0xa0, 0x80, 0x65, 0x64, 0x69, 0x74, 0x65, 0x64]); + Validation.isValidUTF8(invalidBuffer).should.not.be.ok; + }); + it('should return true for valid cases from the autobahn test suite', function() { + Validation.isValidUTF8(new Buffer('\xf0\x90\x80\x80')).should.be.ok; + Validation.isValidUTF8(new Buffer([0xf0, 0x90, 0x80, 0x80])).should.be.ok; + }); + it('should return false for erroneous autobahn strings', function() { + Validation.isValidUTF8(new Buffer([0xce, 0xba, 0xe1, 0xbd])).should.not.be.ok; + }); + }); +}); + diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/WebSocket.integration.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/WebSocket.integration.js new file mode 100644 index 0000000..5d4f426 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/WebSocket.integration.js @@ -0,0 +1,44 @@ +var assert = require('assert') + , WebSocket = require('../') + , server = require('./testserver'); + +var port = 20000; + +function getArrayBuffer(buf) { + var l = buf.length; + var arrayBuf = new ArrayBuffer(l); + var uint8View = new Uint8Array(arrayBuf); + + for (var i = 0; i < l; i++) { + uint8View[i] = buf[i]; + } + return uint8View.buffer; +} + +function areArraysEqual(x, y) { + if (x.length != y.length) return false; + for (var i = 0, l = x.length; i < l; ++i) { + if (x[i] !== y[i]) return false; + } + return true; +} + +describe('WebSocket', function() { + it('communicates successfully with echo service', function(done) { + var ws = new WebSocket('ws://echo.websocket.org/', {protocolVersion: 13, origin: 'http://websocket.org'}); + var str = Date.now().toString(); + var dataReceived = false; + ws.on('open', function() { + ws.send(str, {mask: true}); + }); + ws.on('close', function() { + assert.equal(true, dataReceived); + done(); + }); + ws.on('message', function(data, flags) { + assert.equal(str, data); + ws.terminate(); + dataReceived = true; + }); + }); +}); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/WebSocket.test.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/WebSocket.test.js new file mode 100644 index 0000000..b57daa6 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/WebSocket.test.js @@ -0,0 +1,1698 @@ +var assert = require('assert') + , https = require('https') + , http = require('http') + , should = require('should') + , WebSocket = require('../') + , WebSocketServer = require('../').Server + , fs = require('fs') + , server = require('./testserver') + , crypto = require('crypto'); + +var port = 20000; + +function getArrayBuffer(buf) { + var l = buf.length; + var arrayBuf = new ArrayBuffer(l); + var uint8View = new Uint8Array(arrayBuf); + for (var i = 0; i < l; i++) { + uint8View[i] = buf[i]; + } + return uint8View.buffer; +} + + +function areArraysEqual(x, y) { + if (x.length != y.length) return false; + for (var i = 0, l = x.length; i < l; ++i) { + if (x[i] !== y[i]) return false; + } + return true; +} + +describe('WebSocket', function() { + describe('#ctor', function() { + it('throws exception for invalid url', function(done) { + try { + var ws = new WebSocket('echo.websocket.org'); + } + catch (e) { + done(); + } + }); + }); + + describe('options', function() { + it('should accept an `agent` option', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var agent = { + addRequest: function() { + wss.close(); + done(); + } + }; + var ws = new WebSocket('ws://localhost:' + port, { agent: agent }); + }); + }); + }); + + describe('properties', function() { + it('#bytesReceived exposes number of bytes received', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('message', function() { + ws.bytesReceived.should.eql(8); + wss.close(); + done(); + }); + }); + wss.on('connection', function(ws) { + ws.send('foobar'); + }); + }); + + it('#url exposes the server url', function(done) { + server.createServer(++port, function(srv) { + var url = 'ws://localhost:' + port; + var ws = new WebSocket(url); + assert.equal(url, ws.url); + ws.terminate(); + ws.on('close', function() { + srv.close(); + done(); + }); + }); + }); + + it('#protocolVersion exposes the protocol version', function(done) { + server.createServer(++port, function(srv) { + var url = 'ws://localhost:' + port; + var ws = new WebSocket(url); + assert.equal(13, ws.protocolVersion); + ws.terminate(); + ws.on('close', function() { + srv.close(); + done(); + }); + }); + }); + + describe('#bufferedAmount', function() { + it('defaults to zero', function(done) { + server.createServer(++port, function(srv) { + var url = 'ws://localhost:' + port; + var ws = new WebSocket(url); + assert.equal(0, ws.bufferedAmount); + ws.terminate(); + ws.on('close', function() { + srv.close(); + done(); + }); + }); + }); + + it('stress kernel write buffer', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + }); + wss.on('connection', function(ws) { + while (true) { + if (ws.bufferedAmount > 0) break; + ws.send((new Array(10000)).join('hello')); + } + ws.terminate(); + ws.on('close', function() { + wss.close(); + done(); + }); + }); + }); + }); + + describe('Custom headers', function() { + it('request has an authorization header', function (done) { + var auth = 'test:testpass'; + var srv = http.createServer(function (req, res) {}); + var wss = new WebSocketServer({server: srv}); + srv.listen(++port); + var ws = new WebSocket('ws://' + auth + '@localhost:' + port); + srv.on('upgrade', function (req, socket, head) { + assert(req.headers.authorization, 'auth header exists'); + assert.equal(req.headers.authorization, 'Basic ' + new Buffer(auth).toString('base64')); + ws.terminate(); + ws.on('close', function () { + srv.close(); + wss.close(); + done(); + }); + }); + }); + + it('accepts custom headers', function (done) { + var srv = http.createServer(function (req, res) {}); + var wss = new WebSocketServer({server: srv}); + srv.listen(++port); + + var ws = new WebSocket('ws://localhost:' + port, { + headers: { + 'Cookie': 'foo=bar' + } + }); + + srv.on('upgrade', function (req, socket, head) { + assert(req.headers.cookie, 'auth header exists'); + assert.equal(req.headers.cookie, 'foo=bar'); + + ws.terminate(); + ws.on('close', function () { + srv.close(); + wss.close(); + done(); + }); + }); + }); + }); + + describe('#readyState', function() { + it('defaults to connecting', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + assert.equal(WebSocket.CONNECTING, ws.readyState); + ws.terminate(); + ws.on('close', function() { + srv.close(); + done(); + }); + }); + }); + + it('set to open once connection is established', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + assert.equal(WebSocket.OPEN, ws.readyState); + srv.close(); + done(); + }); + }); + }); + + it('set to closed once connection is closed', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.close(1001); + ws.on('close', function() { + assert.equal(WebSocket.CLOSED, ws.readyState); + srv.close(); + done(); + }); + }); + }); + + it('set to closed once connection is terminated', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.terminate(); + ws.on('close', function() { + assert.equal(WebSocket.CLOSED, ws.readyState); + srv.close(); + done(); + }); + }); + }); + }); + + /* + * Ready state constants + */ + + var readyStates = { + CONNECTING: 0, + OPEN: 1, + CLOSING: 2, + CLOSED: 3 + }; + + /* + * Ready state constant tests + */ + + Object.keys(readyStates).forEach(function(state) { + describe('.' + state, function() { + it('is enumerable property of class', function() { + var propertyDescripter = Object.getOwnPropertyDescriptor(WebSocket, state) + assert.equal(readyStates[state], propertyDescripter.value); + assert.equal(true, propertyDescripter.enumerable); + }); + }); + }); + + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + Object.keys(readyStates).forEach(function(state) { + describe('.' + state, function() { + it('is property of instance', function() { + assert.equal(readyStates[state], ws[state]); + }); + }); + }); + }); + }); + + describe('events', function() { + it('emits a ping event', function(done) { + var wss = new WebSocketServer({port: ++port}); + wss.on('connection', function(client) { + client.ping(); + }); + var ws = new WebSocket('ws://localhost:' + port); + ws.on('ping', function() { + ws.terminate(); + wss.close(); + done(); + }); + }); + + it('emits a pong event', function(done) { + var wss = new WebSocketServer({port: ++port}); + wss.on('connection', function(client) { + client.pong(); + }); + var ws = new WebSocket('ws://localhost:' + port); + ws.on('pong', function() { + ws.terminate(); + wss.close(); + done(); + }); + }); + }); + + describe('connection establishing', function() { + it('can disconnect before connection is established', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.terminate(); + ws.on('open', function() { + assert.fail('connect shouldnt be raised here'); + }); + ws.on('close', function() { + srv.close(); + done(); + }); + }); + }); + + it('can close before connection is established', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.close(1001); + ws.on('open', function() { + assert.fail('connect shouldnt be raised here'); + }); + ws.on('close', function() { + srv.close(); + done(); + }); + }); + }); + + it('invalid server key is denied', function(done) { + server.createServer(++port, server.handlers.invalidKey, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('error', function() { + srv.close(); + done(); + }); + }); + }); + + it('close event is raised when server closes connection', function(done) { + server.createServer(++port, server.handlers.closeAfterConnect, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('close', function() { + srv.close(); + done(); + }); + }); + }); + + it('error is emitted if server aborts connection', function(done) { + server.createServer(++port, server.handlers.return401, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + assert.fail('connect shouldnt be raised here'); + }); + ws.on('error', function() { + srv.close(); + done(); + }); + }); + }); + }); + + describe('#pause and #resume', function() { + it('pauses the underlying stream', function(done) { + // this test is sort-of racecondition'y, since an unlikely slow connection + // to localhost can cause the test to succeed even when the stream pausing + // isn't working as intended. that is an extremely unlikely scenario, though + // and an acceptable risk for the test. + var client; + var serverClient; + var openCount = 0; + function onOpen() { + if (++openCount == 2) { + var paused = true; + serverClient.on('message', function() { + paused.should.not.be.ok; + wss.close(); + done(); + }); + serverClient.pause(); + setTimeout(function() { + paused = false; + serverClient.resume(); + }, 200); + client.send('foo'); + } + } + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + serverClient = ws; + serverClient.on('open', onOpen); + }); + wss.on('connection', function(ws) { + client = ws; + onOpen(); + }); + }); + }); + + describe('#ping', function() { + it('before connect should fail', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('error', function() {}); + try { + ws.ping(); + } + catch (e) { + srv.close(); + ws.terminate(); + done(); + } + }); + }); + + it('before connect can silently fail', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('error', function() {}); + ws.ping('', {}, true); + srv.close(); + ws.terminate(); + done(); + }); + }); + + it('without message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.ping(); + }); + srv.on('ping', function(message) { + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.ping('hi'); + }); + srv.on('ping', function(message) { + assert.equal('hi', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with encoded message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.ping('hi', {mask: true}); + }); + srv.on('ping', function(message, flags) { + assert.ok(flags.masked); + assert.equal('hi', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + }); + + describe('#pong', function() { + it('before connect should fail', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('error', function() {}); + try { + ws.pong(); + } + catch (e) { + srv.close(); + ws.terminate(); + done(); + } + }); + }); + + it('before connect can silently fail', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('error', function() {}); + ws.pong('', {}, true); + srv.close(); + ws.terminate(); + done(); + }); + }); + + it('without message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.pong(); + }); + srv.on('pong', function(message) { + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.pong('hi'); + }); + srv.on('pong', function(message) { + assert.equal('hi', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with encoded message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.pong('hi', {mask: true}); + }); + srv.on('pong', function(message, flags) { + assert.ok(flags.masked); + assert.equal('hi', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + }); + + describe('#send', function() { + it('very long binary data can be sent and received (with echoing server)', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var array = new Float32Array(5 * 1024 * 1024); + for (var i = 0; i < array.length; ++i) array[i] = i / 5; + ws.on('open', function() { + ws.send(array, {binary: true}); + }); + ws.on('message', function(message, flags) { + assert.ok(flags.binary); + assert.ok(areArraysEqual(array, new Float32Array(getArrayBuffer(message)))); + ws.terminate(); + srv.close(); + done(); + }); + }); + }); + + it('can send and receive text data', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.send('hi'); + }); + ws.on('message', function(message, flags) { + assert.equal('hi', message); + ws.terminate(); + srv.close(); + done(); + }); + }); + }); + + it('send and receive binary data as an array', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var array = new Float32Array(6); + for (var i = 0; i < array.length; ++i) array[i] = i / 2; + var partial = array.subarray(2, 5); + ws.on('open', function() { + ws.send(partial, {binary: true}); + }); + ws.on('message', function(message, flags) { + assert.ok(flags.binary); + assert.ok(areArraysEqual(partial, new Float32Array(getArrayBuffer(message)))); + ws.terminate(); + srv.close(); + done(); + }); + }); + }); + + it('binary data can be sent and received as buffer', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var buf = new Buffer('foobar'); + ws.on('open', function() { + ws.send(buf, {binary: true}); + }); + ws.on('message', function(message, flags) { + assert.ok(flags.binary); + assert.ok(areArraysEqual(buf, message)); + ws.terminate(); + srv.close(); + done(); + }); + }); + }); + + it('ArrayBuffer is auto-detected without binary flag', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var array = new Float32Array(5); + for (var i = 0; i < array.length; ++i) array[i] = i / 2; + ws.on('open', function() { + ws.send(array.buffer); + }); + ws.onmessage = function (event) { + assert.ok(event.type = 'Binary'); + assert.ok(areArraysEqual(array, new Float32Array(getArrayBuffer(event.data)))); + ws.terminate(); + srv.close(); + done(); + }; + }); + }); + + it('Buffer is auto-detected without binary flag', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var buf = new Buffer('foobar'); + ws.on('open', function() { + ws.send(buf); + }); + ws.onmessage = function (event) { + assert.ok(event.type = 'Binary'); + assert.ok(areArraysEqual(event.data, buf)); + ws.terminate(); + srv.close(); + done(); + }; + }); + }); + + it('before connect should fail', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('error', function() {}); + try { + ws.send('hi'); + } + catch (e) { + ws.terminate(); + srv.close(); + done(); + } + }); + }); + + it('before connect should pass error through callback, if present', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('error', function() {}); + ws.send('hi', function(error) { + assert.ok(error instanceof Error); + ws.terminate(); + srv.close(); + done(); + }); + }); + }); + + it('without data should be successful', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.send(); + }); + srv.on('message', function(message, flags) { + assert.equal('', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('calls optional callback when flushed', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.send('hi', function() { + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + }); + + it('with unencoded message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.send('hi'); + }); + srv.on('message', function(message, flags) { + assert.equal('hi', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with encoded message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.send('hi', {mask: true}); + }); + srv.on('message', function(message, flags) { + assert.ok(flags.masked); + assert.equal('hi', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with unencoded binary message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var array = new Float32Array(5); + for (var i = 0; i < array.length; ++i) array[i] = i / 2; + ws.on('open', function() { + ws.send(array, {binary: true}); + }); + srv.on('message', function(message, flags) { + assert.ok(flags.binary); + assert.ok(areArraysEqual(array, new Float32Array(getArrayBuffer(message)))); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with encoded binary message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var array = new Float32Array(5); + for (var i = 0; i < array.length; ++i) array[i] = i / 2; + ws.on('open', function() { + ws.send(array, {mask: true, binary: true}); + }); + srv.on('message', function(message, flags) { + assert.ok(flags.binary); + assert.ok(flags.masked); + assert.ok(areArraysEqual(array, new Float32Array(getArrayBuffer(message)))); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with binary stream will send fragmented data', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var callbackFired = false; + ws.on('open', function() { + var fileStream = fs.createReadStream('test/fixtures/textfile'); + fileStream.bufferSize = 100; + ws.send(fileStream, {binary: true}, function(error) { + assert.equal(null, error); + callbackFired = true; + }); + }); + srv.on('message', function(data, flags) { + assert.ok(flags.binary); + assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile'), data)); + ws.terminate(); + }); + ws.on('close', function() { + assert.ok(callbackFired); + srv.close(); + done(); + }); + }); + }); + + it('with text stream will send fragmented data', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var callbackFired = false; + ws.on('open', function() { + var fileStream = fs.createReadStream('test/fixtures/textfile'); + fileStream.setEncoding('utf8'); + fileStream.bufferSize = 100; + ws.send(fileStream, {binary: false}, function(error) { + assert.equal(null, error); + callbackFired = true; + }); + }); + srv.on('message', function(data, flags) { + assert.ok(!flags.binary); + assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data)); + ws.terminate(); + }); + ws.on('close', function() { + assert.ok(callbackFired); + srv.close(); + done(); + }); + }); + }); + + it('will cause intermittent send to be delayed in order', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + var fileStream = fs.createReadStream('test/fixtures/textfile'); + fileStream.setEncoding('utf8'); + fileStream.bufferSize = 100; + ws.send(fileStream); + ws.send('foobar'); + ws.send('baz'); + }); + var receivedIndex = 0; + srv.on('message', function(data, flags) { + ++receivedIndex; + if (receivedIndex == 1) { + assert.ok(!flags.binary); + assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data)); + } + else if (receivedIndex == 2) { + assert.ok(!flags.binary); + assert.equal('foobar', data); + } + else { + assert.ok(!flags.binary); + assert.equal('baz', data); + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('will cause intermittent stream to be delayed in order', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + var fileStream = fs.createReadStream('test/fixtures/textfile'); + fileStream.setEncoding('utf8'); + fileStream.bufferSize = 100; + ws.send(fileStream); + var i = 0; + ws.stream(function(error, send) { + assert.ok(!error); + if (++i == 1) send('foo'); + else send('bar', true); + }); + }); + var receivedIndex = 0; + srv.on('message', function(data, flags) { + ++receivedIndex; + if (receivedIndex == 1) { + assert.ok(!flags.binary); + assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data)); + } + else if (receivedIndex == 2) { + assert.ok(!flags.binary); + assert.equal('foobar', data); + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('will cause intermittent ping to be delivered', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + var fileStream = fs.createReadStream('test/fixtures/textfile'); + fileStream.setEncoding('utf8'); + fileStream.bufferSize = 100; + ws.send(fileStream); + ws.ping('foobar'); + }); + var receivedIndex = 0; + srv.on('message', function(data, flags) { + assert.ok(!flags.binary); + assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data)); + if (++receivedIndex == 2) { + srv.close(); + ws.terminate(); + done(); + } + }); + srv.on('ping', function(data) { + assert.equal('foobar', data); + if (++receivedIndex == 2) { + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('will cause intermittent pong to be delivered', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + var fileStream = fs.createReadStream('test/fixtures/textfile'); + fileStream.setEncoding('utf8'); + fileStream.bufferSize = 100; + ws.send(fileStream); + ws.pong('foobar'); + }); + var receivedIndex = 0; + srv.on('message', function(data, flags) { + assert.ok(!flags.binary); + assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data)); + if (++receivedIndex == 2) { + srv.close(); + ws.terminate(); + done(); + } + }); + srv.on('pong', function(data) { + assert.equal('foobar', data); + if (++receivedIndex == 2) { + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('will cause intermittent close to be delivered', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + var fileStream = fs.createReadStream('test/fixtures/textfile'); + fileStream.setEncoding('utf8'); + fileStream.bufferSize = 100; + ws.send(fileStream); + ws.close(1000, 'foobar'); + }); + ws.on('close', function() { + srv.close(); + ws.terminate(); + done(); + }); + ws.on('error', function() { /* That's quite alright -- a send was attempted after close */ }); + srv.on('message', function(data, flags) { + assert.ok(!flags.binary); + assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data)); + }); + srv.on('close', function(code, data) { + assert.equal(1000, code); + assert.equal('foobar', data); + }); + }); + }); + }); + + describe('#stream', function() { + it('very long binary data can be streamed', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var buffer = new Buffer(10 * 1024); + for (var i = 0; i < buffer.length; ++i) buffer[i] = i % 0xff; + ws.on('open', function() { + var i = 0; + var blockSize = 800; + var bufLen = buffer.length; + ws.stream({binary: true}, function(error, send) { + assert.ok(!error); + var start = i * blockSize; + var toSend = Math.min(blockSize, bufLen - (i * blockSize)); + var end = start + toSend; + var isFinal = toSend < blockSize; + send(buffer.slice(start, end), isFinal); + i += 1; + }); + }); + srv.on('message', function(data, flags) { + assert.ok(flags.binary); + assert.ok(areArraysEqual(buffer, data)); + ws.terminate(); + srv.close(); + done(); + }); + }); + }); + + it('before connect should pass error through callback', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('error', function() {}); + ws.stream(function(error) { + assert.ok(error instanceof Error); + ws.terminate(); + srv.close(); + done(); + }); + }); + }); + + it('without callback should fail', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var payload = 'HelloWorld'; + ws.on('open', function() { + try { + ws.stream(); + } + catch (e) { + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('will cause intermittent send to be delayed in order', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var payload = 'HelloWorld'; + ws.on('open', function() { + var i = 0; + ws.stream(function(error, send) { + assert.ok(!error); + if (++i == 1) { + send(payload.substr(0, 5)); + ws.send('foobar'); + ws.send('baz'); + } + else { + send(payload.substr(5, 5), true); + } + }); + }); + var receivedIndex = 0; + srv.on('message', function(data, flags) { + ++receivedIndex; + if (receivedIndex == 1) { + assert.ok(!flags.binary); + assert.equal(payload, data); + } + else if (receivedIndex == 2) { + assert.ok(!flags.binary); + assert.equal('foobar', data); + } + else { + assert.ok(!flags.binary); + assert.equal('baz', data); + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('will cause intermittent stream to be delayed in order', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var payload = 'HelloWorld'; + ws.on('open', function() { + var i = 0; + ws.stream(function(error, send) { + assert.ok(!error); + if (++i == 1) { + send(payload.substr(0, 5)); + var i2 = 0; + ws.stream(function(error, send) { + assert.ok(!error); + if (++i2 == 1) send('foo'); + else send('bar', true); + }); + ws.send('baz'); + } + else send(payload.substr(5, 5), true); + }); + }); + var receivedIndex = 0; + srv.on('message', function(data, flags) { + ++receivedIndex; + if (receivedIndex == 1) { + assert.ok(!flags.binary); + assert.equal(payload, data); + } + else if (receivedIndex == 2) { + assert.ok(!flags.binary); + assert.equal('foobar', data); + } + else if (receivedIndex == 3){ + assert.ok(!flags.binary); + assert.equal('baz', data); + setTimeout(function() { + srv.close(); + ws.terminate(); + done(); + }, 1000); + } + else throw new Error('more messages than we actually sent just arrived'); + }); + }); + }); + + it('will cause intermittent ping to be delivered', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var payload = 'HelloWorld'; + ws.on('open', function() { + var i = 0; + ws.stream(function(error, send) { + assert.ok(!error); + if (++i == 1) { + send(payload.substr(0, 5)); + ws.ping('foobar'); + } + else { + send(payload.substr(5, 5), true); + } + }); + }); + var receivedIndex = 0; + srv.on('message', function(data, flags) { + assert.ok(!flags.binary); + assert.equal(payload, data); + if (++receivedIndex == 2) { + srv.close(); + ws.terminate(); + done(); + } + }); + srv.on('ping', function(data) { + assert.equal('foobar', data); + if (++receivedIndex == 2) { + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('will cause intermittent pong to be delivered', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var payload = 'HelloWorld'; + ws.on('open', function() { + var i = 0; + ws.stream(function(error, send) { + assert.ok(!error); + if (++i == 1) { + send(payload.substr(0, 5)); + ws.pong('foobar'); + } + else { + send(payload.substr(5, 5), true); + } + }); + }); + var receivedIndex = 0; + srv.on('message', function(data, flags) { + assert.ok(!flags.binary); + assert.equal(payload, data); + if (++receivedIndex == 2) { + srv.close(); + ws.terminate(); + done(); + } + }); + srv.on('pong', function(data) { + assert.equal('foobar', data); + if (++receivedIndex == 2) { + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('will cause intermittent close to be delivered', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var payload = 'HelloWorld'; + var errorGiven = false; + ws.on('open', function() { + var i = 0; + ws.stream(function(error, send) { + if (++i == 1) { + send(payload.substr(0, 5)); + ws.close(1000, 'foobar'); + } + else if(i == 2) { + send(payload.substr(5, 5), true); + } + else if (i == 3) { + assert.ok(error); + errorGiven = true; + } + }); + }); + ws.on('close', function() { + assert.ok(errorGiven); + srv.close(); + ws.terminate(); + done(); + }); + srv.on('message', function(data, flags) { + assert.ok(!flags.binary); + assert.equal(payload, data); + }); + srv.on('close', function(code, data) { + assert.equal(1000, code); + assert.equal('foobar', data); + }); + }); + }); + }); + + describe('#close', function() { + it('will raise error callback, if any, if called during send stream', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var errorGiven = false; + ws.on('open', function() { + var fileStream = fs.createReadStream('test/fixtures/textfile'); + fileStream.setEncoding('utf8'); + fileStream.bufferSize = 100; + ws.send(fileStream, function(error) { + errorGiven = error != null; + }); + ws.close(1000, 'foobar'); + }); + ws.on('close', function() { + setTimeout(function() { + assert.ok(errorGiven); + srv.close(); + ws.terminate(); + done(); + }, 1000); + }); + }); + }); + + it('without invalid first argument throws exception', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + try { + ws.close('error'); + } + catch (e) { + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('without reserved error code 1004 throws exception', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + try { + ws.close(1004); + } + catch (e) { + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('without message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.close(1000); + }); + srv.on('close', function(code, message, flags) { + assert.equal('', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.close(1000, 'some reason'); + }); + srv.on('close', function(code, message, flags) { + assert.ok(flags.masked); + assert.equal('some reason', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with encoded message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.close(1000, 'some reason', {mask: true}); + }); + srv.on('close', function(code, message, flags) { + assert.ok(flags.masked); + assert.equal('some reason', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('ends connection to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var connectedOnce = false; + ws.on('open', function() { + connectedOnce = true; + ws.close(1000, 'some reason', {mask: true}); + }); + ws.on('close', function() { + assert.ok(connectedOnce); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + }); + + describe('W3C API emulation', function() { + it('should not throw errors when getting and setting', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var listener = function () {}; + + ws.onmessage = listener; + ws.onerror = listener; + ws.onclose = listener; + ws.onopen = listener; + + assert.ok(ws.onopen === listener); + assert.ok(ws.onmessage === listener); + assert.ok(ws.onclose === listener); + assert.ok(ws.onerror === listener); + + srv.close(); + ws.terminate(); + done(); + }); + }); + + it('should work the same as the EventEmitter api', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var listener = function() {}; + var message = 0; + var close = 0; + var open = 0; + + ws.onmessage = function(messageEvent) { + assert.ok(!!messageEvent.data); + ++message; + ws.close(); + }; + + ws.onopen = function() { + ++open; + } + + ws.onclose = function() { + ++close; + } + + ws.on('open', function() { + ws.send('foo'); + }); + + ws.on('close', function() { + process.nextTick(function() { + assert.ok(message === 1); + assert.ok(open === 1); + assert.ok(close === 1); + + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + }); + + it('should receive text data wrapped in a MessageEvent when using addEventListener', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.addEventListener('open', function() { + ws.send('hi'); + }); + ws.addEventListener('message', function(messageEvent) { + assert.equal('hi', messageEvent.data); + ws.terminate(); + srv.close(); + done(); + }); + }); + }); + + it('should receive valid CloseEvent when server closes with code 1000', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + ws.addEventListener('close', function(closeEvent) { + assert.equal(true, closeEvent.wasClean); + assert.equal(1000, closeEvent.code); + ws.terminate(); + wss.close(); + done(); + }); + }); + wss.on('connection', function(client) { + client.close(1000); + }); + }); + + it('should receive valid CloseEvent when server closes with code 1001', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + ws.addEventListener('close', function(closeEvent) { + assert.equal(false, closeEvent.wasClean); + assert.equal(1001, closeEvent.code); + assert.equal('some daft reason', closeEvent.reason); + ws.terminate(); + wss.close(); + done(); + }); + }); + wss.on('connection', function(client) { + client.close(1001, 'some daft reason'); + }); + }); + + it('should have target set on Events', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + ws.addEventListener('open', function(openEvent) { + assert.equal(ws, openEvent.target); + }); + ws.addEventListener('message', function(messageEvent) { + assert.equal(ws, messageEvent.target); + wss.close(); + }); + ws.addEventListener('close', function(closeEvent) { + assert.equal(ws, closeEvent.target); + ws.emit('error', new Error('forced')); + }); + ws.addEventListener('error', function(errorEvent) { + assert.equal(errorEvent.message, 'forced'); + assert.equal(ws, errorEvent.target); + ws.terminate(); + done(); + }); + }); + wss.on('connection', function(client) { + client.send('hi') + }); + }); + }); + + describe('ssl', function() { + it('can connect to secure websocket server', function(done) { + var options = { + key: fs.readFileSync('test/fixtures/key.pem'), + cert: fs.readFileSync('test/fixtures/certificate.pem') + }; + var app = https.createServer(options, function (req, res) { + res.writeHead(200); + res.end(); + }); + var wss = new WebSocketServer({server: app}); + app.listen(++port, function() { + var ws = new WebSocket('wss://localhost:' + port); + }); + wss.on('connection', function(ws) { + app.close(); + ws.terminate(); + wss.close(); + done(); + }); + }); + + it('can connect to secure websocket server with client side certificate', function(done) { + var options = { + key: fs.readFileSync('test/fixtures/key.pem'), + cert: fs.readFileSync('test/fixtures/certificate.pem'), + ca: [fs.readFileSync('test/fixtures/ca1-cert.pem')], + requestCert: true + }; + var clientOptions = { + key: fs.readFileSync('test/fixtures/agent1-key.pem'), + cert: fs.readFileSync('test/fixtures/agent1-cert.pem') + }; + var app = https.createServer(options, function (req, res) { + res.writeHead(200); + res.end(); + }); + var success = false; + var wss = new WebSocketServer({ + server: app, + verifyClient: function(info) { + success = !!info.req.client.authorized; + return true; + } + }); + app.listen(++port, function() { + var ws = new WebSocket('wss://localhost:' + port, clientOptions); + }); + wss.on('connection', function(ws) { + app.close(); + ws.terminate(); + wss.close(); + success.should.be.ok; + done(); + }); + }); + + it('cannot connect to secure websocket server via ws://', function(done) { + var options = { + key: fs.readFileSync('test/fixtures/key.pem'), + cert: fs.readFileSync('test/fixtures/certificate.pem') + }; + var app = https.createServer(options, function (req, res) { + res.writeHead(200); + res.end(); + }); + var wss = new WebSocketServer({server: app}); + app.listen(++port, function() { + var ws = new WebSocket('ws://localhost:' + port, { rejectUnauthorized :false }); + ws.on('error', function() { + app.close(); + ws.terminate(); + wss.close(); + done(); + }); + }); + }); + + it('can send and receive text data', function(done) { + var options = { + key: fs.readFileSync('test/fixtures/key.pem'), + cert: fs.readFileSync('test/fixtures/certificate.pem') + }; + var app = https.createServer(options, function (req, res) { + res.writeHead(200); + res.end(); + }); + var wss = new WebSocketServer({server: app}); + app.listen(++port, function() { + var ws = new WebSocket('wss://localhost:' + port); + ws.on('open', function() { + ws.send('foobar'); + }); + }); + wss.on('connection', function(ws) { + ws.on('message', function(message, flags) { + message.should.eql('foobar'); + app.close(); + ws.terminate(); + wss.close(); + done(); + }); + }); + }); + + it('can send and receive very long binary data', function(done) { + var options = { + key: fs.readFileSync('test/fixtures/key.pem'), + cert: fs.readFileSync('test/fixtures/certificate.pem') + } + var app = https.createServer(options, function (req, res) { + res.writeHead(200); + res.end(); + }); + crypto.randomBytes(5 * 1024 * 1024, function(ex, buf) { + if (ex) throw ex; + var wss = new WebSocketServer({server: app}); + app.listen(++port, function() { + var ws = new WebSocket('wss://localhost:' + port); + ws.on('open', function() { + ws.send(buf, {binary: true}); + }); + ws.on('message', function(message, flags) { + flags.binary.should.be.ok; + areArraysEqual(buf, message).should.be.ok; + app.close(); + ws.terminate(); + wss.close(); + done(); + }); + }); + wss.on('connection', function(ws) { + ws.on('message', function(message, flags) { + ws.send(message, {binary: true}); + }); + }); + }); + }); + }); + + describe('protocol support discovery', function() { + describe('#supports', function() { + describe('#binary', function() { + it('returns true for hybi transport', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + }); + wss.on('connection', function(client) { + assert.equal(true, client.supports.binary); + wss.close(); + done(); + }); + }); + + it('returns false for hixie transport', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + }); + wss.on('connection', function(client) { + assert.equal(false, client.supports.binary); + wss.close(); + done(); + }); + }); + }); + }); + }); + + describe('host and origin headers', function() { + + it('includes the host header with port number', function(done) { + var srv = http.createServer(); + srv.listen(++port, function(){ + srv.on('upgrade', function(req, socket, upgradeHeade) { + assert.equal('localhost:' + port, req.headers['host']); + srv.close(); + done(); + }); + var ws = new WebSocket('ws://localhost:' + port); + }); + }); + + it('includes the origin header with port number', function(done) { + var srv = http.createServer(); + srv.listen(++port, function() { + srv.on('upgrade', function(req, socket, upgradeHeade) { + assert.equal('localhost:' + port, req.headers['origin']); + srv.close(); + done(); + }); + var ws = new WebSocket('ws://localhost:' + port); + }); + }); + }); + +}); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/WebSocketServer.test.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/WebSocketServer.test.js new file mode 100644 index 0000000..c21fd97 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/WebSocketServer.test.js @@ -0,0 +1,1103 @@ +var http = require('http') + , https = require('https') + , WebSocket = require('../') + , WebSocketServer = WebSocket.Server + , fs = require('fs') + , should = require('should'); + +var port = 8000; + +function getArrayBuffer(buf) { + var l = buf.length; + var arrayBuf = new ArrayBuffer(l); + for (var i = 0; i < l; ++i) { + arrayBuf[i] = buf[i]; + } + return arrayBuf; +} + +function areArraysEqual(x, y) { + if (x.length != y.length) return false; + for (var i = 0, l = x.length; i < l; ++i) { + if (x[i] !== y[i]) return false; + } + return true; +} + +describe('WebSocketServer', function() { + describe('#ctor', function() { + it('throws an error if no option object is passed', function() { + var gotException = false; + try { + var wss = new WebSocketServer(); + } + catch (e) { + gotException = true; + } + gotException.should.be.ok; + }); + + it('throws an error if no port or server is specified', function() { + var gotException = false; + try { + var wss = new WebSocketServer({}); + } + catch (e) { + gotException = true; + } + gotException.should.be.ok; + }); + + it('does not throw an error if no port or server is specified, when the noServer option is true', function() { + var gotException = false; + try { + var wss = new WebSocketServer({noServer: true}); + } + catch (e) { + gotException = true; + } + gotException.should.eql(false); + }); + + it('emits an error if http server bind fails', function(done) { + var wss = new WebSocketServer({port: 1}); + wss.on('error', function() { done(); }); + }); + + it('starts a server on a given port', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + }); + wss.on('connection', function(client) { + wss.close(); + done(); + }); + }); + + it('uses a precreated http server', function (done) { + var srv = http.createServer(); + srv.listen(++port, function () { + var wss = new WebSocketServer({server: srv}); + var ws = new WebSocket('ws://localhost:' + port); + + wss.on('connection', function(client) { + wss.close(); + srv.close(); + done(); + }); + }); + }); + + it('uses a precreated http server listening on unix socket', function (done) { + var srv = http.createServer(); + var sockPath = '/tmp/ws_socket_'+new Date().getTime()+'.'+Math.floor(Math.random() * 1000); + srv.listen(sockPath, function () { + var wss = new WebSocketServer({server: srv}); + var ws = new WebSocket('ws+unix://'+sockPath); + + wss.on('connection', function(client) { + wss.close(); + srv.close(); + done(); + }); + }); + }); + + it('emits path specific connection event', function (done) { + var srv = http.createServer(); + srv.listen(++port, function () { + var wss = new WebSocketServer({server: srv}); + var ws = new WebSocket('ws://localhost:' + port+'/endpointName'); + + wss.on('connection/endpointName', function(client) { + wss.close(); + srv.close(); + done(); + }); + }); + }); + + it('can have two different instances listening on the same http server with two different paths', function(done) { + var srv = http.createServer(); + srv.listen(++port, function () { + var wss1 = new WebSocketServer({server: srv, path: '/wss1'}) + , wss2 = new WebSocketServer({server: srv, path: '/wss2'}); + var doneCount = 0; + wss1.on('connection', function(client) { + wss1.close(); + if (++doneCount == 2) { + srv.close(); + done(); + } + }); + wss2.on('connection', function(client) { + wss2.close(); + if (++doneCount == 2) { + srv.close(); + done(); + } + }); + var ws1 = new WebSocket('ws://localhost:' + port + '/wss1'); + var ws2 = new WebSocket('ws://localhost:' + port + '/wss2?foo=1'); + }); + }); + + it('cannot have two different instances listening on the same http server with the same path', function(done) { + var srv = http.createServer(); + srv.listen(++port, function () { + var wss1 = new WebSocketServer({server: srv, path: '/wss1'}); + try { + var wss2 = new WebSocketServer({server: srv, path: '/wss1'}); + } + catch (e) { + wss1.close(); + srv.close(); + done(); + } + }); + }); + }); + + describe('#close', function() { + it('will close all clients', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('close', function() { + if (++closes == 2) done(); + }); + }); + var closes = 0; + wss.on('connection', function(client) { + client.on('close', function() { + if (++closes == 2) done(); + }); + wss.close(); + }); + }); + + it('does not close a precreated server', function(done) { + var srv = http.createServer(); + var realClose = srv.close; + srv.close = function() { + should.fail('must not close pre-created server'); + } + srv.listen(++port, function () { + var wss = new WebSocketServer({server: srv}); + var ws = new WebSocket('ws://localhost:' + port); + wss.on('connection', function(client) { + wss.close(); + srv.close = realClose; + srv.close(); + done(); + }); + }); + }); + + it('cleans up websocket data on a precreated server', function(done) { + var srv = http.createServer(); + srv.listen(++port, function () { + var wss1 = new WebSocketServer({server: srv, path: '/wss1'}) + , wss2 = new WebSocketServer({server: srv, path: '/wss2'}); + (typeof srv._webSocketPaths).should.eql('object'); + Object.keys(srv._webSocketPaths).length.should.eql(2); + wss1.close(); + Object.keys(srv._webSocketPaths).length.should.eql(1); + wss2.close(); + (typeof srv._webSocketPaths).should.eql('undefined'); + srv.close(); + done(); + }); + }); + }); + + describe('#clients', function() { + it('returns a list of connected clients', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + wss.clients.length.should.eql(0); + var ws = new WebSocket('ws://localhost:' + port); + }); + wss.on('connection', function(client) { + wss.clients.length.should.eql(1); + wss.close(); + done(); + }); + }); + + it('can be disabled', function(done) { + var wss = new WebSocketServer({port: ++port, clientTracking: false}, function() { + wss.clients.length.should.eql(0); + var ws = new WebSocket('ws://localhost:' + port); + }); + wss.on('connection', function(client) { + wss.clients.length.should.eql(0); + wss.close(); + done(); + }); + }); + + it('is updated when client terminates the connection', function(done) { + var ws; + var wss = new WebSocketServer({port: ++port}, function() { + ws = new WebSocket('ws://localhost:' + port); + }); + wss.on('connection', function(client) { + client.on('close', function() { + wss.clients.length.should.eql(0); + wss.close(); + done(); + }); + ws.terminate(); + }); + }); + + it('is updated when client closes the connection', function(done) { + var ws; + var wss = new WebSocketServer({port: ++port}, function() { + ws = new WebSocket('ws://localhost:' + port); + }); + wss.on('connection', function(client) { + client.on('close', function() { + wss.clients.length.should.eql(0); + wss.close(); + done(); + }); + ws.close(); + }); + }); + }); + + describe('#options', function() { + it('exposes options passed to constructor', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + wss.options.port.should.eql(port); + wss.close(); + done(); + }); + }); + }); + + describe('#handleUpgrade', function() { + it('can be used for a pre-existing server', function (done) { + var srv = http.createServer(); + srv.listen(++port, function () { + var wss = new WebSocketServer({noServer: true}); + srv.on('upgrade', function(req, socket, upgradeHead) { + wss.handleUpgrade(req, socket, upgradeHead, function(client) { + client.send('hello'); + }); + }); + var ws = new WebSocket('ws://localhost:' + port); + ws.on('message', function(message) { + message.should.eql('hello'); + wss.close(); + srv.close(); + done(); + }); + }); + }); + }); + + describe('hybi mode', function() { + describe('connection establishing', function() { + it('does not accept connections with no sec-websocket-key', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket' + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(400); + wss.close(); + done(); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('does not accept connections with no sec-websocket-version', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==' + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(400); + wss.close(); + done(); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('does not accept connections with invalid sec-websocket-version', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 12 + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(400); + wss.close(); + done(); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('client can be denied', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o) { + return false; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 8, + 'Sec-WebSocket-Origin': 'http://foobar.com' + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(401); + process.nextTick(function() { + wss.close(); + done(); + }); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('client can be accepted', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o) { + return true; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 13, + 'Origin': 'http://foobar.com' + } + }; + var req = http.request(options); + req.end(); + }); + wss.on('connection', function(ws) { + ws.terminate(); + wss.close(); + done(); + }); + wss.on('error', function() {}); + }); + + it('verifyClient gets client origin', function(done) { + var verifyClientCalled = false; + var wss = new WebSocketServer({port: ++port, verifyClient: function(info) { + info.origin.should.eql('http://foobarbaz.com'); + verifyClientCalled = true; + return false; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 13, + 'Origin': 'http://foobarbaz.com' + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + verifyClientCalled.should.be.ok; + wss.close(); + done(); + }); + }); + wss.on('error', function() {}); + }); + + it('verifyClient gets original request', function(done) { + var verifyClientCalled = false; + var wss = new WebSocketServer({port: ++port, verifyClient: function(info) { + info.req.headers['sec-websocket-key'].should.eql('dGhlIHNhbXBsZSBub25jZQ=='); + verifyClientCalled = true; + return false; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 13, + 'Origin': 'http://foobarbaz.com' + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + verifyClientCalled.should.be.ok; + wss.close(); + done(); + }); + }); + wss.on('error', function() {}); + }); + + it('verifyClient has secure:true for ssl connections', function(done) { + var options = { + key: fs.readFileSync('test/fixtures/key.pem'), + cert: fs.readFileSync('test/fixtures/certificate.pem') + }; + var app = https.createServer(options, function (req, res) { + res.writeHead(200); + res.end(); + }); + var success = false; + var wss = new WebSocketServer({ + server: app, + verifyClient: function(info) { + success = info.secure === true; + return true; + } + }); + app.listen(++port, function() { + var ws = new WebSocket('wss://localhost:' + port); + }); + wss.on('connection', function(ws) { + app.close(); + ws.terminate(); + wss.close(); + success.should.be.ok; + done(); + }); + }); + + it('verifyClient has secure:false for non-ssl connections', function(done) { + var app = http.createServer(function (req, res) { + res.writeHead(200); + res.end(); + }); + var success = false; + var wss = new WebSocketServer({ + server: app, + verifyClient: function(info) { + success = info.secure === false; + return true; + } + }); + app.listen(++port, function() { + var ws = new WebSocket('ws://localhost:' + port); + }); + wss.on('connection', function(ws) { + app.close(); + ws.terminate(); + wss.close(); + success.should.be.ok; + done(); + }); + }); + + it('client can be denied asynchronously', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) { + process.nextTick(function() { + cb(false); + }); + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 8, + 'Sec-WebSocket-Origin': 'http://foobar.com' + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(401); + process.nextTick(function() { + wss.close(); + done(); + }); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('client can be accepted asynchronously', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) { + process.nextTick(function() { + cb(true); + }); + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 13, + 'Origin': 'http://foobar.com' + } + }; + var req = http.request(options); + req.end(); + }); + wss.on('connection', function(ws) { + ws.terminate(); + wss.close(); + done(); + }); + wss.on('error', function() {}); + }); + + it('handles messages passed along with the upgrade request (upgrade head)', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o) { + return true; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 13, + 'Origin': 'http://foobar.com' + } + }; + var req = http.request(options); + req.write(new Buffer([0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f], 'binary')); + req.end(); + }); + wss.on('connection', function(ws) { + ws.on('message', function(data) { + data.should.eql('Hello'); + ws.terminate(); + wss.close(); + done(); + }); + }); + wss.on('error', function() {}); + }); + + it('selects the first protocol by default', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'}); + ws.on('open', function(client) { + ws.protocol.should.eql('prot1'); + wss.close(); + done(); + }); + }); + }); + + it('selects the last protocol via protocol handler', function(done) { + var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) { + cb(true, ps[ps.length-1]); }}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'}); + ws.on('open', function(client) { + ws.protocol.should.eql('prot2'); + wss.close(); + done(); + }); + }); + }); + + it('client detects invalid server protocol', function(done) { + var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) { + cb(true, 'prot3'); }}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'}); + ws.on('open', function(client) { + done(new Error('connection must not be established')); + }); + ws.on('error', function() { + done(); + }); + }); + }); + + it('client detects no server protocol', function(done) { + var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) { + cb(true); }}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'}); + ws.on('open', function(client) { + done(new Error('connection must not be established')); + }); + ws.on('error', function() { + done(); + }); + }); + }); + + it('client refuses server protocols', function(done) { + var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) { + cb(false); }}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'}); + ws.on('open', function(client) { + done(new Error('connection must not be established')); + }); + ws.on('error', function() { + done(); + }); + }); + }); + + it('server detects invalid protocol handler', function(done) { + var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) { + // not calling callback is an error and shouldn't timeout + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 13, + 'Sec-WebSocket-Origin': 'http://foobar.com' + } + }; + options.port = port; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(501); + wss.close(); + done(); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + }); + + describe('messaging', function() { + it('can send and receive data', function(done) { + var data = new Array(65*1024); + for (var i = 0; i < data.length; ++i) { + data[i] = String.fromCharCode(65 + ~~(25 * Math.random())); + } + data = data.join(''); + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('message', function(message, flags) { + ws.send(message); + }); + }); + wss.on('connection', function(client) { + client.on('message', function(message) { + message.should.eql(data); + wss.close(); + done(); + }); + client.send(data); + }); + }); + }); + }); + + describe('hixie mode', function() { + it('can be disabled', function(done) { + var wss = new WebSocketServer({port: ++port, disableHixie: true}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(401); + process.nextTick(function() { + wss.close(); + done(); + }); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + describe('connection establishing', function() { + it('does not accept connections with no sec-websocket-key1', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80' + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(400); + wss.close(); + done(); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('does not accept connections with no sec-websocket-key2', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(400); + wss.close(); + done(); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('accepts connections with valid handshake', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + }); + wss.on('connection', function(ws) { + ws.terminate(); + wss.close(); + done(); + }); + wss.on('error', function() {}); + }); + + it('client can be denied', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o) { + return false; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(401); + process.nextTick(function() { + wss.close(); + done(); + }); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('client can be accepted', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o) { + return true; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + }); + wss.on('connection', function(ws) { + ws.terminate(); + wss.close(); + done(); + }); + wss.on('error', function() {}); + }); + + it('verifyClient gets client origin', function(done) { + var verifyClientCalled = false; + var wss = new WebSocketServer({port: ++port, verifyClient: function(info) { + info.origin.should.eql('http://foobarbaz.com'); + verifyClientCalled = true; + return false; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Origin': 'http://foobarbaz.com', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + req.on('response', function(res) { + verifyClientCalled.should.be.ok; + wss.close(); + done(); + }); + }); + wss.on('error', function() {}); + }); + + it('verifyClient gets original request', function(done) { + var verifyClientCalled = false; + var wss = new WebSocketServer({port: ++port, verifyClient: function(info) { + info.req.headers['sec-websocket-key1'].should.eql('3e6b263 4 17 80'); + verifyClientCalled = true; + return false; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Origin': 'http://foobarbaz.com', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + req.on('response', function(res) { + verifyClientCalled.should.be.ok; + wss.close(); + done(); + }); + }); + wss.on('error', function() {}); + }); + + it('client can be denied asynchronously', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) { + cb(false); + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Origin': 'http://foobarbaz.com', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(401); + process.nextTick(function() { + wss.close(); + done(); + }); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('client can be accepted asynchronously', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) { + cb(true); + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Origin': 'http://foobarbaz.com', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + }); + wss.on('connection', function(ws) { + wss.close(); + done(); + }); + wss.on('error', function() {}); + }); + + it('handles messages passed along with the upgrade request (upgrade head)', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o) { + return true; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90', + 'Origin': 'http://foobar.com' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.write(new Buffer([0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff], 'binary')); + req.end(); + }); + wss.on('connection', function(ws) { + ws.on('message', function(data) { + data.should.eql('Hello'); + ws.terminate(); + wss.close(); + done(); + }); + }); + wss.on('error', function() {}); + }); + }); + }); + + describe('client properties', function() { + it('protocol is exposed', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocol: 'hi'}); + }); + wss.on('connection', function(client) { + client.protocol.should.eql('hi'); + wss.close(); + done(); + }); + }); + + it('protocolVersion is exposed', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocolVersion: 8}); + }); + wss.on('connection', function(client) { + client.protocolVersion.should.eql(8); + wss.close(); + done(); + }); + }); + + it('upgradeReq is the original request object', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocolVersion: 8}); + }); + wss.on('connection', function(client) { + client.upgradeReq.httpVersion.should.eql('1.1'); + wss.close(); + done(); + }); + }); + }); + +}); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/autobahn-server.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/autobahn-server.js new file mode 100644 index 0000000..36fe0c2 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/autobahn-server.js @@ -0,0 +1,29 @@ +var WebSocketServer = require('../').Server; + +process.on('uncaughtException', function(err) { + console.log('Caught exception: ', err, err.stack); +}); + +process.on('SIGINT', function () { + try { + console.log('Updating reports and shutting down'); + var ws = new WebSocket('ws://localhost:9001/updateReports?agent=ws'); + ws.on('close', function() { + process.exit(); + }); + } + catch(e) { + process.exit(); + } +}); + +var wss = new WebSocketServer({port: 8181}); +wss.on('connection', function(ws) { + console.log('new connection'); + ws.on('message', function(data, flags) { + ws.send(flags.buffer, {binary: flags.binary === true}); + }); + ws.on('error', function() { + console.log('error', arguments); + }); +}); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/autobahn.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/autobahn.js new file mode 100644 index 0000000..048cc90 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/autobahn.js @@ -0,0 +1,52 @@ +var WebSocket = require('../'); +var currentTest = 1; +var lastTest = -1; +var testCount = null; + +process.on('uncaughtException', function(err) { + console.log('Caught exception: ', err, err.stack); +}); + +process.on('SIGINT', function () { + try { + console.log('Updating reports and shutting down'); + var ws = new WebSocket('ws://localhost:9001/updateReports?agent=ws'); + ws.on('close', function() { + process.exit(); + }); + } + catch(e) { + process.exit(); + } +}); + +function nextTest() { + if (currentTest > testCount || (lastTest != -1 && currentTest > lastTest)) { + console.log('Updating reports and shutting down'); + var ws = new WebSocket('ws://localhost:9001/updateReports?agent=ws'); + ws.on('close', function() { + process.exit(); + }); + return; + }; + console.log('Running test case ' + currentTest + '/' + testCount); + var ws = new WebSocket('ws://localhost:9001/runCase?case=' + currentTest + '&agent=ws'); + ws.on('message', function(data, flags) { + ws.send(flags.buffer, {binary: flags.binary === true, mask: true}); + }); + ws.on('close', function(data) { + currentTest += 1; + process.nextTick(nextTest); + }); + ws.on('error', function(e) {}); +} + +var ws = new WebSocket('ws://localhost:9001/getCaseCount'); +ws.on('message', function(data, flags) { + testCount = parseInt(data); +}); +ws.on('close', function() { + if (testCount > 0) { + nextTest(); + } +}); \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/agent1-cert.pem b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/agent1-cert.pem new file mode 100644 index 0000000..cccb9fb --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/agent1-cert.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICbjCCAdcCCQCVvok5oeLpqzANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJV +UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO +BgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMTEgMB4GCSqGSIb3DQEJARYRcnlA +dGlueWNsb3Vkcy5vcmcwHhcNMTMwMzA4MDAzMDIyWhcNNDAwNzIzMDAzMDIyWjB9 +MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQK +EwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MTEgMB4G +CSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwgZ8wDQYJKoZIhvcNAQEBBQAD +gY0AMIGJAoGBAL6GwKosYb0Yc3Qo0OtQVlCJ4208Idw11ij+t2W5sfYbCil5tyQo +jnhGM1CJhEXynQpXXwjKJuIeTQCkeUibTyFKa0bs8+li2FiGoKYbb4G81ovnqkmE +2iDVb8Gw3rrM4zeZ0ZdFnjMsAZac8h6+C4sB/pS9BiMOo6qTl15RQlcJAgMBAAEw +DQYJKoZIhvcNAQEFBQADgYEAOtmLo8DwTPnI4wfQbQ3hWlTS/9itww6IsxH2ODt9 +ggB7wi7N3uAdIWRZ54ke0NEAO5CW1xNTwsWcxQbiHrDOqX1vfVCjIenI76jVEEap +/Ay53ydHNBKdsKkib61Me14Mu0bA3lUul57VXwmH4NUEFB3w973Q60PschUhOEXj +7DY= +-----END CERTIFICATE----- diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/agent1-key.pem b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/agent1-key.pem new file mode 100644 index 0000000..cbd5f0c --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/agent1-key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQC+hsCqLGG9GHN0KNDrUFZQieNtPCHcNdYo/rdlubH2Gwopebck +KI54RjNQiYRF8p0KV18IyibiHk0ApHlIm08hSmtG7PPpYthYhqCmG2+BvNaL56pJ +hNog1W/BsN66zOM3mdGXRZ4zLAGWnPIevguLAf6UvQYjDqOqk5deUUJXCQIDAQAB +AoGANu/CBA+SCyVOvRK70u4yRTzNMAUjukxnuSBhH1rg/pajYnwvG6T6F6IeT72n +P0gKkh3JUE6B0bds+p9yPUZTFUXghxjcF33wlIY44H6gFE4K5WutsFJ9c450wtuu +8rXZTsIg7lAXWjTFVmdtOEPetcGlO2Hpi1O7ZzkzHgB2w9ECQQDksCCYx78or1zY +ZSokm8jmpIjG3VLKdvI9HAoJRN40ldnwFoigrFa1AHwsFtWNe8bKyVRPDoLDUjpB +dkPWgweVAkEA1UfgqguQ2KIkbtp9nDBionu3QaajksrRHwIa8vdfRfLxszfHk2fh +NGY3dkRZF8HUAbzYLrd9poVhCBAEjWekpQJASOM6AHfpnXYHCZF01SYx6hEW5wsz +kARJQODm8f1ZNTlttO/5q/xBxn7ZFNRSTD3fJlL05B2j380ddC/Vf1FT4QJAP1BC +GliqnBSuGhZUWYxni3KMeTm9rzL0F29pjpzutHYlWB2D6ndY/FQnvL0XcZ0Bka58 +womIDGnl3x3aLBwLXQJBAJv6h5CHbXHx7VyDJAcNfppAqZGcEaiVg8yf2F33iWy2 +FLthhJucx7df7SO2aw5h06bRDRAhb9br0R9/3mLr7RE= +-----END RSA PRIVATE KEY----- diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/ca1-cert.pem b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/ca1-cert.pem new file mode 100644 index 0000000..1d0c0d6 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/ca1-cert.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICazCCAdQCCQC9/g69HtxXRzANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJV +UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO +BgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMTEgMB4GCSqGSIb3DQEJARYRcnlA +dGlueWNsb3Vkcy5vcmcwHhcNMTMwMzA4MDAzMDIyWhcNNDAwNzIzMDAzMDIyWjB6 +MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQK +EwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMTEgMB4GCSqG +SIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0A +MIGJAoGBAKxr1mARUcv7zaqx5y4AxJPK6c1jdbSg7StcL4vg8klaPAlfNO6o+/Cl +w5CdQD3ukaVUwUOJ4T/+b3Xf7785XcWBC33GdjVQkfbHATJYcka7j7JDw3qev5Jk +1rAbRw48hF6rYlSGcx1mccAjoLoa3I8jgxCNAYHIjUQXgdmU893rAgMBAAEwDQYJ +KoZIhvcNAQEFBQADgYEAis05yxjCtJRuv8uX/DK6TX/j9C9Lzp1rKDNFTaTZ0iRw +KCw1EcNx4OXSj9gNblW4PWxpDvygrt1AmH9h2cb8K859NSHa9JOBFw6MA5C2A4Sj +NQfNATqUl4T6cdORlcDEZwHtT8b6D4A6Er31G/eJF4Sen0TUFpjdjd+l9RBjHlo= +-----END CERTIFICATE----- diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/ca1-key.pem b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/ca1-key.pem new file mode 100644 index 0000000..df14950 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/ca1-key.pem @@ -0,0 +1,17 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIFeWxJE1BrRECAggA +MBQGCCqGSIb3DQMHBAgu9PlMSQ+BOASCAoDEZN2tX0xWo/N+Jg+PrvCrFDk3P+3x +5xG/PEDjtMCAWPBEwbnaYHDzYmhNcAmxzGqEHGMDiWYs46LbO560VS3uMvFbEWPo +KYYVb13vkxl2poXdonCb5cHZA5GUYzTIVVJFptl4LHwBczHoMHtA4FqAhKlYvlWw +EOrdLB8XcwMmGPFabbbGxno0+EWWM27uNjlogfoxj35mQqSW4rOlhZ460XjOB1Zx +LjXMuZeONojkGYQRG5EUMchBoctQpCOM6cAi9r1B9BvtFCBpDV1c1zEZBzTEUd8o +kLn6tjLmY+QpTdylFjEWc7U3ppLY/pkoTBv4r85a2sEMWqkhSJboLaTboWzDJcU3 +Ke61pMpovt/3yCUd3TKgwduVwwQtDVTlBe0p66aN9QVj3CrFy/bKAGO3vxlli24H +aIjZf+OVoBY21ESlW3jLvNlBf7Ezf///2E7j4SCDLyZSFMTpFoAG/jDRyvi+wTKX +Kh485Bptnip6DCSuoH4u2SkOqwz3gJS/6s02YKe4m311QT4Pzne5/FwOFaS/HhQg +Xvyh2/d00OgJ0Y0PYQsHILPRgTUCKUXvj1O58opn3fxSacsPxIXwj6Z4FYAjUTaV +2B85k1lpant/JJEilDqMjqzx4pHZ/Z3Uto1lSM1JZs9SNL/0UR+6F0TXZTULVU9V +w8jYzz4sPr7LEyrrTbzmjQgnQFVbhAN/eKgRZK/SpLjxpmBV5MfpbPKsPUZqT4UC +4nXa8a/NYUQ9e+QKK8enq9E599c2W442W7Z1uFRZTWReMx/lF8wwA6G8zOPG0bdj +d+T5Gegzd5mvRiXMBklCo8RLxOOvgxun1n3PY4a63aH6mqBhdfhiLp5j +-----END ENCRYPTED PRIVATE KEY----- diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/certificate.pem b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/certificate.pem new file mode 100644 index 0000000..0efc2ef --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/certificate.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIICATCCAWoCCQDPufXH86n2QzANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJu +bzETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMB4XDTEyMDEwMTE0NDQwMFoXDTIwMDMxOTE0NDQwMFowRTELMAkG +A1UEBhMCbm8xEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 +IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtrQ7 ++r//2iV/B6F+4boH0XqFn7alcV9lpjvAmwRXNKnxAoa0f97AjYPGNLKrjpkNXXhB +JROIdbRbZnCNeC5fzX1a+JCo7KStzBXuGSZr27TtFmcV4H+9gIRIcNHtZmJLnxbJ +sIhkGR8yVYdmJZe4eT5ldk1zoB1adgPF1hZhCBMCAwEAATANBgkqhkiG9w0BAQUF +AAOBgQCeWBEHYJ4mCB5McwSSUox0T+/mJ4W48L/ZUE4LtRhHasU9hiW92xZkTa7E +QLcoJKQiWfiLX2ysAro0NX4+V8iqLziMqvswnPzz5nezaOLE/9U/QvH3l8qqNkXu +rNbsW1h/IO6FV8avWFYVFoutUwOaZ809k7iMh2F2JMgXQ5EymQ== +-----END CERTIFICATE----- diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/key.pem b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/key.pem new file mode 100644 index 0000000..176fe32 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQC2tDv6v//aJX8HoX7hugfReoWftqVxX2WmO8CbBFc0qfEChrR/ +3sCNg8Y0squOmQ1deEElE4h1tFtmcI14Ll/NfVr4kKjspK3MFe4ZJmvbtO0WZxXg +f72AhEhw0e1mYkufFsmwiGQZHzJVh2Yll7h5PmV2TXOgHVp2A8XWFmEIEwIDAQAB +AoGAAlVY8sHi/aE+9xT77twWX3mGHV0SzdjfDnly40fx6S1Gc7bOtVdd9DC7pk6l +3ENeJVR02IlgU8iC5lMHq4JEHPE272jtPrLlrpWLTGmHEqoVFv9AITPqUDLhB9Kk +Hjl7h8NYBKbr2JHKICr3DIPKOT+RnXVb1PD4EORbJ3ooYmkCQQDfknUnVxPgxUGs +ouABw1WJIOVgcCY/IFt4Ihf6VWTsxBgzTJKxn3HtgvE0oqTH7V480XoH0QxHhjLq +DrgobWU9AkEA0TRJ8/ouXGnFEPAXjWr9GdPQRZ1Use2MrFjneH2+Sxc0CmYtwwqL +Kr5kS6mqJrxprJeluSjBd+3/ElxURrEXjwJAUvmlN1OPEhXDmRHd92mKnlkyKEeX +OkiFCiIFKih1S5Y/sRJTQ0781nyJjtJqO7UyC3pnQu1oFEePL+UEniRztQJAMfav +AtnpYKDSM+1jcp7uu9BemYGtzKDTTAYfoiNF42EzSJiGrWJDQn4eLgPjY0T0aAf/ +yGz3Z9ErbhMm/Ysl+QJBAL4kBxRT8gM4ByJw4sdOvSeCCANFq8fhbgm8pGWlCPb5 +JGmX3/GHFM8x2tbWMGpyZP1DLtiNEFz7eCGktWK5rqE= +-----END RSA PRIVATE KEY----- diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/request.pem b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/request.pem new file mode 100644 index 0000000..51bc7f6 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/request.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBhDCB7gIBADBFMQswCQYDVQQGEwJubzETMBEGA1UECAwKU29tZS1TdGF0ZTEh +MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQC2tDv6v//aJX8HoX7hugfReoWftqVxX2WmO8CbBFc0qfEC +hrR/3sCNg8Y0squOmQ1deEElE4h1tFtmcI14Ll/NfVr4kKjspK3MFe4ZJmvbtO0W +ZxXgf72AhEhw0e1mYkufFsmwiGQZHzJVh2Yll7h5PmV2TXOgHVp2A8XWFmEIEwID +AQABoAAwDQYJKoZIhvcNAQEFBQADgYEAjsUXEARgfxZNkMjuUcudgU2w4JXS0gGI +JQ0U1LmU0vMDSKwqndMlvCbKzEgPbJnGJDI8D4MeINCJHa5Ceyb8c+jaJYUcCabl +lQW5Psn3+eWp8ncKlIycDRj1Qk615XuXtV0fhkrgQM2ZCm9LaQ1O1Gd/CzLihLjF +W0MmgMKMMRk= +-----END CERTIFICATE REQUEST----- diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/textfile b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/textfile new file mode 100644 index 0000000..a10483b --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/fixtures/textfile @@ -0,0 +1,9 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam egestas, massa at aliquam luctus, sapien erat viverra elit, nec pulvinar turpis eros sagittis urna. Pellentesque imperdiet tempor varius. Pellentesque blandit, ipsum in imperdiet venenatis, mi elit faucibus odio, id condimentum ante enim sed lectus. Aliquam et odio non odio pellentesque pulvinar. Vestibulum a erat dolor. Integer pretium risus sit amet nisl volutpat nec venenatis magna egestas. Ut bibendum felis eu tellus laoreet eleifend. Nam pulvinar auctor tortor, eu iaculis leo vestibulum quis. In euismod risus ac purus vehicula et fermentum ligula consectetur. Vivamus condimentum tempus lacinia. + +Curabitur sodales condimentum urna id dictum. Sed quis justo sit amet quam ultrices tincidunt vel laoreet nulla. Nullam quis ipsum sed nisi mollis bibendum at sit amet nisi. Donec laoreet consequat velit sit amet mollis. Nam sed sapien a massa iaculis dapibus. Sed dui nunc, ultricies et pellentesque ullamcorper, aliquet vitae ligula. Integer eu velit in neque iaculis venenatis. Ut rhoncus cursus est, ac dignissim leo vehicula a. Nulla ullamcorper vulputate mauris id blandit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque eleifend, nisi a tempor sollicitudin, odio massa pretium urna, quis congue sapien elit at tortor. Curabitur ipsum orci, vehicula non commodo molestie, laoreet id enim. Pellentesque convallis ultrices congue. Pellentesque nec iaculis lorem. In sagittis pharetra ipsum eget sodales. + +Fusce id nulla odio. Nunc nibh justo, placerat vel tincidunt sed, ornare et enim. Nulla vel urna vel ante commodo bibendum in vitae metus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis erat nunc, semper eget sagittis sit amet, ullamcorper eget lacus. Donec hendrerit ipsum vitae eros vestibulum eu gravida neque tincidunt. Ut molestie lacinia nulla. Donec mattis odio at magna egestas at pellentesque eros accumsan. Praesent interdum sem sit amet nibh commodo dignissim. Duis laoreet, enim ultricies fringilla suscipit, enim libero cursus nulla, sollicitudin adipiscing erat velit ut dui. Nulla eleifend mauris at velit fringilla a molestie lorem venenatis. + +Donec sit amet scelerisque metus. Cras ac felis a nulla venenatis vulputate. Duis porttitor eros ac neque rhoncus eget aliquet neque egestas. Quisque sed nunc est, vitae dapibus quam. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In vehicula, est vitae posuere ultricies, diam purus pretium sapien, nec rhoncus dolor nisl eget arcu. Aliquam et nisi vitae risus tincidunt auctor. In vehicula, erat a cursus adipiscing, lorem orci congue est, nec ultricies elit dui in nunc. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + +Duis congue tempus elit sit amet auctor. Duis dignissim, risus ut sollicitudin ultricies, dolor ligula gravida odio, nec congue orci purus ut ligula. Fusce pretium dictum lectus at volutpat. Sed non auctor mauris. Etiam placerat vestibulum massa id blandit. Quisque consequat lacus ut nulla euismod facilisis. Sed aliquet ipsum nec mi imperdiet viverra. Pellentesque ullamcorper, lectus nec varius gravida, odio justo cursus risus, eu sagittis metus arcu quis felis. Phasellus consectetur vehicula libero, at condimentum orci euismod vel. Nunc purus tortor, suscipit nec fringilla nec, vulputate et nibh. Nam porta vehicula neque. Praesent porttitor, sapien eu auctor euismod, arcu quam elementum urna, sed hendrerit magna augue sed quam. \ No newline at end of file diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/hybi-common.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/hybi-common.js new file mode 100644 index 0000000..006f9c6 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/hybi-common.js @@ -0,0 +1,99 @@ +/** + * Returns a Buffer from a "ff 00 ff"-type hex string. + */ + +getBufferFromHexString = function(byteStr) { + var bytes = byteStr.split(' '); + var buf = new Buffer(bytes.length); + for (var i = 0; i < bytes.length; ++i) { + buf[i] = parseInt(bytes[i], 16); + } + return buf; +} + +/** + * Returns a hex string from a Buffer. + */ + +getHexStringFromBuffer = function(data) { + var s = ''; + for (var i = 0; i < data.length; ++i) { + s += padl(data[i].toString(16), 2, '0') + ' '; + } + return s.trim(); +} + +/** + * Splits a buffer in two parts. + */ + +splitBuffer = function(buffer) { + var b1 = new Buffer(Math.ceil(buffer.length / 2)); + buffer.copy(b1, 0, 0, b1.length); + var b2 = new Buffer(Math.floor(buffer.length / 2)); + buffer.copy(b2, 0, b1.length, b1.length + b2.length); + return [b1, b2]; +} + +/** + * Performs hybi07+ type masking on a hex string or buffer. + */ + +mask = function(buf, maskString) { + if (typeof buf == 'string') buf = new Buffer(buf); + var mask = getBufferFromHexString(maskString || '34 83 a8 68'); + for (var i = 0; i < buf.length; ++i) { + buf[i] ^= mask[i % 4]; + } + return buf; +} + +/** + * Returns a hex string representing the length of a message + */ + +getHybiLengthAsHexString = function(len, masked) { + if (len < 126) { + var buf = new Buffer(1); + buf[0] = (masked ? 0x80 : 0) | len; + } + else if (len < 65536) { + var buf = new Buffer(3); + buf[0] = (masked ? 0x80 : 0) | 126; + getBufferFromHexString(pack(4, len)).copy(buf, 1); + } + else { + var buf = new Buffer(9); + buf[0] = (masked ? 0x80 : 0) | 127; + getBufferFromHexString(pack(16, len)).copy(buf, 1); + } + return getHexStringFromBuffer(buf); +} + +/** + * Unpacks a Buffer into a number. + */ + +unpack = function(buffer) { + var n = 0; + for (var i = 0; i < buffer.length; ++i) { + n = (i == 0) ? buffer[i] : (n * 256) + buffer[i]; + } + return n; +} + +/** + * Returns a hex string, representing a specific byte count 'length', from a number. + */ + +pack = function(length, number) { + return padl(number.toString(16), length, '0').replace(/([0-9a-f][0-9a-f])/gi, '$1 ').trim(); +} + +/** + * Left pads the string 's' to a total length of 'n' with char 'c'. + */ + +padl = function(s, n, c) { + return new Array(1 + n - s.length).join(c) + s; +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/testserver.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/testserver.js new file mode 100644 index 0000000..3e7a966 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/ws/test/testserver.js @@ -0,0 +1,180 @@ +var http = require('http') + , util = require('util') + , crypto = require('crypto') + , events = require('events') + , Sender = require('../lib/Sender') + , Receiver = require('../lib/Receiver'); + +module.exports = { + handlers: { + valid: validServer, + invalidKey: invalidRequestHandler, + closeAfterConnect: closeAfterConnectHandler, + return401: return401 + }, + createServer: function(port, handler, cb) { + if (handler && !cb) { + cb = handler; + handler = null; + } + var webServer = http.createServer(function (req, res) { + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end('okay'); + }); + var srv = new Server(webServer); + webServer.on('upgrade', function(req, socket) { + webServer._socket = socket; + (handler || validServer)(srv, req, socket); + }); + webServer.listen(port, '127.0.0.1', function() { cb(srv); }); + } +}; + +/** + * Test strategies + */ + +function validServer(server, req, socket) { + if (typeof req.headers.upgrade === 'undefined' || + req.headers.upgrade.toLowerCase() !== 'websocket') { + throw new Error('invalid headers'); + return; + } + + if (!req.headers['sec-websocket-key']) { + socket.end(); + throw new Error('websocket key is missing'); + } + + // calc key + var key = req.headers['sec-websocket-key']; + var shasum = crypto.createHash('sha1'); + shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); + key = shasum.digest('base64'); + + var headers = [ + 'HTTP/1.1 101 Switching Protocols' + , 'Upgrade: websocket' + , 'Connection: Upgrade' + , 'Sec-WebSocket-Accept: ' + key + ]; + + socket.write(headers.concat('', '').join('\r\n')); + socket.setTimeout(0); + socket.setNoDelay(true); + + var sender = new Sender(socket); + var receiver = new Receiver(); + receiver.ontext = function (message, flags) { + server.emit('message', message, flags); + sender.send(message); + }; + receiver.onbinary = function (message, flags) { + flags = flags || {}; + flags.binary = true; + server.emit('message', message, flags); + sender.send(message, {binary: true}); + }; + receiver.onping = function (message, flags) { + flags = flags || {}; + server.emit('ping', message, flags); + }; + receiver.onpong = function (message, flags) { + flags = flags || {}; + server.emit('pong', message, flags); + }; + receiver.onclose = function (code, message, flags) { + flags = flags || {}; + server.emit('close', code, message, flags); + }; + socket.on('data', function (data) { + receiver.add(data); + }); + socket.on('end', function() { + socket.end(); + }); +} + +function invalidRequestHandler(server, req, socket) { + if (typeof req.headers.upgrade === 'undefined' || + req.headers.upgrade.toLowerCase() !== 'websocket') { + throw new Error('invalid headers'); + return; + } + + if (!req.headers['sec-websocket-key']) { + socket.end(); + throw new Error('websocket key is missing'); + } + + // calc key + var key = req.headers['sec-websocket-key']; + var shasum = crypto.createHash('sha1'); + shasum.update(key + "bogus"); + key = shasum.digest('base64'); + + var headers = [ + 'HTTP/1.1 101 Switching Protocols' + , 'Upgrade: websocket' + , 'Connection: Upgrade' + , 'Sec-WebSocket-Accept: ' + key + ]; + + socket.write(headers.concat('', '').join('\r\n')); + socket.end(); +} + +function closeAfterConnectHandler(server, req, socket) { + if (typeof req.headers.upgrade === 'undefined' || + req.headers.upgrade.toLowerCase() !== 'websocket') { + throw new Error('invalid headers'); + return; + } + + if (!req.headers['sec-websocket-key']) { + socket.end(); + throw new Error('websocket key is missing'); + } + + // calc key + var key = req.headers['sec-websocket-key']; + var shasum = crypto.createHash('sha1'); + shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); + key = shasum.digest('base64'); + + var headers = [ + 'HTTP/1.1 101 Switching Protocols' + , 'Upgrade: websocket' + , 'Connection: Upgrade' + , 'Sec-WebSocket-Accept: ' + key + ]; + + socket.write(headers.concat('', '').join('\r\n')); + socket.end(); +} + + +function return401(server, req, socket) { + var headers = [ + 'HTTP/1.1 401 Unauthorized' + , 'Content-type: text/html' + ]; + + socket.write(headers.concat('', '').join('\r\n')); + socket.end(); +} + +/** + * Server object, which will do the actual emitting + */ + +function Server(webServer) { + this.webServer = webServer; +} + +util.inherits(Server, events.EventEmitter); + +Server.prototype.close = function() { + this.webServer.close(); + if (this._socket) this._socket.end(); +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/README.md b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/README.md new file mode 100644 index 0000000..22aab8b --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/README.md @@ -0,0 +1,53 @@ +# node-XMLHttpRequest # + +node-XMLHttpRequest is a wrapper for the built-in http client to emulate the +browser XMLHttpRequest object. + +This can be used with JS designed for browsers to improve reuse of code and +allow the use of existing libraries. + +Note: This library currently conforms to [XMLHttpRequest 1](http://www.w3.org/TR/XMLHttpRequest/). Version 2.0 will target [XMLHttpRequest Level 2](http://www.w3.org/TR/XMLHttpRequest2/). + +## Usage ## + +Here's how to include the module in your project and use as the browser-based +XHR object. + + var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest; + var xhr = new XMLHttpRequest(); + +Note: use the lowercase string "xmlhttprequest" in your require(). On +case-sensitive systems (eg Linux) using uppercase letters won't work. + +## Versions ## + +Prior to 1.4.0 version numbers were arbitrary. From 1.4.0 on they conform to +the standard major.minor.bugfix. 1.x shouldn't necessarily be considered +stable just because it's above 0.x. + +Since the XMLHttpRequest API is stable this library's API is stable as +well. Major version numbers indicate significant core code changes. +Minor versions indicate minor core code changes or better conformity to +the W3C spec. + +## Supports ## + +* Async and synchronous requests +* GET, POST, PUT, and DELETE requests +* All spec methods (open, send, abort, getRequestHeader, + getAllRequestHeaders, event methods) +* Requests to all domains + +## Known Issues / Missing Features ## + +For a list of open issues or to report your own visit the [github issues +page](https://github.com/driverdan/node-XMLHttpRequest/issues). + +* Local file access may have unexpected results for non-UTF8 files +* Synchronous requests don't set headers properly +* Synchronous requests freeze node while waiting for response (But that's what you want, right? Stick with async!). +* Some events are missing, such as abort +* getRequestHeader is case-sensitive +* Cookies aren't persisted between requests +* Missing XML support +* Missing basic auth diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/autotest.watchr b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/autotest.watchr new file mode 100644 index 0000000..5324db6 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/autotest.watchr @@ -0,0 +1,8 @@ +def run_all_tests + puts `clear` + puts `node tests/test-constants.js` + puts `node tests/test-headers.js` + puts `node tests/test-request.js` +end +watch('.*.js') { run_all_tests } +run_all_tests diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/example/demo.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/example/demo.js new file mode 100644 index 0000000..4f333de --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/example/demo.js @@ -0,0 +1,16 @@ +var sys = require('util'); +var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest; + +var xhr = new XMLHttpRequest(); + +xhr.onreadystatechange = function() { + sys.puts("State: " + this.readyState); + + if (this.readyState == 4) { + sys.puts("Complete.\nBody length: " + this.responseText.length); + sys.puts("Body:\n" + this.responseText); + } +}; + +xhr.open("GET", "http://driverdan.com"); +xhr.send(); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/lib/XMLHttpRequest.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/lib/XMLHttpRequest.js new file mode 100644 index 0000000..214a2e3 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/lib/XMLHttpRequest.js @@ -0,0 +1,548 @@ +/** + * Wrapper for built-in http.js to emulate the browser XMLHttpRequest object. + * + * This can be used with JS designed for browsers to improve reuse of code and + * allow the use of existing libraries. + * + * Usage: include("XMLHttpRequest.js") and use XMLHttpRequest per W3C specs. + * + * @author Dan DeFelippi + * @contributor David Ellis + * @license MIT + */ + +var Url = require("url") + , spawn = require("child_process").spawn + , fs = require('fs'); + +exports.XMLHttpRequest = function() { + /** + * Private variables + */ + var self = this; + var http = require('http'); + var https = require('https'); + + // Holds http.js objects + var client; + var request; + var response; + + // Request settings + var settings = {}; + + // Set some default headers + var defaultHeaders = { + "User-Agent": "node-XMLHttpRequest", + "Accept": "*/*", + }; + + var headers = defaultHeaders; + + // These headers are not user setable. + // The following are allowed but banned in the spec: + // * user-agent + var forbiddenRequestHeaders = [ + "accept-charset", + "accept-encoding", + "access-control-request-headers", + "access-control-request-method", + "connection", + "content-length", + "content-transfer-encoding", + "cookie", + "cookie2", + "date", + "expect", + "host", + "keep-alive", + "origin", + "referer", + "te", + "trailer", + "transfer-encoding", + "upgrade", + "via" + ]; + + // These request methods are not allowed + var forbiddenRequestMethods = [ + "TRACE", + "TRACK", + "CONNECT" + ]; + + // Send flag + var sendFlag = false; + // Error flag, used when errors occur or abort is called + var errorFlag = false; + + // Event listeners + var listeners = {}; + + /** + * Constants + */ + + this.UNSENT = 0; + this.OPENED = 1; + this.HEADERS_RECEIVED = 2; + this.LOADING = 3; + this.DONE = 4; + + /** + * Public vars + */ + + // Current state + this.readyState = this.UNSENT; + + // default ready state change handler in case one is not set or is set late + this.onreadystatechange = null; + + // Result & response + this.responseText = ""; + this.responseXML = ""; + this.status = null; + this.statusText = null; + + /** + * Private methods + */ + + /** + * Check if the specified header is allowed. + * + * @param string header Header to validate + * @return boolean False if not allowed, otherwise true + */ + var isAllowedHttpHeader = function(header) { + return (header && forbiddenRequestHeaders.indexOf(header.toLowerCase()) === -1); + }; + + /** + * Check if the specified method is allowed. + * + * @param string method Request method to validate + * @return boolean False if not allowed, otherwise true + */ + var isAllowedHttpMethod = function(method) { + return (method && forbiddenRequestMethods.indexOf(method) === -1); + }; + + /** + * Public methods + */ + + /** + * Open the connection. Currently supports local server requests. + * + * @param string method Connection method (eg GET, POST) + * @param string url URL for the connection. + * @param boolean async Asynchronous connection. Default is true. + * @param string user Username for basic authentication (optional) + * @param string password Password for basic authentication (optional) + */ + this.open = function(method, url, async, user, password) { + this.abort(); + errorFlag = false; + + // Check for valid request method + if (!isAllowedHttpMethod(method)) { + throw "SecurityError: Request method not allowed"; + return; + } + + settings = { + "method": method, + "url": url.toString(), + "async": (typeof async !== "boolean" ? true : async), + "user": user || null, + "password": password || null + }; + + setState(this.OPENED); + }; + + /** + * Sets a header for the request. + * + * @param string header Header name + * @param string value Header value + */ + this.setRequestHeader = function(header, value) { + if (this.readyState != this.OPENED) { + throw "INVALID_STATE_ERR: setRequestHeader can only be called when state is OPEN"; + } + if (!isAllowedHttpHeader(header)) { + console.warn('Refused to set unsafe header "' + header + '"'); + return; + } + if (sendFlag) { + throw "INVALID_STATE_ERR: send flag is true"; + } + headers[header] = value; + }; + + /** + * Gets a header from the server response. + * + * @param string header Name of header to get. + * @return string Text of the header or null if it doesn't exist. + */ + this.getResponseHeader = function(header) { + if (typeof header === "string" + && this.readyState > this.OPENED + && response.headers[header.toLowerCase()] + && !errorFlag + ) { + return response.headers[header.toLowerCase()]; + } + + return null; + }; + + /** + * Gets all the response headers. + * + * @return string A string with all response headers separated by CR+LF + */ + this.getAllResponseHeaders = function() { + if (this.readyState < this.HEADERS_RECEIVED || errorFlag) { + return ""; + } + var result = ""; + + for (var i in response.headers) { + // Cookie headers are excluded + if (i !== "set-cookie" && i !== "set-cookie2") { + result += i + ": " + response.headers[i] + "\r\n"; + } + } + return result.substr(0, result.length - 2); + }; + + /** + * Gets a request header + * + * @param string name Name of header to get + * @return string Returns the request header or empty string if not set + */ + this.getRequestHeader = function(name) { + // @TODO Make this case insensitive + if (typeof name === "string" && headers[name]) { + return headers[name]; + } + + return ""; + } + + /** + * Sends the request to the server. + * + * @param string data Optional data to send as request body. + */ + this.send = function(data) { + if (this.readyState != this.OPENED) { + throw "INVALID_STATE_ERR: connection must be opened before send() is called"; + } + + if (sendFlag) { + throw "INVALID_STATE_ERR: send has already been called"; + } + + var ssl = false, local = false; + var url = Url.parse(settings.url); + + // Determine the server + switch (url.protocol) { + case 'https:': + ssl = true; + // SSL & non-SSL both need host, no break here. + case 'http:': + var host = url.hostname; + break; + + case 'file:': + local = true; + break; + + case undefined: + case '': + var host = "localhost"; + break; + + default: + throw "Protocol not supported."; + } + + // Load files off the local filesystem (file://) + if (local) { + if (settings.method !== "GET") { + throw "XMLHttpRequest: Only GET method is supported"; + } + + if (settings.async) { + fs.readFile(url.pathname, 'utf8', function(error, data) { + if (error) { + self.handleError(error); + } else { + self.status = 200; + self.responseText = data; + setState(self.DONE); + } + }); + } else { + try { + this.responseText = fs.readFileSync(url.pathname, 'utf8'); + this.status = 200; + setState(self.DONE); + } catch(e) { + this.handleError(e); + } + } + + return; + } + + // Default to port 80. If accessing localhost on another port be sure + // to use http://localhost:port/path + var port = url.port || (ssl ? 443 : 80); + // Add query string if one is used + var uri = url.pathname + (url.search ? url.search : ''); + + // Set the Host header or the server may reject the request + headers["Host"] = host; + if (!((ssl && port === 443) || port === 80)) { + headers["Host"] += ':' + url.port; + } + + // Set Basic Auth if necessary + if (settings.user) { + if (typeof settings.password == "undefined") { + settings.password = ""; + } + var authBuf = new Buffer(settings.user + ":" + settings.password); + headers["Authorization"] = "Basic " + authBuf.toString("base64"); + } + + // Set content length header + if (settings.method === "GET" || settings.method === "HEAD") { + data = null; + } else if (data) { + headers["Content-Length"] = Buffer.byteLength(data); + + if (!headers["Content-Type"]) { + headers["Content-Type"] = "text/plain;charset=UTF-8"; + } + } else if (settings.method === "POST") { + // For a post with no data set Content-Length: 0. + // This is required by buggy servers that don't meet the specs. + headers["Content-Length"] = 0; + } + + var options = { + host: host, + port: port, + path: uri, + method: settings.method, + headers: headers + }; + + // Reset error flag + errorFlag = false; + + // Handle async requests + if (settings.async) { + // Use the proper protocol + var doRequest = ssl ? https.request : http.request; + + // Request is being sent, set send flag + sendFlag = true; + + // As per spec, this is called here for historical reasons. + self.dispatchEvent("readystatechange"); + + // Create the request + request = doRequest(options, function(resp) { + response = resp; + response.setEncoding("utf8"); + + setState(self.HEADERS_RECEIVED); + self.status = response.statusCode; + + response.on('data', function(chunk) { + // Make sure there's some data + if (chunk) { + self.responseText += chunk; + } + // Don't emit state changes if the connection has been aborted. + if (sendFlag) { + setState(self.LOADING); + } + }); + + response.on('end', function() { + if (sendFlag) { + // Discard the 'end' event if the connection has been aborted + setState(self.DONE); + sendFlag = false; + } + }); + + response.on('error', function(error) { + self.handleError(error); + }); + }).on('error', function(error) { + self.handleError(error); + }); + + // Node 0.4 and later won't accept empty data. Make sure it's needed. + if (data) { + request.write(data); + } + + request.end(); + + self.dispatchEvent("loadstart"); + } else { // Synchronous + // Create a temporary file for communication with the other Node process + var syncFile = ".node-xmlhttprequest-sync-" + process.pid; + fs.writeFileSync(syncFile, "", "utf8"); + // The async request the other Node process executes + var execString = "var http = require('http'), https = require('https'), fs = require('fs');" + + "var doRequest = http" + (ssl ? "s" : "") + ".request;" + + "var options = " + JSON.stringify(options) + ";" + + "var responseText = '';" + + "var req = doRequest(options, function(response) {" + + "response.setEncoding('utf8');" + + "response.on('data', function(chunk) {" + + "responseText += chunk;" + + "});" + + "response.on('end', function() {" + + "fs.writeFileSync('" + syncFile + "', 'NODE-XMLHTTPREQUEST-STATUS:' + response.statusCode + ',' + responseText, 'utf8');" + + "});" + + "response.on('error', function(error) {" + + "fs.writeFileSync('" + syncFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');" + + "});" + + "}).on('error', function(error) {" + + "fs.writeFileSync('" + syncFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');" + + "});" + + (data ? "req.write('" + data.replace(/'/g, "\\'") + "');":"") + + "req.end();"; + // Start the other Node Process, executing this string + syncProc = spawn(process.argv[0], ["-e", execString]); + while((self.responseText = fs.readFileSync(syncFile, 'utf8')) == "") { + // Wait while the file is empty + } + // Kill the child process once the file has data + syncProc.stdin.end(); + // Remove the temporary file + fs.unlinkSync(syncFile); + if (self.responseText.match(/^NODE-XMLHTTPREQUEST-ERROR:/)) { + // If the file returned an error, handle it + var errorObj = self.responseText.replace(/^NODE-XMLHTTPREQUEST-ERROR:/, ""); + self.handleError(errorObj); + } else { + // If the file returned okay, parse its data and move to the DONE state + self.status = self.responseText.replace(/^NODE-XMLHTTPREQUEST-STATUS:([0-9]*),.*/, "$1"); + self.responseText = self.responseText.replace(/^NODE-XMLHTTPREQUEST-STATUS:[0-9]*,(.*)/, "$1"); + setState(self.DONE); + } + } + }; + + /** + * Called when an error is encountered to deal with it. + */ + this.handleError = function(error) { + this.status = 503; + this.statusText = error; + this.responseText = error.stack; + errorFlag = true; + setState(this.DONE); + }; + + /** + * Aborts a request. + */ + this.abort = function() { + if (request) { + request.abort(); + request = null; + } + + headers = defaultHeaders; + this.responseText = ""; + this.responseXML = ""; + + errorFlag = true; + + if (this.readyState !== this.UNSENT + && (this.readyState !== this.OPENED || sendFlag) + && this.readyState !== this.DONE) { + sendFlag = false; + setState(this.DONE); + } + this.readyState = this.UNSENT; + }; + + /** + * Adds an event listener. Preferred method of binding to events. + */ + this.addEventListener = function(event, callback) { + if (!(event in listeners)) { + listeners[event] = []; + } + // Currently allows duplicate callbacks. Should it? + listeners[event].push(callback); + }; + + /** + * Remove an event callback that has already been bound. + * Only works on the matching funciton, cannot be a copy. + */ + this.removeEventListener = function(event, callback) { + if (event in listeners) { + // Filter will return a new array with the callback removed + listeners[event] = listeners[event].filter(function(ev) { + return ev !== callback; + }); + } + }; + + /** + * Dispatch any events, including both "on" methods and events attached using addEventListener. + */ + this.dispatchEvent = function(event) { + if (typeof self["on" + event] === "function") { + self["on" + event](); + } + if (event in listeners) { + for (var i = 0, len = listeners[event].length; i < len; i++) { + listeners[event][i].call(self); + } + } + }; + + /** + * Changes readyState and calls onreadystatechange. + * + * @param int state New state + */ + var setState = function(state) { + if (self.readyState !== state) { + self.readyState = state; + + if (settings.async || self.readyState < self.OPENED || self.readyState === self.DONE) { + self.dispatchEvent("readystatechange"); + } + + if (self.readyState === self.DONE && !errorFlag) { + self.dispatchEvent("load"); + // @TODO figure out InspectorInstrumentation::didLoadXHR(cookie) + self.dispatchEvent("loadend"); + } + } + }; +}; diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/package.json b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/package.json new file mode 100644 index 0000000..bcbee67 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/package.json @@ -0,0 +1,42 @@ +{ + "name": "xmlhttprequest", + "description": "XMLHttpRequest for Node", + "version": "1.4.2", + "author": { + "name": "Dan DeFelippi", + "url": "http://driverdan.com" + }, + "keywords": [ + "xhr", + "ajax" + ], + "licenses": [ + { + "type": "MIT", + "url": "http://creativecommons.org/licenses/MIT/" + } + ], + "repository": { + "type": "git", + "url": "git://github.com/driverdan/node-XMLHttpRequest.git" + }, + "bugs": { + "url": "http://github.com/driverdan/node-XMLHttpRequest/issues" + }, + "engines": { + "node": ">=0.4.0" + }, + "directories": { + "lib": "./lib", + "example": "./example" + }, + "main": "./lib/XMLHttpRequest.js", + "readme": "# node-XMLHttpRequest #\n\nnode-XMLHttpRequest is a wrapper for the built-in http client to emulate the\nbrowser XMLHttpRequest object.\n\nThis can be used with JS designed for browsers to improve reuse of code and\nallow the use of existing libraries.\n\nNote: This library currently conforms to [XMLHttpRequest 1](http://www.w3.org/TR/XMLHttpRequest/). Version 2.0 will target [XMLHttpRequest Level 2](http://www.w3.org/TR/XMLHttpRequest2/).\n\n## Usage ##\n\nHere's how to include the module in your project and use as the browser-based\nXHR object.\n\n\tvar XMLHttpRequest = require(\"xmlhttprequest\").XMLHttpRequest;\n\tvar xhr = new XMLHttpRequest();\n\nNote: use the lowercase string \"xmlhttprequest\" in your require(). On\ncase-sensitive systems (eg Linux) using uppercase letters won't work.\n\n## Versions ##\n\nPrior to 1.4.0 version numbers were arbitrary. From 1.4.0 on they conform to\nthe standard major.minor.bugfix. 1.x shouldn't necessarily be considered\nstable just because it's above 0.x.\n\nSince the XMLHttpRequest API is stable this library's API is stable as\nwell. Major version numbers indicate significant core code changes.\nMinor versions indicate minor core code changes or better conformity to\nthe W3C spec.\n\n## Supports ##\n\n* Async and synchronous requests\n* GET, POST, PUT, and DELETE requests\n* All spec methods (open, send, abort, getRequestHeader,\n getAllRequestHeaders, event methods)\n* Requests to all domains\n\n## Known Issues / Missing Features ##\n\nFor a list of open issues or to report your own visit the [github issues\npage](https://github.com/driverdan/node-XMLHttpRequest/issues).\n\n* Local file access may have unexpected results for non-UTF8 files\n* Synchronous requests don't set headers properly\n* Synchronous requests freeze node while waiting for response (But that's what you want, right? Stick with async!).\n* Some events are missing, such as abort\n* getRequestHeader is case-sensitive\n* Cookies aren't persisted between requests\n* Missing XML support\n* Missing basic auth\n", + "readmeFilename": "README.md", + "_id": "xmlhttprequest@1.4.2", + "dist": { + "shasum": "01453a1d9bed1e8f172f6495bbf4c8c426321500" + }, + "_from": "xmlhttprequest@1.4.2", + "_resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.4.2.tgz" +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-constants.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-constants.js new file mode 100644 index 0000000..372e46c --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-constants.js @@ -0,0 +1,13 @@ +var sys = require("util") + , assert = require("assert") + , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest + , xhr = new XMLHttpRequest(); + +// Test constant values +assert.equal(0, xhr.UNSENT); +assert.equal(1, xhr.OPENED); +assert.equal(2, xhr.HEADERS_RECEIVED); +assert.equal(3, xhr.LOADING); +assert.equal(4, xhr.DONE); + +sys.puts("done"); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-events.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-events.js new file mode 100644 index 0000000..c72f001 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-events.js @@ -0,0 +1,50 @@ +var sys = require("util") + , assert = require("assert") + , http = require("http") + , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest + , xhr; + +// Test server +var server = http.createServer(function (req, res) { + var body = (req.method != "HEAD" ? "Hello World" : ""); + + res.writeHead(200, { + "Content-Type": "text/plain", + "Content-Length": Buffer.byteLength(body) + }); + // HEAD has no body + if (req.method != "HEAD") { + res.write(body); + } + res.end(); + assert.equal(onreadystatechange, true); + assert.equal(readystatechange, true); + assert.equal(removed, true); + sys.puts("done"); + this.close(); +}).listen(8000); + +xhr = new XMLHttpRequest(); + +// Track event calls +var onreadystatechange = false; +var readystatechange = false; +var removed = true; +var removedEvent = function() { + removed = false; +}; + +xhr.onreadystatechange = function() { + onreadystatechange = true; +}; + +xhr.addEventListener("readystatechange", function() { + readystatechange = true; +}); + +// This isn't perfect, won't guarantee it was added in the first place +xhr.addEventListener("readystatechange", removedEvent); +xhr.removeEventListener("readystatechange", removedEvent); + +xhr.open("GET", "http://localhost:8000"); +xhr.send(); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-exceptions.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-exceptions.js new file mode 100644 index 0000000..f1edd71 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-exceptions.js @@ -0,0 +1,62 @@ +var sys = require("util") + , assert = require("assert") + , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest + , xhr = new XMLHttpRequest(); + +// Test request methods that aren't allowed +try { + xhr.open("TRACK", "http://localhost:8000/"); + console.log("ERROR: TRACK should have thrown exception"); +} catch(e) {} +try { + xhr.open("TRACE", "http://localhost:8000/"); + console.log("ERROR: TRACE should have thrown exception"); +} catch(e) {} +try { + xhr.open("CONNECT", "http://localhost:8000/"); + console.log("ERROR: CONNECT should have thrown exception"); +} catch(e) {} +// Test valid request method +try { + xhr.open("GET", "http://localhost:8000/"); +} catch(e) { + console.log("ERROR: Invalid exception for GET", e); +} + +// Test forbidden headers +var forbiddenRequestHeaders = [ + "accept-charset", + "accept-encoding", + "access-control-request-headers", + "access-control-request-method", + "connection", + "content-length", + "content-transfer-encoding", + "cookie", + "cookie2", + "date", + "expect", + "host", + "keep-alive", + "origin", + "referer", + "te", + "trailer", + "transfer-encoding", + "upgrade", + "user-agent", + "via" +]; + +for (var i in forbiddenRequestHeaders) { + try { + xhr.setRequestHeader(forbiddenRequestHeaders[i], "Test"); + console.log("ERROR: " + forbiddenRequestHeaders[i] + " should have thrown exception"); + } catch(e) { + } +} + +// Try valid header +xhr.setRequestHeader("X-Foobar", "Test"); + +console.log("Done"); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-headers.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-headers.js new file mode 100644 index 0000000..2ecb045 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-headers.js @@ -0,0 +1,61 @@ +var sys = require("util") + , assert = require("assert") + , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest + , xhr = new XMLHttpRequest() + , http = require("http"); + +// Test server +var server = http.createServer(function (req, res) { + // Test setRequestHeader + assert.equal("Foobar", req.headers["x-test"]); + + var body = "Hello World"; + res.writeHead(200, { + "Content-Type": "text/plain", + "Content-Length": Buffer.byteLength(body), + // Set cookie headers to see if they're correctly suppressed + // Actual values don't matter + "Set-Cookie": "foo=bar", + "Set-Cookie2": "bar=baz", + "Connection": "close" + }); + res.write("Hello World"); + res.end(); + + this.close(); +}).listen(8000); + +xhr.onreadystatechange = function() { + if (this.readyState == 4) { + // Test getAllResponseHeaders() + var headers = "content-type: text/plain\r\ncontent-length: 11\r\nconnection: close"; + assert.equal(headers, this.getAllResponseHeaders()); + + // Test case insensitivity + assert.equal('text/plain', this.getResponseHeader('Content-Type')); + assert.equal('text/plain', this.getResponseHeader('Content-type')); + assert.equal('text/plain', this.getResponseHeader('content-Type')); + assert.equal('text/plain', this.getResponseHeader('content-type')); + + // Test aborted getAllResponseHeaders + this.abort(); + assert.equal("", this.getAllResponseHeaders()); + assert.equal(null, this.getResponseHeader("Connection")); + + sys.puts("done"); + } +}; + +assert.equal(null, xhr.getResponseHeader("Content-Type")); +try { + xhr.open("GET", "http://localhost:8000/"); + // Valid header + xhr.setRequestHeader("X-Test", "Foobar"); + // Invalid header + xhr.setRequestHeader("Content-Length", 0); + // Test getRequestHeader + assert.equal("Foobar", xhr.getRequestHeader("X-Test")); + xhr.send(); +} catch(e) { + console.log("ERROR: Exception raised", e); +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-request-methods.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-request-methods.js new file mode 100644 index 0000000..fa1b1be --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-request-methods.js @@ -0,0 +1,62 @@ +var sys = require("util") + , assert = require("assert") + , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest + , http = require("http") + , xhr; + +// Test server +var server = http.createServer(function (req, res) { + // Check request method and URL + assert.equal(methods[curMethod], req.method); + assert.equal("/" + methods[curMethod], req.url); + + var body = (req.method != "HEAD" ? "Hello World" : ""); + + res.writeHead(200, { + "Content-Type": "text/plain", + "Content-Length": Buffer.byteLength(body) + }); + // HEAD has no body + if (req.method != "HEAD") { + res.write(body); + } + res.end(); + + if (curMethod == methods.length - 1) { + this.close(); + sys.puts("done"); + } +}).listen(8000); + +// Test standard methods +var methods = ["GET", "POST", "HEAD", "PUT", "DELETE"]; +var curMethod = 0; + +function start(method) { + // Reset each time + xhr = new XMLHttpRequest(); + + xhr.onreadystatechange = function() { + if (this.readyState == 4) { + if (method == "HEAD") { + assert.equal("", this.responseText); + } else { + assert.equal("Hello World", this.responseText); + } + + curMethod++; + + if (curMethod < methods.length) { + sys.puts("Testing " + methods[curMethod]); + start(methods[curMethod]); + } + } + }; + + var url = "http://localhost:8000/" + method; + xhr.open(method, url); + xhr.send(); +} + +sys.puts("Testing " + methods[curMethod]); +start(methods[curMethod]); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-request-protocols.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-request-protocols.js new file mode 100644 index 0000000..cd4e174 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/test-request-protocols.js @@ -0,0 +1,34 @@ +var sys = require("util") + , assert = require("assert") + , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest + , xhr; + +xhr = new XMLHttpRequest(); + +xhr.onreadystatechange = function() { + if (this.readyState == 4) { + assert.equal("Hello World", this.responseText); + this.close(); + runSync(); + } +}; + +// Async +var url = "file://" + __dirname + "/testdata.txt"; +xhr.open("GET", url); +xhr.send(); + +// Sync +var runSync = function() { + xhr = new XMLHttpRequest(); + + xhr.onreadystatechange = function() { + if (this.readyState == 4) { + assert.equal("Hello World", this.responseText); + this.close(); + sys.puts("done"); + } + }; + xhr.open("GET", url, false); + xhr.send(); +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/testdata.txt b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/testdata.txt new file mode 100644 index 0000000..557db03 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/node_modules/xmlhttprequest/tests/testdata.txt @@ -0,0 +1 @@ +Hello World diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/package.json b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/package.json new file mode 100644 index 0000000..5f2db26 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/package.json @@ -0,0 +1,71 @@ +{ + "name": "socket.io-client", + "description": "Socket.IO client for the browser and node.js", + "version": "0.9.16", + "main": "./lib/io.js", + "browserify": "./dist/socket.io.js", + "homepage": "http://socket.io", + "keywords": [ + "websocket", + "socket", + "realtime", + "socket.io", + "comet", + "ajax" + ], + "author": { + "name": "Guillermo Rauch", + "email": "guillermo@learnboost.com" + }, + "contributors": [ + { + "name": "Guillermo Rauch", + "email": "rauchg@gmail.com" + }, + { + "name": "Arnout Kazemier", + "email": "info@3rd-eden.com" + }, + { + "name": "Vladimir Dronnikov", + "email": "dronnikov@gmail.com" + }, + { + "name": "Einar Otto Stangvik", + "email": "einaros@gmail.com" + } + ], + "repository": { + "type": "git", + "url": "https://github.com/LearnBoost/socket.io-client.git" + }, + "dependencies": { + "uglify-js": "1.2.5", + "ws": "0.4.x", + "xmlhttprequest": "1.4.2", + "active-x-obfuscator": "0.0.1" + }, + "devDependencies": { + "expresso": "*", + "express": "2.5.x", + "jade": "*", + "stylus": "*", + "socket.io": "0.9.16", + "socket.io-client": "0.9.16", + "should": "*" + }, + "engines": { + "node": ">= 0.4.0" + }, + "readme": "socket.io\n=========\n\n#### Sockets for the rest of us\n\nThe `socket.io` client is basically a simple HTTP Socket interface implementation.\nIt looks similar to WebSocket while providing additional features and\nleveraging other transports when WebSocket is not supported by the user's\nbrowser.\n\n```js\nvar socket = io.connect('http://domain.com');\nsocket.on('connect', function () {\n // socket connected\n});\nsocket.on('custom event', function () {\n // server emitted a custom event\n});\nsocket.on('disconnect', function () {\n // socket disconnected\n});\nsocket.send('hi there');\n```\n\n### Recipes\n\n#### Utilizing namespaces (ie: multiple sockets)\n\nIf you want to namespace all the messages and events emitted to a particular\nendpoint, simply specify it as part of the `connect` uri:\n\n```js\nvar chat = io.connect('http://localhost/chat');\nchat.on('connect', function () {\n // chat socket connected\n});\n\nvar news = io.connect('/news'); // io.connect auto-detects host\nnews.on('connect', function () {\n // news socket connected\n});\n```\n\n#### Emitting custom events\n\nTo ease with the creation of applications, you can emit custom events outside\nof the global `message` event.\n\n```js\nvar socket = io.connect();\nsocket.emit('server custom event', { my: 'data' });\n```\n\n#### Forcing disconnection\n\n```js\nvar socket = io.connect();\nsocket.on('connect', function () {\n socket.disconnect();\n});\n```\n\n### Documentation \n\n#### io#connect\n\n```js\nio.connect(uri, [options]);\n```\n\n##### Options:\n\n- *resource*\n\n socket.io\n\n The resource is what allows the `socket.io` server to identify incoming connections by `socket.io` clients. In other words, any HTTP server can implement socket.io and still serve other normal, non-realtime HTTP requests.\n\n- *transports*\n\n```js\n['websocket', 'flashsocket', 'htmlfile', 'xhr-multipart', 'xhr-polling', 'jsonp-polling']\n```\n\n A list of the transports to attempt to utilize (in order of preference).\n\n- *'connect timeout'*\n\n```js\n5000\n```\n\n The amount of milliseconds a transport has to create a connection before we consider it timed out.\n \n- *'try multiple transports'*\n\n```js\ntrue\n```\n\n A boolean indicating if we should try other transports when the connectTimeout occurs.\n \n- *reconnect*\n\n```js\ntrue\n```\n\n A boolean indicating if we should automatically reconnect if a connection is disconnected. \n \n- *'reconnection delay'*\n\n```js\n500\n```\n\n The amount of milliseconds before we try to connect to the server again. We are using a exponential back off algorithm for the following reconnections, on each reconnect attempt this value will get multiplied (500 > 1000 > 2000 > 4000 > 8000).\n \n\n- *'max reconnection attempts'*\n\n```js\n10\n```\n\n The amount of attempts should we make using the current transport to connect to the server? After this we will do one final attempt, and re-try with all enabled transport methods before we give up.\n\n##### Properties:\n\n- *options*\n\n The passed in options combined with the defaults.\n\n- *connected*\n\n Whether the socket is connected or not.\n \n- *connecting*\n\n Whether the socket is connecting or not.\n\n- *reconnecting*\n\n Whether we are reconnecting or not.\n \n- *transport* \n\n The transport instance.\n\n##### Methods:\n \n- *connect(λ)*\n\n Establishes a connection. If λ is supplied as argument, it will be called once the connection is established.\n \n- *send(message)*\n \n A string of data to send.\n \n- *disconnect*\n\n Closes the connection.\n \n- *on(event, λ)*\n\n Adds a listener for the event *event*.\n\n- *once(event, λ)*\n\n Adds a one time listener for the event *event*. The listener is removed after the first time the event is fired.\n \n- *removeListener(event, λ)*\n\n Removes the listener λ for the event *event*.\n \n##### Events:\n\n- *connect*\n\n Fired when the connection is established and the handshake successful.\n \n- *connecting(transport_type)*\n\n Fired when a connection is attempted, passing the transport name.\n \n- *connect_failed*\n\n Fired when the connection timeout occurs after the last connection attempt.\n This only fires if the `connectTimeout` option is set.\n If the `tryTransportsOnConnectTimeout` option is set, this only fires once all\n possible transports have been tried.\n \n- *message(message)*\n \n Fired when a message arrives from the server\n\n- *close*\n\n Fired when the connection is closed. Be careful with using this event, as some transports will fire it even under temporary, expected disconnections (such as XHR-Polling).\n \n- *disconnect*\n\n Fired when the connection is considered disconnected.\n \n- *reconnect(transport_type,reconnectionAttempts)*\n\n Fired when the connection has been re-established. This only fires if the `reconnect` option is set.\n\n- *reconnecting(reconnectionDelay,reconnectionAttempts)*\n\n Fired when a reconnection is attempted, passing the next delay for the next reconnection.\n\n- *reconnect_failed*\n\n Fired when all reconnection attempts have failed and we where unsuccessful in reconnecting to the server. \n\n### Contributors\n\nGuillermo Rauch <guillermo@learnboost.com>\n\nArnout Kazemier <info@3rd-eden.com>\n\n### License \n\n(The MIT License)\n\nCopyright (c) 2010 LearnBoost <dev@learnboost.com>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/LearnBoost/socket.io-client/issues" + }, + "_id": "socket.io-client@0.9.16", + "dist": { + "shasum": "4647a4ca0fab802c72f34d79cfdd1e14b94232e3" + }, + "_from": "socket.io-client@0.9.16", + "_resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-0.9.16.tgz" +} diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/events.test.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/events.test.js new file mode 100644 index 0000000..365c422 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/events.test.js @@ -0,0 +1,120 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (module, io, should) { + + module.exports = { + + 'add listeners': function () { + var event = new io.EventEmitter + , calls = 0; + + event.on('test', function (a, b) { + ++calls; + a.should().eql('a'); + b.should().eql('b'); + }); + + event.emit('test', 'a', 'b'); + calls.should().eql(1); + event.on.should().eql(event.addListener); + }, + + 'remove listener': function () { + var event = new io.EventEmitter; + function empty () { } + + event.on('test', empty); + event.on('test:more', empty); + event.removeAllListeners('test'); + + event.listeners('test').should().eql([]); + event.listeners('test:more').should().eql([empty]); + }, + + 'remove all listeners with no arguments': function () { + var event = new io.EventEmitter; + function empty () { } + + event.on('test', empty); + event.on('test:more', empty); + event.removeAllListeners(); + + event.listeners('test').should().eql([]); + event.listeners('test:more').should().eql([]); + }, + + 'remove listeners functions': function () { + var event = new io.EventEmitter + , calls = 0; + + function one () { ++calls } + function two () { ++calls } + function three () { ++calls } + + event.on('one', one); + event.removeListener('one', one); + event.listeners('one').should().eql([]); + + event.on('two', two); + event.removeListener('two', one); + event.listeners('two').should().eql([two]); + + event.on('three', three); + event.on('three', two); + event.removeListener('three', three); + event.listeners('three').should().eql([two]); + }, + + 'number of arguments': function () { + var event = new io.EventEmitter + , number = []; + + event.on('test', function () { + number.push(arguments.length); + }); + + event.emit('test'); + event.emit('test', null); + event.emit('test', null, null); + event.emit('test', null, null, null); + event.emit('test', null, null, null, null); + event.emit('test', null, null, null, null, null); + + [0, 1, 2, 3, 4, 5].should().eql(number); + }, + + 'once': function () { + var event = new io.EventEmitter + , calls = 0; + + event.once('test', function (a, b) { + ++calls; + }); + + event.emit('test', 'a', 'b'); + event.emit('test', 'a', 'b'); + event.emit('test', 'a', 'b'); + + function removed () { + should().fail('not removed'); + }; + + event.once('test:removed', removed); + event.removeListener('test:removed', removed); + event.emit('test:removed'); + + calls.should().eql(1); + } + + }; + +})( + 'undefined' == typeof module ? module = {} : module + , 'undefined' == typeof io ? require('socket.io-client') : io + , 'undefined' == typeof should || !should.fail ? require('should') : should +); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/io.test.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/io.test.js new file mode 100644 index 0000000..d9f0b09 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/io.test.js @@ -0,0 +1,31 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (module, io, should) { + + module.exports = { + + 'client version number': function () { + io.version.should().match(/([0-9]+)\.([0-9]+)\.([0-9]+)/); + }, + + 'socket.io protocol version': function () { + io.protocol.should().be.a('number'); + io.protocol.toString().should().match(/^\d+$/); + }, + + 'socket.io available transports': function () { + (io.transports.length > 0).should().be_true; + } + + }; + +})( + 'undefined' == typeof module ? module = {} : module + , 'undefined' == typeof io ? require('socket.io-client') : io + , 'undefined' == typeof should ? require('should') : should +); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/node/builder.common.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/node/builder.common.js new file mode 100644 index 0000000..fa8d46e --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/node/builder.common.js @@ -0,0 +1,102 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +var vm = require('vm') + , should = require('should'); + +/** + * Generates evn variables for the vm so we can `emulate` a browser. + * @returns {Object} evn variables + */ + +exports.env = function env () { + var details = { + location: { + port: 8080 + , host: 'www.example.org' + , hostname: 'www.example.org' + , href: 'http://www.example.org/example/' + , pathname: '/example/' + , protocol: 'http:' + , search: '' + , hash: '' + } + , console: { + log: function(){}, + info: function(){}, + warn: function(){}, + error: function(){} + } + , navigator: { + userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_7) AppleWebKit' + + '/534.27 (KHTML, like Gecko) Chrome/12.0.716.0 Safari/534.27' + , appName: 'socket.io' + , platform: process.platform + , appVersion: process.version + , } + , name: 'socket.io' + , innerWidth: 1024 + , innerHeight: 768 + , length: 1 + , outerWidth: 1024 + , outerHeight: 768 + , pageXOffset: 0 + , pageYOffset: 0 + , screenX: 0 + , screenY: 0 + , screenLeft: 0 + , screenTop: 0 + , scrollX: 0 + , scrollY: 0 + , scrollTop: 0 + , scrollLeft: 0 + , screen: { + width: 0 + , height: 0 + } + }; + + // circular references + details.window = details.self = details.contentWindow = details; + + // callable methods + details.Image = details.scrollTo = details.scrollBy = details.scroll = + details.resizeTo = details.resizeBy = details.prompt = details.print = + details.open = details.moveTo = details.moveBy = details.focus = + details.createPopup = details.confirm = details.close = details.blur = + details.alert = details.clearTimeout = details.clearInterval = + details.setInterval = details.setTimeout = details.XMLHttpRequest = + details.getComputedStyle = details.trigger = details.dispatchEvent = + details.removeEventListener = details.addEventListener = function(){}; + + // frames + details.frames = [details]; + + // document + details.document = details; + details.document.domain = details.location.href; + + return details; +}; + +/** + * Executes a script in a browser like env and returns + * the result + * + * @param {String} contents The script content + * @returns {Object} The evaluated script. + */ + +exports.execute = function execute (contents) { + var env = exports.env() + , script = vm.createScript(contents); + + // run the script with `browser like` globals + script.runInNewContext(env); + + return env; +}; diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/node/builder.test.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/node/builder.test.js new file mode 100644 index 0000000..989e2bc --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/node/builder.test.js @@ -0,0 +1,131 @@ +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Test dependencies. + */ + +var builder = require('../../bin/builder') + , common = require('./builder.common') + , should = require('should'); + +/** + * Tests. + */ + +module.exports = { + + 'version number': function () { + builder.version.should().match(/([0-9]+)\.([0-9]+)\.([0-9]+)/); + builder.version.should().equal(require('../../lib/io').version); + }, + + 'production build LOC': function () { + builder(function (err, result) { + should.strictEqual(err, null) + + var lines = result.split('\n'); + lines.length.should().be.below(5); + lines[0].should().match(/production/gi); + Buffer.byteLength(result).should().be.below(43000); + }); + }, + + 'development build LOC': function () { + builder({ minify: false }, function (err, result) { + should.strictEqual(err, null) + + var lines = result.split('\n'); + lines.length.should().be.above(5); + lines[0].should().match(/development/gi); + Buffer.byteLength(result).should().be.above(35000); + }); + }, + + 'default builds': function () { + builder(function (err, result) { + should.strictEqual(err, null); + + var io = common.execute(result).io + , transports = Object.keys(io.Transport) + , defaults = Object.keys(builder.transports); + + /* XHR transport is private, but still available */ + transports.length.should().be.equal(defaults.length + 1); + + defaults.forEach(function (transport) { + transports.indexOf(transport).should().be.above(-1); + }) + }); + }, + + 'custom build': function () { + builder(['websocket'], function (err, result) { + should.strictEqual(err, null); + + var io = common.execute(result).io + , transports = Object.keys(io.Transport); + + transports.should().have.length(1); + transports[0].should().eql('websocket'); + }); + }, + + 'custom code': function () { + var custom = 'var hello = "world";'; + builder({ custom: [custom], minify: false }, function (err, result) { + should.strictEqual(err, null); + + result.should().include.string(custom); + }); + }, + + 'node if': function () { + var custom = '// if node \nvar hello = "world";\n' + + '// end node\nvar pew = "pew";'; + + builder({ custom: [custom], minify: false }, function (err, result) { + should.strictEqual(err, null); + + result.should().not.include.string(custom); + result.should().not.include.string('// if node'); + result.should().not.include.string('// end node'); + result.should().not.include.string('"world"'); + + result.should().include.string('var pew = "pew"'); + }); + }, + + 'preserve the encoding during minification': function () { + builder(function (err, result) { + should.strictEqual(err, null); + + result.should().match(/(\\ufffd)/g); + }) + }, + + 'globals': function () { + builder(function (err, result) { + should.strictEqual(err, null); + + var io = common.execute(result) + , env = common.env() + , allowed = ['io', 'swfobject', 'WEB_SOCKET_DISABLE_AUTO_INITIALIZATION']; + + Array.prototype.push.apply(allowed, Object.keys(env)); + + Object.keys(io).forEach(function (global) { + var index = allowed.indexOf(global); + + // the global is not allowed! + if (!~index) { + throw new Error('Global leak: ' + global); + } + }); + }) + } + +}; diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/parser.test.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/parser.test.js new file mode 100644 index 0000000..0022afb --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/parser.test.js @@ -0,0 +1,360 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (module, io, should) { + + var parser = io.parser; + + module.exports = { + + 'decoding error packet': function () { + parser.decodePacket('7:::').should().eql({ + type: 'error' + , reason: '' + , advice: '' + , endpoint: '' + }); + }, + + 'decoding error packet with reason': function () { + parser.decodePacket('7:::0').should().eql({ + type: 'error' + , reason: 'transport not supported' + , advice: '' + , endpoint: '' + }); + }, + + 'decoding error packet with reason and advice': function () { + parser.decodePacket('7:::2+0').should().eql({ + type: 'error' + , reason: 'unauthorized' + , advice: 'reconnect' + , endpoint: '' + }); + }, + + 'decoding error packet with endpoint': function () { + parser.decodePacket('7::/woot').should().eql({ + type: 'error' + , reason: '' + , advice: '' + , endpoint: '/woot' + }); + }, + + 'decoding ack packet': function () { + parser.decodePacket('6:::140').should().eql({ + type: 'ack' + , ackId: '140' + , endpoint: '' + , args: [] + }); + }, + + 'decoding ack packet with args': function () { + parser.decodePacket('6:::12+["woot","wa"]').should().eql({ + type: 'ack' + , ackId: '12' + , endpoint: '' + , args: ['woot', 'wa'] + }); + }, + + 'decoding ack packet with bad json': function () { + var thrown = false; + + try { + parser.decodePacket('6:::1+{"++]').should().eql({ + type: 'ack' + , ackId: '1' + , endpoint: '' + , args: [] + }); + } catch (e) { + thrown = true; + } + + thrown.should().be_false; + }, + + 'decoding json packet': function () { + parser.decodePacket('4:::"2"').should().eql({ + type: 'json' + , endpoint: '' + , data: '2' + }); + }, + + 'decoding json packet with message id and ack data': function () { + parser.decodePacket('4:1+::{"a":"b"}').should().eql({ + type: 'json' + , id: 1 + , ack: 'data' + , endpoint: '' + , data: { a: 'b' } + }); + }, + + 'decoding an event packet': function () { + parser.decodePacket('5:::{"name":"woot"}').should().eql({ + type: 'event' + , name: 'woot' + , endpoint: '' + , args: [] + }); + }, + + 'decoding an event packet with message id and ack': function () { + parser.decodePacket('5:1+::{"name":"tobi"}').should().eql({ + type: 'event' + , id: 1 + , ack: 'data' + , endpoint: '' + , name: 'tobi' + , args: [] + }); + }, + + 'decoding an event packet with data': function () { + parser.decodePacket('5:::{"name":"edwald","args":[{"a": "b"},2,"3"]}') + .should().eql({ + type: 'event' + , name: 'edwald' + , endpoint: '' + , args: [{a: 'b'}, 2, '3'] + }); + }, + + 'decoding a message packet': function () { + parser.decodePacket('3:::woot').should().eql({ + type: 'message' + , endpoint: '' + , data: 'woot' + }); + }, + + 'decoding a message packet with id and endpoint': function () { + parser.decodePacket('3:5:/tobi').should().eql({ + type: 'message' + , id: 5 + , ack: true + , endpoint: '/tobi' + , data: '' + }); + }, + + 'decoding a heartbeat packet': function () { + parser.decodePacket('2:::').should().eql({ + type: 'heartbeat' + , endpoint: '' + }); + }, + + 'decoding a connection packet': function () { + parser.decodePacket('1::/tobi').should().eql({ + type: 'connect' + , endpoint: '/tobi' + , qs: '' + }); + }, + + 'decoding a connection packet with query string': function () { + parser.decodePacket('1::/test:?test=1').should().eql({ + type: 'connect' + , endpoint: '/test' + , qs: '?test=1' + }); + }, + + 'decoding a disconnection packet': function () { + parser.decodePacket('0::/woot').should().eql({ + type: 'disconnect' + , endpoint: '/woot' + }); + }, + + 'encoding error packet': function () { + parser.encodePacket({ + type: 'error' + , reason: '' + , advice: '' + , endpoint: '' + }).should().eql('7::'); + }, + + 'encoding error packet with reason': function () { + parser.encodePacket({ + type: 'error' + , reason: 'transport not supported' + , advice: '' + , endpoint: '' + }).should().eql('7:::0'); + }, + + 'encoding error packet with reason and advice': function () { + parser.encodePacket({ + type: 'error' + , reason: 'unauthorized' + , advice: 'reconnect' + , endpoint: '' + }).should().eql('7:::2+0'); + }, + + 'encoding error packet with endpoint': function () { + parser.encodePacket({ + type: 'error' + , reason: '' + , advice: '' + , endpoint: '/woot' + }).should().eql('7::/woot'); + }, + + 'encoding ack packet': function () { + parser.encodePacket({ + type: 'ack' + , ackId: '140' + , endpoint: '' + , args: [] + }).should().eql('6:::140'); + }, + + 'encoding ack packet with args': function () { + parser.encodePacket({ + type: 'ack' + , ackId: '12' + , endpoint: '' + , args: ['woot', 'wa'] + }).should().eql('6:::12+["woot","wa"]'); + }, + + 'encoding json packet': function () { + parser.encodePacket({ + type: 'json' + , endpoint: '' + , data: '2' + }).should().eql('4:::"2"'); + }, + + 'encoding json packet with message id and ack data': function () { + parser.encodePacket({ + type: 'json' + , id: 1 + , ack: 'data' + , endpoint: '' + , data: { a: 'b' } + }).should().eql('4:1+::{"a":"b"}'); + }, + + 'encoding an event packet': function () { + parser.encodePacket({ + type: 'event' + , name: 'woot' + , endpoint: '' + , args: [] + }).should().eql('5:::{"name":"woot"}'); + }, + + 'encoding an event packet with message id and ack': function () { + parser.encodePacket({ + type: 'event' + , id: 1 + , ack: 'data' + , endpoint: '' + , name: 'tobi' + , args: [] + }).should().eql('5:1+::{"name":"tobi"}'); + }, + + 'encoding an event packet with data': function () { + parser.encodePacket({ + type: 'event' + , name: 'edwald' + , endpoint: '' + , args: [{a: 'b'}, 2, '3'] + }).should().eql('5:::{"name":"edwald","args":[{"a":"b"},2,"3"]}'); + }, + + 'encoding a message packet': function () { + parser.encodePacket({ + type: 'message' + , endpoint: '' + , data: 'woot' + }).should().eql('3:::woot'); + }, + + 'encoding a message packet with id and endpoint': function () { + parser.encodePacket({ + type: 'message' + , id: 5 + , ack: true + , endpoint: '/tobi' + , data: '' + }).should().eql('3:5:/tobi'); + }, + + 'encoding a heartbeat packet': function () { + parser.encodePacket({ + type: 'heartbeat' + , endpoint: '' + }).should().eql('2::'); + }, + + 'encoding a connection packet': function () { + parser.encodePacket({ + type: 'connect' + , endpoint: '/tobi' + , qs: '' + }).should().eql('1::/tobi'); + }, + + 'encoding a connection packet with query string': function () { + parser.encodePacket({ + type: 'connect' + , endpoint: '/test' + , qs: '?test=1' + }).should().eql('1::/test:?test=1'); + }, + + 'encoding a disconnection packet': function () { + parser.encodePacket({ + type: 'disconnect' + , endpoint: '/woot' + }).should().eql('0::/woot'); + }, + + 'test decoding a payload': function () { + parser.decodePayload('\ufffd5\ufffd3:::5\ufffd7\ufffd3:::53d' + + '\ufffd3\ufffd0::').should().eql([ + { type: 'message', data: '5', endpoint: '' } + , { type: 'message', data: '53d', endpoint: '' } + , { type: 'disconnect', endpoint: '' } + ]); + }, + + 'test encoding a payload': function () { + parser.encodePayload([ + parser.encodePacket({ type: 'message', data: '5', endpoint: '' }) + , parser.encodePacket({ type: 'message', data: '53d', endpoint: '' }) + ]).should().eql('\ufffd5\ufffd3:::5\ufffd7\ufffd3:::53d') + }, + + 'test decoding newline': function () { + parser.decodePacket('3:::\n').should().eql({ + type: 'message' + , endpoint: '' + , data: '\n' + }); + } + + }; + +})( + 'undefined' == typeof module ? module = {} : module + , 'undefined' == typeof io ? require('socket.io-client') : io + , 'undefined' == typeof should ? require('should') : should +); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/socket.test.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/socket.test.js new file mode 100644 index 0000000..eae4956 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/socket.test.js @@ -0,0 +1,422 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (module, io, should) { + + if ('object' == typeof global) { + return module.exports = { '': function () {} }; + } + + module.exports = { + + 'test connecting the socket and disconnecting': function (next) { + var socket = create(); + + socket.on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.on('connect', function () { + socket.disconnect(); + next(); + }); + }, + + 'test receiving messages': function (next) { + var socket = create() + , connected = false + , messages = 0; + + socket.on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.on('connect', function () { + connected = true; + }); + + socket.on('message', function (i) { + String(++messages).should().equal(i); + }); + + socket.on('disconnect', function (reason) { + connected.should().be_true; + messages.should().equal(3); + reason.should().eql('booted'); + next(); + }); + }, + + 'test sending messages': function (next) { + var socket = create(); + + socket.on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.on('connect', function () { + socket.send('echo'); + + socket.on('message', function (msg) { + msg.should().equal('echo'); + socket.disconnect(); + next(); + }); + }); + }, + + 'test manual buffer flushing': function (next) { + var socket = create(); + + socket.socket.options['manualFlush'] = true; + + socket.on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.on('connect', function () { + socket.socket.connected = false; + socket.send('buffered'); + socket.socket.onConnect(); + socket.socket.flushBuffer(); + + socket.on('message', function (msg) { + msg.should().equal('buffered'); + socket.disconnect(); + next(); + }); + }); + }, + + 'test automatic buffer flushing': function (next) { + var socket = create(); + + socket.on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.on('connect', function () { + socket.socket.connected = false; + socket.send('buffered'); + socket.socket.onConnect(); + + socket.on('message', function (msg) { + msg.should().equal('buffered'); + socket.disconnect(); + next(); + }); + }); + }, + + 'test acks sent from client': function (next) { + var socket = create(); + + socket.on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.on('connect', function () { + socket.on('message', function (msg) { + if ('tobi 2' == msg) { + socket.disconnect(); + next(); + } + }); + }); + }, + + 'test acks sent from server': function (next) { + var socket = create(); + + socket.on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.on('connect', function () { + socket.send('ooo', function () { + socket.disconnect(); + next(); + }); + }); + }, + + 'test connecting to namespaces': function (next) { + var io = create() + , socket = io.socket + , namespaces = 2 + , connect = 0; + + function finish () { + socket.of('').disconnect(); + connect.should().equal(3); + next(); + } + + socket.on('connect', function(){ + connect++; + }); + + socket.of('/woot').on('connect', function () { + connect++; + }).on('message', function (msg) { + msg.should().equal('connected to woot'); + --namespaces || finish(); + }).on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.of('/chat').on('connect', function () { + connect++; + }).on('message', function (msg) { + msg.should().equal('connected to chat'); + --namespaces || finish(); + }).on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + }, + + 'test disconnecting from namespaces': function (next) { + var socket = create().socket + , namespaces = 2 + , disconnections = 0; + + function finish () { + socket.of('').disconnect(); + next(); + }; + + socket.of('/a').on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.of('/a').on('connect', function () { + socket.of('/a').disconnect(); + }); + + socket.of('/a').on('disconnect', function () { + --namespaces || finish(); + }); + + socket.of('/b').on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.of('/b').on('connect', function () { + socket.of('/b').disconnect(); + }); + + socket.of('/b').on('disconnect', function () { + --namespaces || finish(); + }); + }, + + 'test authorizing for namespaces': function (next) { + var socket = create().socket + + function finish () { + socket.of('').disconnect(); + next(); + }; + + socket.of('/a') + .on('connect_failed', function (msg) { + next(); + }) + .on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + }, + + 'test sending json from server': function (next) { + var socket = create(); + + socket.on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.on('message', function (msg) { + msg.should().eql(3141592); + socket.disconnect(); + next(); + }); + }, + + 'test sending json from client': function (next) { + var socket = create(); + + socket.on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.json.send([1, 2, 3]); + socket.on('message', function (msg) { + msg.should().equal('echo'); + socket.disconnect(); + next(); + }); + }, + + 'test emitting an event from server': function (next) { + var socket = create(); + + socket.on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.on('woot', function () { + socket.disconnect(); + next(); + }); + }, + + 'test emitting an event to server': function (next) { + var socket = create(); + + socket.on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.emit('woot'); + socket.on('echo', function () { + socket.disconnect(); + next(); + }) + }, + + 'test emitting multiple events at once to the server': function (next) { + var socket = create(); + + socket.on('connect', function () { + socket.emit('print', 'foo'); + socket.emit('print', 'bar'); + }); + + socket.on('done', function () { + socket.disconnect(); + next(); + }); + }, + + 'test emitting an event from server and sending back data': function (next) { + var socket = create(); + + socket.on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.on('woot', function (a, fn) { + a.should().eql(1); + fn('test'); + + socket.on('done', function () { + socket.disconnect(); + next(); + }); + }); + }, + + 'test emitting an event to server and sending back data': function (next) { + var socket = create(); + + socket.on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.emit('tobi', 1, 2, function (a) { + a.should().eql({ hello: 'world' }); + socket.disconnect(); + next(); + }); + }, + + 'test encoding a payload': function (next) { + var socket = create('/woot'); + + socket.on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.on('connect', function () { + socket.socket.setBuffer(true); + socket.send('ñ'); + socket.send('ñ'); + socket.send('ñ'); + socket.send('ñ'); + socket.socket.setBuffer(false); + }); + + socket.on('done', function () { + socket.disconnect(); + next(); + }); + }, + + 'test sending query strings to the server': function (next) { + var socket = create('?foo=bar'); + + socket.on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.on('message', function (data) { + data.query.foo.should().eql('bar'); + + socket.disconnect(); + next(); + }); + }, + + 'test sending newline': function (next) { + var socket = create(); + + socket.on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.send('\n'); + + socket.on('done', function () { + socket.disconnect(); + next(); + }); + }, + + 'test sending unicode': function (next) { + var socket = create(); + + socket.on('error', function (msg) { + throw new Error(msg || 'Received an error'); + }); + + socket.json.send({ test: "☃" }); + + socket.on('done', function () { + socket.disconnect(); + next(); + }); + }, + + 'test webworker connection': function (next) { + if (!window.Worker) { + return next(); + } + + var worker = new Worker('/test/worker.js'); + worker.postMessage(uri()); + worker.onmessage = function (ev) { + if ('done!' == ev.data) return next(); + throw new Error('Unexpected message: ' + ev.data); + } + } + + }; + +})( + 'undefined' == typeof module ? module = {} : module + , 'undefined' == typeof io ? require('socket.io-client') : io + , 'undefined' == typeof should ? require('should-browser') : should +); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/util.test.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/util.test.js new file mode 100644 index 0000000..30db5a6 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/util.test.js @@ -0,0 +1,156 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (module, io, should) { + + module.exports = { + + 'parse uri': function () { + var http = io.util.parseUri('http://google.com') + , https = io.util.parseUri('https://www.google.com:80') + , query = io.util.parseUri('google.com:8080/foo/bar?foo=bar'); + + http.protocol.should().eql('http'); + http.port.should().eql(''); + http.host.should().eql('google.com'); + https.protocol.should().eql('https'); + https.port.should().eql('80'); + https.host.should().eql('www.google.com'); + query.port.should().eql('8080'); + query.query.should().eql('foo=bar'); + query.path.should().eql('/foo/bar'); + query.relative.should().eql('/foo/bar?foo=bar'); + }, + + 'unique uri': function () { + var protocol = io.util.parseUri('http://google.com') + , noprotocol = io.util.parseUri('google.com') + , https = io.util.parseUri('https://google.com') + , path = io.util.parseUri('https://google.com/google.com/com/?foo=bar'); + + if ('object' == typeof window) { + io.util.uniqueUri(protocol).should().eql('http://google.com:3000'); + io.util.uniqueUri(noprotocol).should().eql('http://google.com:3000'); + } else { + io.util.uniqueUri(protocol).should().eql('http://google.com:80'); + io.util.uniqueUri(noprotocol).should().eql('http://google.com:80'); + } + + io.util.uniqueUri(https).should().eql('https://google.com:443'); + io.util.uniqueUri(path).should().eql('https://google.com:443'); + }, + + 'chunk query string': function () { + io.util.chunkQuery('foo=bar').should().be.a('object'); + io.util.chunkQuery('foo=bar').foo.should().eql('bar'); + }, + + 'merge query strings': function () { + var base = io.util.query('foo=bar', 'foo=baz') + , add = io.util.query('foo=bar', 'bar=foo') + + base.should().eql('?foo=baz'); + add.should().eql('?foo=bar&bar=foo'); + + io.util.query('','').should().eql(''); + io.util.query('foo=bar', '').should().eql('?foo=bar'); + io.util.query('', 'foo=bar').should().eql('?foo=bar'); + }, + + 'request': function () { + var type = typeof io.util.request(); + type.should().eql('object'); + }, + + 'is array': function () { + io.util.isArray([]).should().be_true; + io.util.isArray({}).should().be_false; + io.util.isArray('str').should().be_false; + io.util.isArray(new Date).should().be_false; + io.util.isArray(true).should().be_false; + io.util.isArray(arguments).should().be_false; + }, + + 'merge, deep merge': function () { + var start = { + foo: 'bar' + , bar: 'baz' + } + , duplicate = { + foo: 'foo' + , bar: 'bar' + } + , extra = { + ping: 'pong' + } + , deep = { + level1:{ + foo: 'bar' + , level2: { + foo: 'bar' + , level3:{ + foo: 'bar' + , rescursive: deep + } + } + } + } + // same structure, but changed names + , deeper = { + foo: 'bar' + , level1:{ + foo: 'baz' + , level2: { + foo: 'foo' + , level3:{ + foo: 'pewpew' + , rescursive: deep + } + } + } + }; + + io.util.merge(start, duplicate); + + start.foo.should().eql('foo'); + start.bar.should().eql('bar'); + + io.util.merge(start, extra); + start.ping.should().eql('pong'); + start.foo.should().eql('foo'); + + io.util.merge(deep, deeper); + + deep.foo.should().eql('bar'); + deep.level1.foo.should().eql('baz'); + deep.level1.level2.foo.should().eql('foo'); + deep.level1.level2.level3.foo.should().eql('pewpew'); + }, + + 'defer': function (next) { + var now = +new Date; + + io.util.defer(function () { + ((new Date - now) >= ( io.util.webkit ? 100 : 0 )).should().be_true(); + next(); + }) + }, + + 'indexOf': function () { + var data = ['socket', 2, 3, 4, 'socket', 5, 6, 7, 'io']; + io.util.indexOf(data, 'socket', 1).should().eql(4); + io.util.indexOf(data, 'socket').should().eql(0); + io.util.indexOf(data, 'waffles').should().eql(-1); + } + + }; + +})( + 'undefined' == typeof module ? module = {} : module + , 'undefined' == typeof io ? require('socket.io-client') : io + , 'undefined' == typeof should ? require('should') : should +); diff --git a/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/worker.js b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/worker.js new file mode 100644 index 0000000..c542632 --- /dev/null +++ b/sockio-server/node_modules/socket.io/node_modules/socket.io-client/test/worker.js @@ -0,0 +1,20 @@ +importScripts('/socket.io/socket.io.js'); + +self.onmessage = function (ev) { + var url = ev.data + , socket = io.connect(url); + + socket.on('done', function () { + self.postMessage('done!'); + }); + + socket.on('connect_failed', function () { + self.postMessage('connect failed'); + }); + + socket.on('error', function () { + self.postMessage('error'); + }); + + socket.send('woot'); +} diff --git a/sockio-server/node_modules/socket.io/package.json b/sockio-server/node_modules/socket.io/package.json new file mode 100644 index 0000000..8b488af --- /dev/null +++ b/sockio-server/node_modules/socket.io/package.json @@ -0,0 +1,74 @@ +{ + "name": "socket.io", + "version": "0.9.16", + "description": "Real-time apps made cross-browser & easy with a WebSocket-like API", + "homepage": "http://socket.io", + "keywords": [ + "websocket", + "socket", + "realtime", + "socket.io", + "comet", + "ajax" + ], + "author": { + "name": "Guillermo Rauch", + "email": "guillermo@learnboost.com" + }, + "contributors": [ + { + "name": "Guillermo Rauch", + "email": "rauchg@gmail.com" + }, + { + "name": "Arnout Kazemier", + "email": "info@3rd-eden.com" + }, + { + "name": "Vladimir Dronnikov", + "email": "dronnikov@gmail.com" + }, + { + "name": "Einar Otto Stangvik", + "email": "einaros@gmail.com" + } + ], + "repository": { + "type": "git", + "url": "https://github.com/LearnBoost/socket.io.git" + }, + "dependencies": { + "socket.io-client": "0.9.16", + "policyfile": "0.0.4", + "base64id": "0.1.0", + "redis": "0.7.3" + }, + "devDependencies": { + "expresso": "0.9.2", + "should": "*", + "benchmark": "0.2.2", + "microtime": "0.1.3-1", + "colors": "0.5.1" + }, + "optionalDependencies": { + "redis": "0.7.3" + }, + "main": "index", + "engines": { + "node": ">= 0.4.0" + }, + "scripts": { + "test": "make test" + }, + "readme": "# Socket.IO\n\nSocket.IO is a Node.JS project that makes WebSockets and realtime possible in\nall browsers. It also enhances WebSockets by providing built-in multiplexing,\nhorizontal scalability, automatic JSON encoding/decoding, and more.\n\n## How to Install\n\n```bash\nnpm install socket.io\n```\n\n## How to use\n\nFirst, require `socket.io`:\n\n```js\nvar io = require('socket.io');\n```\n\nNext, attach it to a HTTP/HTTPS server. If you're using the fantastic `express`\nweb framework:\n\n#### Express 3.x\n\n```js\nvar app = express()\n , server = require('http').createServer(app)\n , io = io.listen(server);\n\nserver.listen(80);\n\nio.sockets.on('connection', function (socket) {\n socket.emit('news', { hello: 'world' });\n socket.on('my other event', function (data) {\n console.log(data);\n });\n});\n```\n\n#### Express 2.x\n\n```js\nvar app = express.createServer()\n , io = io.listen(app);\n\napp.listen(80);\n\nio.sockets.on('connection', function (socket) {\n socket.emit('news', { hello: 'world' });\n socket.on('my other event', function (data) {\n console.log(data);\n });\n});\n```\n\nFinally, load it from the client side code:\n\n```html\n\n\n```\n\nFor more thorough examples, look at the `examples/` directory.\n\n## Short recipes\n\n### Sending and receiving events.\n\nSocket.IO allows you to emit and receive custom events.\nBesides `connect`, `message` and `disconnect`, you can emit custom events:\n\n```js\n// note, io.listen() will create a http server for you\nvar io = require('socket.io').listen(80);\n\nio.sockets.on('connection', function (socket) {\n io.sockets.emit('this', { will: 'be received by everyone' });\n\n socket.on('private message', function (from, msg) {\n console.log('I received a private message by ', from, ' saying ', msg);\n });\n\n socket.on('disconnect', function () {\n io.sockets.emit('user disconnected');\n });\n});\n```\n\n### Storing data associated to a client\n\nSometimes it's necessary to store data associated with a client that's\nnecessary for the duration of the session.\n\n#### Server side\n\n```js\nvar io = require('socket.io').listen(80);\n\nio.sockets.on('connection', function (socket) {\n socket.on('set nickname', function (name) {\n socket.set('nickname', name, function () { socket.emit('ready'); });\n });\n\n socket.on('msg', function () {\n socket.get('nickname', function (err, name) {\n console.log('Chat message by ', name);\n });\n });\n});\n```\n\n#### Client side\n\n```html\n\n```\n\n### Restricting yourself to a namespace\n\nIf you have control over all the messages and events emitted for a particular\napplication, using the default `/` namespace works.\n\nIf you want to leverage 3rd-party code, or produce code to share with others,\nsocket.io provides a way of namespacing a `socket`.\n\nThis has the benefit of `multiplexing` a single connection. Instead of\nsocket.io using two `WebSocket` connections, it'll use one.\n\nThe following example defines a socket that listens on '/chat' and one for\n'/news':\n\n#### Server side\n\n```js\nvar io = require('socket.io').listen(80);\n\nvar chat = io\n .of('/chat')\n .on('connection', function (socket) {\n socket.emit('a message', { that: 'only', '/chat': 'will get' });\n chat.emit('a message', { everyone: 'in', '/chat': 'will get' });\n });\n\nvar news = io\n .of('/news');\n .on('connection', function (socket) {\n socket.emit('item', { news: 'item' });\n });\n```\n\n#### Client side:\n\n```html\n\n```\n\n### Sending volatile messages.\n\nSometimes certain messages can be dropped. Let's say you have an app that\nshows realtime tweets for the keyword `bieber`. \n\nIf a certain client is not ready to receive messages (because of network slowness\nor other issues, or because he's connected through long polling and is in the\nmiddle of a request-response cycle), if he doesn't receive ALL the tweets related\nto bieber your application won't suffer.\n\nIn that case, you might want to send those messages as volatile messages.\n\n#### Server side\n\n```js\nvar io = require('socket.io').listen(80);\n\nio.sockets.on('connection', function (socket) {\n var tweets = setInterval(function () {\n getBieberTweet(function (tweet) {\n socket.volatile.emit('bieber tweet', tweet);\n });\n }, 100);\n\n socket.on('disconnect', function () {\n clearInterval(tweets);\n });\n});\n```\n\n#### Client side\n\nIn the client side, messages are received the same way whether they're volatile\nor not.\n\n### Getting acknowledgements\n\nSometimes, you might want to get a callback when the client confirmed the message\nreception.\n\nTo do this, simply pass a function as the last parameter of `.send` or `.emit`.\nWhat's more, when you use `.emit`, the acknowledgement is done by you, which\nmeans you can also pass data along:\n\n#### Server side\n\n```js\nvar io = require('socket.io').listen(80);\n\nio.sockets.on('connection', function (socket) {\n socket.on('ferret', function (name, fn) {\n fn('woot');\n });\n});\n```\n\n#### Client side\n\n```html\n\n```\n\n### Broadcasting messages\n\nTo broadcast, simply add a `broadcast` flag to `emit` and `send` method calls.\nBroadcasting means sending a message to everyone else except for the socket\nthat starts it.\n\n#### Server side\n\n```js\nvar io = require('socket.io').listen(80);\n\nio.sockets.on('connection', function (socket) {\n socket.broadcast.emit('user connected');\n socket.broadcast.json.send({ a: 'message' });\n});\n```\n\n### Rooms\n\nSometimes you want to put certain sockets in the same room, so that it's easy\nto broadcast to all of them together.\n\nThink of this as built-in channels for sockets. Sockets `join` and `leave`\nrooms in each socket.\n\n#### Server side\n\n```js\nvar io = require('socket.io').listen(80);\n\nio.sockets.on('connection', function (socket) {\n socket.join('justin bieber fans');\n socket.broadcast.to('justin bieber fans').emit('new fan');\n io.sockets.in('rammstein fans').emit('new non-fan');\n});\n```\n\n### Using it just as a cross-browser WebSocket\n\nIf you just want the WebSocket semantics, you can do that too.\nSimply leverage `send` and listen on the `message` event:\n\n#### Server side\n\n```js\nvar io = require('socket.io').listen(80);\n\nio.sockets.on('connection', function (socket) {\n socket.on('message', function () { });\n socket.on('disconnect', function () { });\n});\n```\n\n#### Client side\n\n```html\n\n```\n\n### Changing configuration\n\nConfiguration in socket.io is TJ-style:\n\n#### Server side\n\n```js\nvar io = require('socket.io').listen(80);\n\nio.configure(function () {\n io.set('transports', ['websocket', 'flashsocket', 'xhr-polling']);\n});\n\nio.configure('development', function () {\n io.set('transports', ['websocket', 'xhr-polling']);\n io.enable('log');\n});\n```\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2011 Guillermo Rauch <guillermo@learnboost.com>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + "readmeFilename": "Readme.md", + "bugs": { + "url": "https://github.com/LearnBoost/socket.io/issues" + }, + "_id": "socket.io@0.9.16", + "dist": { + "shasum": "66c811944e52058d9fb4e587e6b459018e796eff" + }, + "_from": "socket.io@~0.9.16", + "_resolved": "https://registry.npmjs.org/socket.io/-/socket.io-0.9.16.tgz" +} diff --git a/sockio-server/package.json b/sockio-server/package.json new file mode 100644 index 0000000..876af2b --- /dev/null +++ b/sockio-server/package.json @@ -0,0 +1,10 @@ +{ + "name": "ChatForGeeks", + "version": "1.0.0", + "description": "A simple real-time chat app. For GeekCamp SG 2013 presentation. By Joshua Teng. joshteng.com", + "dependencies": { + "express": "~3.3.5", + "socket.io": "~0.9.16", + "redis": "~0.8.4" + } +} \ No newline at end of file diff --git a/sockio-server/views/index.html b/sockio-server/views/index.html new file mode 100644 index 0000000..08dd807 --- /dev/null +++ b/sockio-server/views/index.html @@ -0,0 +1,89 @@ + + + + Geeks Chat App + + + + + + + +
+
+
+
Geeks in Chat
+
+
    +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file