From ab46173519224a84135814230f429d6fb9dd6c78 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Thu, 21 Jul 2011 22:19:36 -0700 Subject: [PATCH 0001/1128] Initial commit --- .gitignore | 17 + .nuget/NuGet.exe | Bin 0 -> 411648 bytes .nuget/NuGet.settings.targets | 41 + .nuget/NuGet.targets | 15 + SignalR.SQLServer/IJsonSerializer.cs | 11 + .../JavaScriptSerializerAdapter.cs | 32 + SignalR.SQLServer/PreApplicationStart.cs | 19 + SignalR.SQLServer/Properties/AssemblyInfo.cs | 36 + SignalR.SQLServer/SQLMessageStore.cs | 159 + .../SQLQueryNotificationsSignalBus.cs | 154 + SignalR.SQLServer/SignalR.SQLServer.csproj | 75 + SignalR.SQLServer/SignalRMessagesTable.sql | 44 + SignalR.SQLServer/SignalRSignalsTable.sql | 32 + SignalR.SQLServer/SqlAsyncExtensions.cs | 33 + SignalR.Samples/App_Start/Startup.cs | 31 + .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 0 -> 120 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 105 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 0 -> 111 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 0 -> 110 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 119 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 0 -> 101 bytes .../base/images/ui-icons_222222_256x240.png | Bin 0 -> 4369 bytes .../base/images/ui-icons_2e83ff_256x240.png | Bin 0 -> 4369 bytes .../base/images/ui-icons_454545_256x240.png | Bin 0 -> 4369 bytes .../base/images/ui-icons_888888_256x240.png | Bin 0 -> 4369 bytes .../base/images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4369 bytes .../themes/base/jquery.ui.accordion.css | 19 + .../Content/themes/base/jquery.ui.all.css | 11 + .../themes/base/jquery.ui.autocomplete.css | 53 + .../Content/themes/base/jquery.ui.base.css | 11 + .../Content/themes/base/jquery.ui.button.css | 38 + .../Content/themes/base/jquery.ui.core.css | 41 + .../themes/base/jquery.ui.datepicker.css | 68 + .../Content/themes/base/jquery.ui.dialog.css | 21 + .../themes/base/jquery.ui.progressbar.css | 11 + .../themes/base/jquery.ui.resizable.css | 25 + .../themes/base/jquery.ui.selectable.css | 10 + .../Content/themes/base/jquery.ui.slider.css | 24 + .../Content/themes/base/jquery.ui.tabs.css | 18 + .../Content/themes/base/jquery.ui.theme.css | 252 + .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 0 -> 120 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 105 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 0 -> 111 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 0 -> 110 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 119 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 0 -> 101 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_2e83ff_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_454545_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_888888_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4369 bytes .../themes/smoothness/jquery-ui-1.8.12.css | 578 + SignalR.Samples/Hubs/Chat/Chat.cs | 417 + SignalR.Samples/Hubs/Chat/Chat.css | 75 + SignalR.Samples/Hubs/Chat/Chat.js | 180 + .../CollegeHumorContentProvider.cs | 24 + .../ContentProviders/EmbedContentProvider.cs | 41 + .../Chat/ContentProviders/IContentProvider.cs | 7 + .../ContentProviders/ImageContentProvider.cs | 23 + .../YoutubeContentProvider.cs | 25 + SignalR.Samples/Hubs/Chat/index.htm | 39 + SignalR.Samples/Hubs/DemoHub/DemoHub.cs | 38 + SignalR.Samples/Hubs/DemoHub/DemoHub.js | 24 + SignalR.Samples/Hubs/DemoHub/index.htm | 19 + .../Hubs/MouseTracking/MouseTracking.cs | 16 + .../Hubs/MouseTracking/MouseTracking.css | 18 + .../Hubs/MouseTracking/MouseTracking.js | 35 + .../Hubs/MouseTracking/images/wand.png | Bin 0 -> 570 bytes SignalR.Samples/Hubs/MouseTracking/index.htm | 15 + SignalR.Samples/Hubs/ShapeShare/ShapeShare.cs | 204 + .../Hubs/ShapeShare/ShapeShare.css | 65 + SignalR.Samples/Hubs/ShapeShare/ShapeShare.js | 150 + SignalR.Samples/Hubs/ShapeShare/index.htm | 45 + SignalR.Samples/Performance/Performance.ashx | 1 + .../Performance/Performance.ashx.cs | 28 + SignalR.Samples/Performance/index.html | 25 + SignalR.Samples/Properties/AssemblyInfo.cs | 34 + SignalR.Samples/Raw/Raw.ashx | 1 + SignalR.Samples/Raw/Raw.ashx.cs | 111 + SignalR.Samples/Raw/index.htm | 119 + SignalR.Samples/Scripts/SignalR.js | 376 + SignalR.Samples/Scripts/SignalR.min.js | 1 + SignalR.Samples/Scripts/jQuery.tmpl.js | 486 + SignalR.Samples/Scripts/jQuery.tmpl.min.js | 1 + SignalR.Samples/Scripts/jquery-1.6.2-vsdoc.js | 6987 ++++++++++ SignalR.Samples/Scripts/jquery-1.6.2.js | 8981 ++++++++++++ SignalR.Samples/Scripts/jquery-1.6.2.min.js | 18 + SignalR.Samples/Scripts/jquery-ui-1.8.13.js | 11631 ++++++++++++++++ .../Scripts/jquery-ui-1.8.13.min.js | 784 ++ SignalR.Samples/Scripts/jquery.cookie.js | 96 + SignalR.Samples/Scripts/json2.js | 480 + SignalR.Samples/Scripts/json2.min.js | 1 + SignalR.Samples/SignalR.Samples.csproj | 240 + SignalR.Samples/Streaming/Streaming.ashx | 1 + SignalR.Samples/Streaming/Streaming.ashx.cs | 9 + SignalR.Samples/Streaming/index.html | 23 + SignalR.Samples/Web.Debug.config | 30 + SignalR.Samples/Web.Release.config | 31 + SignalR.Samples/Web.config | 29 + SignalR.Samples/packages.config | 12 + SignalR.sln | 32 + SignalR/CommandType.cs | 6 + SignalR/Connection.cs | 203 + SignalR/Hubs/ActionInfo.cs | 8 + SignalR/Hubs/ClientAgent.cs | 67 + SignalR/Hubs/DefaultActionResolver.cs | 60 + SignalR/Hubs/DefaultHubActivator.cs | 11 + SignalR/Hubs/DefaultHubFactory.cs | 37 + SignalR/Hubs/DefaultHubLocator.cs | 26 + .../Hubs/DefaultJavaScriptProxyGenerator.cs | 113 + SignalR/Hubs/Hub.cs | 24 + SignalR/Hubs/HubContext.cs | 24 + SignalR/Hubs/HubDispatcher.cs | 212 + SignalR/Hubs/HubModule.cs | 22 + SignalR/Hubs/IActionResolver.cs | 8 + SignalR/Hubs/IClientAgent.cs | 8 + SignalR/Hubs/IHubActivator.cs | 7 + SignalR/Hubs/IHubFactory.cs | 6 + SignalR/Hubs/IHubLocator.cs | 8 + SignalR/Hubs/IJavaScriptProxyGenerator.cs | 7 + SignalR/Hubs/SignalAgent.cs | 68 + SignalR/Hubs/TrackingDictionary.cs | 50 + SignalR/IConnection.cs | 15 + SignalR/IGroupManager.cs | 6 + SignalR/IJsonStringifier.cs | 6 + SignalR/IMessageStore.cs | 11 + SignalR/ISignalBus.cs | 10 + SignalR/InProcessMessageStore.cs | 75 + SignalR/InProcessSignalBus.cs | 39 + SignalR/Infrastructure/DependencyResolver.cs | 150 + SignalR/Infrastructure/IDependencyResolver.cs | 11 + SignalR/Infrastructure/SafeSet.cs | 37 + SignalR/JavaScriptSerializerAdapter.cs | 16 + SignalR/Json.cs | 18 + SignalR/Message.cs | 31 + SignalR/PersistentConnection.cs | 182 + SignalR/PersistentResponse.cs | 14 + SignalR/PreApplicationStart.cs | 21 + SignalR/Properties/AssemblyInfo.cs | 35 + SignalR/Properties/SignalR.nuspec | 14 + SignalR/Scripts/SignalR.min.js | 1 + SignalR/Scripts/hubs.js | 154 + SignalR/Scripts/jquery-1.6.2-vsdoc.js | 6987 ++++++++++ SignalR/Scripts/jquery-1.6.2.js | 8981 ++++++++++++ SignalR/Scripts/signalR.js | 376 + SignalR/Scripts/signalR.js.orig | 312 + SignalR/SignalBuses/ConfigPeerUrlSource.cs | 18 + SignalR/SignalBuses/IPeerUrlSource.cs | 7 + .../SignalBuses/PeerToPeerHttpSignalBus.cs | 132 + .../PeerToPeerHttpSignalBusAppStart.cs | 11 + SignalR/SignalBuses/SignalReceiverHandler.cs | 49 + SignalR/SignalBuses/SignalReceiverModule.cs | 19 + .../SignalBuses/WebRequestAsyncExtensions.cs | 14 + SignalR/SignalCommand.cs | 6 + SignalR/SignalR.csproj | 138 + SignalR/SignalResult.cs | 6 + SignalR/SignaledEventArgs.cs | 11 + SignalR/Signaler.cs | 125 + SignalR/TaskAsyncHelper.cs | 48 + SignalR/TaskAsyncHelper.cs.orig | 83 + SignalR/TaskWrapperAsyncResult.cs | 34 + SignalR/Transports/ForeverTransport.cs | 64 + SignalR/Transports/ITrackingDisconnect.cs | 7 + SignalR/Transports/ITransport.cs | 13 + SignalR/Transports/LongPollingTransport.cs | 128 + SignalR/Transports/TransportHeartBeat.cs | 57 + SignalR/Transports/TransportManager.cs | 33 + SignalR/Web/HttpTaskAsyncHandler.cs | 25 + SignalR/packages.config | 4 + lib/AjaxMin.exe | Bin 0 -> 319488 bytes lib/Microsoft.Web.Infrastructure.dll | Bin 0 -> 45416 bytes 175 files changed, 53416 insertions(+) create mode 100644 .gitignore create mode 100644 .nuget/NuGet.exe create mode 100644 .nuget/NuGet.settings.targets create mode 100644 .nuget/NuGet.targets create mode 100644 SignalR.SQLServer/IJsonSerializer.cs create mode 100644 SignalR.SQLServer/JavaScriptSerializerAdapter.cs create mode 100644 SignalR.SQLServer/PreApplicationStart.cs create mode 100644 SignalR.SQLServer/Properties/AssemblyInfo.cs create mode 100644 SignalR.SQLServer/SQLMessageStore.cs create mode 100644 SignalR.SQLServer/SQLQueryNotificationsSignalBus.cs create mode 100644 SignalR.SQLServer/SignalR.SQLServer.csproj create mode 100644 SignalR.SQLServer/SignalRMessagesTable.sql create mode 100644 SignalR.SQLServer/SignalRSignalsTable.sql create mode 100644 SignalR.SQLServer/SqlAsyncExtensions.cs create mode 100644 SignalR.Samples/App_Start/Startup.cs create mode 100644 SignalR.Samples/Content/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100644 SignalR.Samples/Content/themes/base/images/ui-bg_flat_75_ffffff_40x100.png create mode 100644 SignalR.Samples/Content/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png create mode 100644 SignalR.Samples/Content/themes/base/images/ui-bg_glass_65_ffffff_1x400.png create mode 100644 SignalR.Samples/Content/themes/base/images/ui-bg_glass_75_dadada_1x400.png create mode 100644 SignalR.Samples/Content/themes/base/images/ui-bg_glass_75_e6e6e6_1x400.png create mode 100644 SignalR.Samples/Content/themes/base/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100644 SignalR.Samples/Content/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png create mode 100644 SignalR.Samples/Content/themes/base/images/ui-icons_222222_256x240.png create mode 100644 SignalR.Samples/Content/themes/base/images/ui-icons_2e83ff_256x240.png create mode 100644 SignalR.Samples/Content/themes/base/images/ui-icons_454545_256x240.png create mode 100644 SignalR.Samples/Content/themes/base/images/ui-icons_888888_256x240.png create mode 100644 SignalR.Samples/Content/themes/base/images/ui-icons_cd0a0a_256x240.png create mode 100644 SignalR.Samples/Content/themes/base/jquery.ui.accordion.css create mode 100644 SignalR.Samples/Content/themes/base/jquery.ui.all.css create mode 100644 SignalR.Samples/Content/themes/base/jquery.ui.autocomplete.css create mode 100644 SignalR.Samples/Content/themes/base/jquery.ui.base.css create mode 100644 SignalR.Samples/Content/themes/base/jquery.ui.button.css create mode 100644 SignalR.Samples/Content/themes/base/jquery.ui.core.css create mode 100644 SignalR.Samples/Content/themes/base/jquery.ui.datepicker.css create mode 100644 SignalR.Samples/Content/themes/base/jquery.ui.dialog.css create mode 100644 SignalR.Samples/Content/themes/base/jquery.ui.progressbar.css create mode 100644 SignalR.Samples/Content/themes/base/jquery.ui.resizable.css create mode 100644 SignalR.Samples/Content/themes/base/jquery.ui.selectable.css create mode 100644 SignalR.Samples/Content/themes/base/jquery.ui.slider.css create mode 100644 SignalR.Samples/Content/themes/base/jquery.ui.tabs.css create mode 100644 SignalR.Samples/Content/themes/base/jquery.ui.theme.css create mode 100644 SignalR.Samples/Content/themes/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100644 SignalR.Samples/Content/themes/smoothness/images/ui-bg_flat_75_ffffff_40x100.png create mode 100644 SignalR.Samples/Content/themes/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png create mode 100644 SignalR.Samples/Content/themes/smoothness/images/ui-bg_glass_65_ffffff_1x400.png create mode 100644 SignalR.Samples/Content/themes/smoothness/images/ui-bg_glass_75_dadada_1x400.png create mode 100644 SignalR.Samples/Content/themes/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png create mode 100644 SignalR.Samples/Content/themes/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100644 SignalR.Samples/Content/themes/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png create mode 100644 SignalR.Samples/Content/themes/smoothness/images/ui-icons_222222_256x240.png create mode 100644 SignalR.Samples/Content/themes/smoothness/images/ui-icons_2e83ff_256x240.png create mode 100644 SignalR.Samples/Content/themes/smoothness/images/ui-icons_454545_256x240.png create mode 100644 SignalR.Samples/Content/themes/smoothness/images/ui-icons_888888_256x240.png create mode 100644 SignalR.Samples/Content/themes/smoothness/images/ui-icons_cd0a0a_256x240.png create mode 100644 SignalR.Samples/Content/themes/smoothness/jquery-ui-1.8.12.css create mode 100644 SignalR.Samples/Hubs/Chat/Chat.cs create mode 100644 SignalR.Samples/Hubs/Chat/Chat.css create mode 100644 SignalR.Samples/Hubs/Chat/Chat.js create mode 100644 SignalR.Samples/Hubs/Chat/ContentProviders/CollegeHumorContentProvider.cs create mode 100644 SignalR.Samples/Hubs/Chat/ContentProviders/EmbedContentProvider.cs create mode 100644 SignalR.Samples/Hubs/Chat/ContentProviders/IContentProvider.cs create mode 100644 SignalR.Samples/Hubs/Chat/ContentProviders/ImageContentProvider.cs create mode 100644 SignalR.Samples/Hubs/Chat/ContentProviders/YoutubeContentProvider.cs create mode 100644 SignalR.Samples/Hubs/Chat/index.htm create mode 100644 SignalR.Samples/Hubs/DemoHub/DemoHub.cs create mode 100644 SignalR.Samples/Hubs/DemoHub/DemoHub.js create mode 100644 SignalR.Samples/Hubs/DemoHub/index.htm create mode 100644 SignalR.Samples/Hubs/MouseTracking/MouseTracking.cs create mode 100644 SignalR.Samples/Hubs/MouseTracking/MouseTracking.css create mode 100644 SignalR.Samples/Hubs/MouseTracking/MouseTracking.js create mode 100644 SignalR.Samples/Hubs/MouseTracking/images/wand.png create mode 100644 SignalR.Samples/Hubs/MouseTracking/index.htm create mode 100644 SignalR.Samples/Hubs/ShapeShare/ShapeShare.cs create mode 100644 SignalR.Samples/Hubs/ShapeShare/ShapeShare.css create mode 100644 SignalR.Samples/Hubs/ShapeShare/ShapeShare.js create mode 100644 SignalR.Samples/Hubs/ShapeShare/index.htm create mode 100644 SignalR.Samples/Performance/Performance.ashx create mode 100644 SignalR.Samples/Performance/Performance.ashx.cs create mode 100644 SignalR.Samples/Performance/index.html create mode 100644 SignalR.Samples/Properties/AssemblyInfo.cs create mode 100644 SignalR.Samples/Raw/Raw.ashx create mode 100644 SignalR.Samples/Raw/Raw.ashx.cs create mode 100644 SignalR.Samples/Raw/index.htm create mode 100644 SignalR.Samples/Scripts/SignalR.js create mode 100644 SignalR.Samples/Scripts/SignalR.min.js create mode 100644 SignalR.Samples/Scripts/jQuery.tmpl.js create mode 100644 SignalR.Samples/Scripts/jQuery.tmpl.min.js create mode 100644 SignalR.Samples/Scripts/jquery-1.6.2-vsdoc.js create mode 100644 SignalR.Samples/Scripts/jquery-1.6.2.js create mode 100644 SignalR.Samples/Scripts/jquery-1.6.2.min.js create mode 100644 SignalR.Samples/Scripts/jquery-ui-1.8.13.js create mode 100644 SignalR.Samples/Scripts/jquery-ui-1.8.13.min.js create mode 100644 SignalR.Samples/Scripts/jquery.cookie.js create mode 100644 SignalR.Samples/Scripts/json2.js create mode 100644 SignalR.Samples/Scripts/json2.min.js create mode 100644 SignalR.Samples/SignalR.Samples.csproj create mode 100644 SignalR.Samples/Streaming/Streaming.ashx create mode 100644 SignalR.Samples/Streaming/Streaming.ashx.cs create mode 100644 SignalR.Samples/Streaming/index.html create mode 100644 SignalR.Samples/Web.Debug.config create mode 100644 SignalR.Samples/Web.Release.config create mode 100644 SignalR.Samples/Web.config create mode 100644 SignalR.Samples/packages.config create mode 100644 SignalR.sln create mode 100644 SignalR/CommandType.cs create mode 100644 SignalR/Connection.cs create mode 100644 SignalR/Hubs/ActionInfo.cs create mode 100644 SignalR/Hubs/ClientAgent.cs create mode 100644 SignalR/Hubs/DefaultActionResolver.cs create mode 100644 SignalR/Hubs/DefaultHubActivator.cs create mode 100644 SignalR/Hubs/DefaultHubFactory.cs create mode 100644 SignalR/Hubs/DefaultHubLocator.cs create mode 100644 SignalR/Hubs/DefaultJavaScriptProxyGenerator.cs create mode 100644 SignalR/Hubs/Hub.cs create mode 100644 SignalR/Hubs/HubContext.cs create mode 100644 SignalR/Hubs/HubDispatcher.cs create mode 100644 SignalR/Hubs/HubModule.cs create mode 100644 SignalR/Hubs/IActionResolver.cs create mode 100644 SignalR/Hubs/IClientAgent.cs create mode 100644 SignalR/Hubs/IHubActivator.cs create mode 100644 SignalR/Hubs/IHubFactory.cs create mode 100644 SignalR/Hubs/IHubLocator.cs create mode 100644 SignalR/Hubs/IJavaScriptProxyGenerator.cs create mode 100644 SignalR/Hubs/SignalAgent.cs create mode 100644 SignalR/Hubs/TrackingDictionary.cs create mode 100644 SignalR/IConnection.cs create mode 100644 SignalR/IGroupManager.cs create mode 100644 SignalR/IJsonStringifier.cs create mode 100644 SignalR/IMessageStore.cs create mode 100644 SignalR/ISignalBus.cs create mode 100644 SignalR/InProcessMessageStore.cs create mode 100644 SignalR/InProcessSignalBus.cs create mode 100644 SignalR/Infrastructure/DependencyResolver.cs create mode 100644 SignalR/Infrastructure/IDependencyResolver.cs create mode 100644 SignalR/Infrastructure/SafeSet.cs create mode 100644 SignalR/JavaScriptSerializerAdapter.cs create mode 100644 SignalR/Json.cs create mode 100644 SignalR/Message.cs create mode 100644 SignalR/PersistentConnection.cs create mode 100644 SignalR/PersistentResponse.cs create mode 100644 SignalR/PreApplicationStart.cs create mode 100644 SignalR/Properties/AssemblyInfo.cs create mode 100644 SignalR/Properties/SignalR.nuspec create mode 100644 SignalR/Scripts/SignalR.min.js create mode 100644 SignalR/Scripts/hubs.js create mode 100644 SignalR/Scripts/jquery-1.6.2-vsdoc.js create mode 100644 SignalR/Scripts/jquery-1.6.2.js create mode 100644 SignalR/Scripts/signalR.js create mode 100644 SignalR/Scripts/signalR.js.orig create mode 100644 SignalR/SignalBuses/ConfigPeerUrlSource.cs create mode 100644 SignalR/SignalBuses/IPeerUrlSource.cs create mode 100644 SignalR/SignalBuses/PeerToPeerHttpSignalBus.cs create mode 100644 SignalR/SignalBuses/PeerToPeerHttpSignalBusAppStart.cs create mode 100644 SignalR/SignalBuses/SignalReceiverHandler.cs create mode 100644 SignalR/SignalBuses/SignalReceiverModule.cs create mode 100644 SignalR/SignalBuses/WebRequestAsyncExtensions.cs create mode 100644 SignalR/SignalCommand.cs create mode 100644 SignalR/SignalR.csproj create mode 100644 SignalR/SignalResult.cs create mode 100644 SignalR/SignaledEventArgs.cs create mode 100644 SignalR/Signaler.cs create mode 100644 SignalR/TaskAsyncHelper.cs create mode 100644 SignalR/TaskAsyncHelper.cs.orig create mode 100644 SignalR/TaskWrapperAsyncResult.cs create mode 100644 SignalR/Transports/ForeverTransport.cs create mode 100644 SignalR/Transports/ITrackingDisconnect.cs create mode 100644 SignalR/Transports/ITransport.cs create mode 100644 SignalR/Transports/LongPollingTransport.cs create mode 100644 SignalR/Transports/TransportHeartBeat.cs create mode 100644 SignalR/Transports/TransportManager.cs create mode 100644 SignalR/Web/HttpTaskAsyncHandler.cs create mode 100644 SignalR/packages.config create mode 100644 lib/AjaxMin.exe create mode 100644 lib/Microsoft.Web.Infrastructure.dll diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..5a45732bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +[Oo]bj/ +[Bb]in/ +*.user +/TestResults +*.vspscc +*.vssscc +deploy +deploy/* +*.suo +*.cache +*.docstates +_ReSharper.* +*.csproj.user +*[Rr]e[Ss]harper.user +_ReSharper.*/ +packages/* +Kudu.Services.Web/App_Data/* \ No newline at end of file diff --git a/.nuget/NuGet.exe b/.nuget/NuGet.exe new file mode 100644 index 0000000000000000000000000000000000000000..7259c19503adc24cb2beba2d500fbec5a9b2305f GIT binary patch literal 411648 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 Grbl_Rd1fh}_lj)v$ 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 literal 0 HcmV?d00001 diff --git a/.nuget/NuGet.settings.targets b/.nuget/NuGet.settings.targets new file mode 100644 index 000000000..23c0a3181 --- /dev/null +++ b/.nuget/NuGet.settings.targets @@ -0,0 +1,41 @@ + + + + $(MSBuildProjectDirectory)\..\ + $(SolutionDir).nuget + $(NuGetToolsPath)\nuget.exe + $(ProjectDir)packages.config + $(SolutionDir)packages + $(TargetDir.Trim('\\')) + + + "" + + + false + + + false + + + "$(NuGetExePath)" install "$(PackagesConfig)" -source $(PackageSources) -o "$(PackagesDir)" + "$(NuGetExePath)" pack "$(ProjectPath)" -p Configuration=$(Configuration) -o "$(PackageOutputDir)" + + + + RestorePackages; + $(BuildDependsOn); + + + + + $(BuildDependsOn); + BuildPackage; + + + + + + + + \ No newline at end of file diff --git a/.nuget/NuGet.targets b/.nuget/NuGet.targets new file mode 100644 index 000000000..a7cd1ef6a --- /dev/null +++ b/.nuget/NuGet.targets @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/SignalR.SQLServer/IJsonSerializer.cs b/SignalR.SQLServer/IJsonSerializer.cs new file mode 100644 index 000000000..614c7460a --- /dev/null +++ b/SignalR.SQLServer/IJsonSerializer.cs @@ -0,0 +1,11 @@ +using System; + +namespace SignalR { + public interface IJsonSerializer : IJsonStringifier { + object Parse(string json); + + object Parse(string json, Type targetType); + + T Parse(string json); + } +} \ No newline at end of file diff --git a/SignalR.SQLServer/JavaScriptSerializerAdapter.cs b/SignalR.SQLServer/JavaScriptSerializerAdapter.cs new file mode 100644 index 000000000..02e03f949 --- /dev/null +++ b/SignalR.SQLServer/JavaScriptSerializerAdapter.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using SignalR; +using System.Web.Script.Serialization; + +namespace SignalR.SQL { + internal class JavaScriptSerializerAdapter : IJsonSerializer { + private JavaScriptSerializer _serializer; + + public JavaScriptSerializerAdapter(JavaScriptSerializer serializer) { + _serializer = serializer; + } + + public string Stringify(object obj) { + return _serializer.Serialize(obj); + } + + public object Parse(string json) { + return _serializer.DeserializeObject(json); + } + + public object Parse(string json, Type targetType) { + return _serializer.Deserialize(json, targetType); + } + + public T Parse(string json) { + return _serializer.Deserialize(json); + } + } +} \ No newline at end of file diff --git a/SignalR.SQLServer/PreApplicationStart.cs b/SignalR.SQLServer/PreApplicationStart.cs new file mode 100644 index 000000000..301fdabc8 --- /dev/null +++ b/SignalR.SQLServer/PreApplicationStart.cs @@ -0,0 +1,19 @@ +using System.Web; +using System.Web.Script.Serialization; +using SignalR.Infrastructure; +using SignalR.SQL; + +[assembly: PreApplicationStartMethod(typeof(PreApplicationStart), "Start")] + +namespace SignalR.SQL { + public static class PreApplicationStart { + + public static void Start() { + var serializer = new JavaScriptSerializerAdapter(new JavaScriptSerializer { + MaxJsonLength = 30 * 1024 * 1024 + }); + DependencyResolver.Register(typeof(IJsonSerializer), () => serializer); + } + + } +} \ No newline at end of file diff --git a/SignalR.SQLServer/Properties/AssemblyInfo.cs b/SignalR.SQLServer/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..fb8355b7e --- /dev/null +++ b/SignalR.SQLServer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SignalR.SQLServer")] +[assembly: AssemblyDescription("SQL Server signal bus and message stores for SignalR")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("SignalR.SQLServer")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("8565e3ac-fd4c-4bb6-b6f2-3814ac0a9a32")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SignalR.SQLServer/SQLMessageStore.cs b/SignalR.SQLServer/SQLMessageStore.cs new file mode 100644 index 000000000..731b33bd7 --- /dev/null +++ b/SignalR.SQLServer/SQLMessageStore.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Data.SqlClient; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using SignalR.Infrastructure; + +namespace SignalR { + public class SQLMessageStore : IMessageStore { + private static readonly string _getLastIdSQL = "SELECT MAX([MessageId]) FROM {TableName}"; + + private static readonly string _saveSQL = "INSERT INTO {TableName} (EventKey, SmallValue, BigValue, Created) " + + "VALUES (@EventKey, @SmallValue, @BigValue, GETDATE())"; + + private static readonly string _getAllSQL = "SELECT [MessageId], COALESCE([SmallValue],[BigValue]) as [Value], [Created], [EventKey] " + + "FROM {TableName} " + + "WHERE [EventKey] = @EventKey "; + + private static readonly string _getAllSinceSQL = _getAllSQL + + "AND [MessageId] > @MessageId"; + + private static readonly string _getAllSinceMultiEventKeysSQL = "SELECT [MessageId], COALESCE([SmallValue],[BigValue]) as [Value], [Created], [EventKey] " + + "FROM {TableName} m " + + " INNER JOIN [dbo].[SignalR_charlist_to_table](@EventKey, ',') k " + + " ON m.[EventKey] = k.[nstr] " + + "WHERE m.[MessageId] > @MessageId"; + + // Interval to wait before cleaning up old queries + private static readonly TimeSpan _cleanupInterval = TimeSpan.FromSeconds(10); + + private readonly ConcurrentDictionary, Task>> _queries = new ConcurrentDictionary, Task>>(); + + private readonly Timer _timer; + + public SQLMessageStore(string connectionString) { + if (String.IsNullOrEmpty(connectionString)) { + throw new ArgumentNullException("connectionString"); + } + + ConnectionString = connectionString; + MessageTableName = "[dbo].[SignalRMessages]"; + + _timer = new Timer(RemoveOldQueries, null, _cleanupInterval, _cleanupInterval); + } + + private IJsonSerializer Json { + get { + var json = DependencyResolver.Resolve(); + if (json == null) { + throw new InvalidOperationException("No implementation of IJsonSerializer is registered."); + } + return json; + } + } + + public virtual string ConnectionString { get; private set; } + public virtual string MessageTableName { get; set; } + + public Task GetLastId() { + var connection = CreateAndOpenConnection(); + var transaction = connection.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted); + var cmd = new SqlCommand(_getLastIdSQL.Replace("{TableName}", MessageTableName), connection, transaction); + + return cmd.ExecuteScalarAsync() + .ContinueWith(t => { + connection.Close(); + return t.Result; + }); + } + + public Task Save(string key, object value) { + var connection = CreateAndOpenConnection(); + var cmd = new SqlCommand(_saveSQL.Replace("{TableName}", MessageTableName), connection); + var json = Json.Stringify(value); + cmd.Parameters.AddWithValue("EventKey", key); + if (json.Length <= 2000) { + cmd.Parameters.AddWithValue("SmallValue", json); + cmd.Parameters.AddWithValue("BigValue", DBNull.Value); + } + else { + cmd.Parameters.AddWithValue("SmallValue", DBNull.Value); + cmd.Parameters.AddWithValue("BigValue", json); + } + return cmd.ExecuteNonQueryAsync() + .ContinueWith(t => { + connection.Close(); + }); + } + + public Task> GetAll(string key) { + return GetMessages(key, _getAllSQL.Replace("{TableName}", MessageTableName), + new [] { new SqlParameter("EventKey", key) } + ); + } + + public Task> GetAllSince(string key, long id) { + return _queries.GetOrAdd(Tuple.Create(id, key), + GetMessages(key, _getAllSinceSQL.Replace("{TableName}", MessageTableName), + new[] { + new SqlParameter("EventKey", key), + new SqlParameter("MessageId", id) + } + ).ContinueWith(t => { + if (t.Exception != null || !t.Result.Any()) { + // Remove from queries + Task> removedQuery; + _queries.TryRemove(Tuple.Create(id, key), out removedQuery); + } + return t.Result; + }) + ); + } + + private Task> GetMessages(string key, string sql, SqlParameter[] parameters) { + var connection = CreateAndOpenConnection(); + var transaction = connection.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted); + var cmd = new SqlCommand(sql, connection, transaction); + cmd.Parameters.AddRange(parameters); + return cmd.ExecuteReaderAsync() + .ContinueWith>(t => { + var rdr = t.Result; + var messages = new List(); + while (rdr.Read()) { + messages.Add(new Message( + signalKey: key, + id: rdr.GetInt64(0), + value: Json.Parse(rdr.GetString(1)), + created: rdr.GetDateTime(2) + )); + } + connection.Close(); + return messages; + }); + } + + private SqlConnection CreateAndOpenConnection() { + var connection = new SqlConnection(ConnectionString); + connection.Open(); + return connection; + } + + private void RemoveOldQueries(object state) { + // Take a snapshot of the queries + var queries = _queries.ToList(); + + // Remove all the expired ones + foreach (var query in queries) { + if (query.Value.IsCompleted) { + if (query.Value.Result.All(m => m.Expired)) { + Task> removed; + _queries.TryRemove(query.Key, out removed); + } + } + } + } + } +} \ No newline at end of file diff --git a/SignalR.SQLServer/SQLQueryNotificationsSignalBus.cs b/SignalR.SQLServer/SQLQueryNotificationsSignalBus.cs new file mode 100644 index 000000000..c76630810 --- /dev/null +++ b/SignalR.SQLServer/SQLQueryNotificationsSignalBus.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Data.SqlClient; +using System.Security.Permissions; +using System.Threading.Tasks; + +namespace SignalR { + /// + /// A signaler that uses SQL Server and Query Notifications to send signals between app-domains + /// + public class SQLQueryNotificationsSignalBus : ISignalBus { + private static readonly string _getSignalsSQL = "SELECT [SignalID], [EventKey], [LastSignaledAt] " + + "FROM {TableName} " + + "WHERE [LastSignaledAt] > @LastSignaledAt"; + + private static readonly string _upsertSignalSQL = "MERGE dbo.[SignalRSignals] WITH (HOLDLOCK) AS s " + + "USING ( SELECT @EventKey AS EventKey ) AS existing_Signal " + + " ON s.EventKey = existing_Signal.EventKey " + + "WHEN MATCHED THEN " + + " UPDATE " + + " SET s.LastSignaledAt = SYSDATETIME() " + + "WHEN NOT MATCHED THEN " + + " INSERT ( EventKey, LastSignaledAt ) " + + " VALUES ( @EventKey, SYSDATETIME() );"; + + private DateTime _lastSignaledAt = System.Data.SqlTypes.SqlDateTime.MinValue.Value; + private bool _sqlDependencyListenerStarted = false; + private object _ensureSqlDependencyListeningLocker = new object(); + private object _ensureSqlConnectionLocker = new object(); + + public SQLQueryNotificationsSignalBus(string connectionString) { + if (String.IsNullOrEmpty(connectionString)) { + throw new ArgumentNullException("connectionString"); + } + + ConnectionString = connectionString; + SignalTableName = "[dbo].[SignalRSignals]"; + StartListeningForQueryNotification(); + } + + public virtual string ConnectionString { get; private set; } + public virtual string SignalTableName { get; set; } + + public event EventHandler Signaled; + private void OnSignaled(string eventKey) { + if (Signaled != null) { + Signaled(this, new SignaledEventArgs(eventKey)); + } + } + + public virtual Task Signal(string eventKey) { + return InsertUpdateSignal(eventKey); + + // Maybe we can immediately raise event here too + // and prevent same signal from SQL QN from propagating? + } + + // TODO: Not sure this is even needed, or we can do it automatically + public void StopSqlDependencyListener() { + if (_sqlDependencyListenerStarted) { + lock (_ensureSqlDependencyListeningLocker) { + if (_sqlDependencyListenerStarted) { + SqlDependency.Stop(ConnectionString); + } + } + } + } + + protected virtual Task InsertUpdateSignal(string eventKey) { + var connection = CreateAndOpenConnection(); + var command = connection.CreateCommand(); + // UPSERT statement (http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx) + command.CommandText = _upsertSignalSQL.Replace("{TableName}", SignalTableName); + command.Parameters.AddWithValue("@EventKey", eventKey); + return Task.Factory.FromAsync(command.BeginExecuteNonQuery, command.EndExecuteNonQuery, null) + .ContinueWith(_ => connection.Close()); + } + + private void StartListeningForQueryNotification() { + EnsureSqlDependencyListening(); + var connection = CreateAndOpenConnection(); + var command = BuildQueryCommand(connection); + var sd = new SqlDependency(command); + sd.OnChange += (sender, e) => { + ProcessSignalReceived(); + StartListeningForQueryNotification(); + }; + Task.Factory.FromAsync(command.BeginExecuteReader, command.EndExecuteReader, null) + .ContinueWith(_ => connection.Close()); + } + + private void ProcessSignalReceived() { + // Get event keys for signals since last signal received and update last signal ID + var connection = CreateAndOpenConnection(); + var command = BuildQueryCommand(connection); + var lastSignaledAtInResult = System.Data.SqlTypes.SqlDateTime.MinValue.Value; + var eventKeys = new HashSet(); + + Task.Factory.FromAsync(command.BeginExecuteReader, command.EndExecuteReader, null) + .ContinueWith(t => { + var rdr = t.Result; + while (rdr.Read()) { + //lastSignalIdInResult = rdr.GetInt32(0); + if (rdr.GetDateTime(2) > lastSignaledAtInResult) { + lastSignaledAtInResult = rdr.GetDateTime(2); + } + eventKeys.Add(rdr.GetString(1)); + } + rdr.Close(); + + foreach (var key in eventKeys) { + OnSignaled(key); + } + _lastSignaledAt = lastSignaledAtInResult; + connection.Close(); + }); + } + + private SqlCommand BuildQueryCommand(SqlConnection connection) { + var command = connection.CreateCommand(); + command.CommandText = _getSignalsSQL.Replace("{TableName}", SignalTableName); + //command.Parameters.AddWithValue("LastSignalID", _lastSignalID); + command.Parameters.AddWithValue("LastSignaledAt", _lastSignaledAt); + return command; + } + + private void EnsureSqlDependencyListening() { + if (!_sqlDependencyListenerStarted) { + lock (_ensureSqlDependencyListeningLocker) { + if (!_sqlDependencyListenerStarted) { + SqlDependency.Start(ConnectionString); + var perm = new SqlClientPermission(PermissionState.Unrestricted); + perm.Demand(); + _sqlDependencyListenerStarted = true; + } + } + } + } + + private SqlConnection CreateAndOpenConnection() { + var connection = new SqlConnection(ConnectionString); + connection.Open(); + return connection; + } + + public void AddHandler(string eventKey, EventHandler handler) { + Signaled += handler; + } + + public void RemoveHandler(string eventKey, EventHandler handler) { + Signaled -= handler; + } + } +} \ No newline at end of file diff --git a/SignalR.SQLServer/SignalR.SQLServer.csproj b/SignalR.SQLServer/SignalR.SQLServer.csproj new file mode 100644 index 000000000..a746e5e26 --- /dev/null +++ b/SignalR.SQLServer/SignalR.SQLServer.csproj @@ -0,0 +1,75 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {32D16B36-970E-4CF2-B954-78CB1833CBC1} + Library + Properties + SignalR + SignalR.SQLServer + v4.0 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + {1B9A82C4-BCA1-4834-A33E-226F17BE070B} + SignalR + + + + + + + + \ No newline at end of file diff --git a/SignalR.SQLServer/SignalRMessagesTable.sql b/SignalR.SQLServer/SignalRMessagesTable.sql new file mode 100644 index 000000000..72833d7f0 --- /dev/null +++ b/SignalR.SQLServer/SignalRMessagesTable.sql @@ -0,0 +1,44 @@ +SET ANSI_NULLS ON +GO + +SET QUOTED_IDENTIFIER ON +GO + +/****** Object: Table [dbo].[SignalRMessages] ******/ +CREATE TABLE [dbo].[SignalRMessages]( + [MessageID] [bigint] IDENTITY(1,1) NOT NULL, + [EventKey] [nvarchar](400) NOT NULL, + [SmallValue] [nvarchar](4000) NULL, + [BigValue] [nvarchar](max) NULL, + [Created] [datetime] NOT NULL, + CONSTRAINT [PK_SignalRMessages] PRIMARY KEY CLUSTERED +( + [MessageID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] + +GO + +/****** Object: Index [IX_SignalRMessages_EventKey] ******/ +CREATE NONCLUSTERED INDEX [IX_SignalRMessages_EventKey] ON [dbo].[SignalRMessages] +( + [EventKey] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +GO + +/****** Object: Trigger [SignalR_DeleteExpiredMessages] ******/ +CREATE TRIGGER [dbo].[SignalR_DeleteExpiredMessages] + ON [dbo].[SignalRMessages] + AFTER INSERT +AS +IF (SELECT MessageID FROM Inserted) % 5000 = 0 -- Trigger every 5000th insert +BEGIN + -- SET NOCOUNT ON added to prevent extra result sets from + -- interfering with SELECT statements. + SET NOCOUNT ON; + + -- Delete messages older than 60 seconds + DELETE FROM [dbo].[SignalRMessages] + WHERE DATEDIFF(second, [Created], GETDATE()) > 60 +END +GO \ No newline at end of file diff --git a/SignalR.SQLServer/SignalRSignalsTable.sql b/SignalR.SQLServer/SignalRSignalsTable.sql new file mode 100644 index 000000000..fc379fd04 --- /dev/null +++ b/SignalR.SQLServer/SignalRSignalsTable.sql @@ -0,0 +1,32 @@ +/****** Object: Table [dbo].[SignalRSignals] Script Date: 05/11/2011 18:18:37 ******/ +SET ANSI_NULLS ON +GO + +SET QUOTED_IDENTIFIER ON +GO + +CREATE TABLE [dbo].[SignalRSignals]( + [SignalID] [int] IDENTITY(1,1) NOT NULL, + [EventKey] [nvarchar](4000) NOT NULL, + [LastSignaledAt] [datetime] NOT NULL, + CONSTRAINT [PK_SignalRSignals] PRIMARY KEY CLUSTERED +( + [SignalID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] + +GO + +/****** Object: Index [IX_SignalRSignals_EventKey] Script Date: 05/11/2011 18:18:53 ******/ +CREATE UNIQUE NONCLUSTERED INDEX [IX_SignalRSignals_EventKey] ON [dbo].[SignalRSignals] +( + [EventKey] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +GO + +/****** Object: Index [IX_SignalRSignals_LastSignaledAt] Script Date: 05/11/2011 18:19:08 ******/ +CREATE NONCLUSTERED INDEX [IX_SignalRSignals_LastSignaledAt] ON [dbo].[SignalRSignals] +( + [LastSignaledAt] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +GO \ No newline at end of file diff --git a/SignalR.SQLServer/SqlAsyncExtensions.cs b/SignalR.SQLServer/SqlAsyncExtensions.cs new file mode 100644 index 000000000..fa7e6a11d --- /dev/null +++ b/SignalR.SQLServer/SqlAsyncExtensions.cs @@ -0,0 +1,33 @@ +using System.Data.SqlClient; +using System.Threading.Tasks; +using System; + +namespace SignalR { + internal static class SqlAsyncExtensions { + internal static Task ExecuteReaderAsync(this SqlCommand command) { + return Task.Factory.FromAsync(command.BeginExecuteReader, command.EndExecuteReader, null); + } + + internal static Task ExecuteNonQueryAsync(this SqlCommand command) { + return Task.Factory.FromAsync(command.BeginExecuteNonQuery, command.EndExecuteNonQuery, null); + } + + internal static Task ExecuteScalarAsync(this SqlCommand command) { + return command.ExecuteReaderAsync() + .ContinueWith(t => { + var rdr = t.Result; + object result = null; + if (rdr.Read()) { + result = rdr.IsDBNull(0) ? null : rdr[0]; + } + return result; + }); + } + + internal static Task ExecuteScalarAsync(this SqlCommand command) { + Type type = Nullable.GetUnderlyingType(typeof(TResult)) ?? typeof(TResult); + return command.ExecuteScalarAsync() + .ContinueWith(t => (TResult)Convert.ChangeType(t.Result, type)); + } + } +} \ No newline at end of file diff --git a/SignalR.Samples/App_Start/Startup.cs b/SignalR.Samples/App_Start/Startup.cs new file mode 100644 index 000000000..da46962e1 --- /dev/null +++ b/SignalR.Samples/App_Start/Startup.cs @@ -0,0 +1,31 @@ +using System; +using System.Configuration; +using System.Threading; +using SignalR.Infrastructure; +using SignalR.Samples.App_Start; +using SignalR.SignalBuses; + +[assembly: WebActivator.PreApplicationStartMethod(typeof(Startup), "Start")] + +namespace SignalR.Samples.App_Start { + public class Startup { + public static void Start() { + + // Uncomment this for web farm support + //var signalBus = new PeerToPeerHttpSignalBus(); + //var cs = ConfigurationManager.ConnectionStrings["SignalR"].ConnectionString; + //var messageStore = new SQLMessageStore(cs); + //DependencyResolver.Register(typeof(ISignalBus), () => signalBus); + //DependencyResolver.Register(typeof(IMessageStore), () => messageStore); + + ThreadPool.QueueUserWorkItem(_ => { + var connection = Connection.GetConnection(); + + while (true) { + connection.Broadcast(DateTime.Now.ToString()); + Thread.Sleep(2000); + } + }); + } + } +} \ No newline at end of file diff --git a/SignalR.Samples/Content/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png b/SignalR.Samples/Content/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png new file mode 100644 index 0000000000000000000000000000000000000000..5b5dab2ab7b1c50dea9cfe73dc5a269a92d2d4b4 GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^8bF-F!3HG1q!d*FscKIb$B>N1x91EQ4=4yQ7#`R^ z$vje}bP0l+XkK DSH>_4 literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/themes/base/images/ui-bg_flat_75_ffffff_40x100.png b/SignalR.Samples/Content/themes/base/images/ui-bg_flat_75_ffffff_40x100.png new file mode 100644 index 0000000000000000000000000000000000000000..ac8b229af950c29356abf64a6c4aa894575445f0 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^8bF-F!3HG1q!d*FsY*{5$B>N1x91EQ4=4yQYz+E8 zPo9&<{J;c_6SHRil>2s{Zw^OT)6@jj2u|u!(plXsM>LJD`vD!n;OXk;vd$@?2>^GI BH@yG= literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png b/SignalR.Samples/Content/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..ad3d6346e00f246102f72f2e026ed0491988b394 GIT binary patch literal 120 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjgAK^akKnour0hLi978O6-<~(*I$*%ybaDOn z{W;e!B}_MSUQoPXhYd^Y6RUoS1yepnPx`2Kz)7OXQG!!=-jY=F+d2OOy?#DnJ32>z UEim$g7SJdLPgg&ebxsLQ09~*s;{X5v literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/themes/base/images/ui-bg_glass_65_ffffff_1x400.png b/SignalR.Samples/Content/themes/base/images/ui-bg_glass_65_ffffff_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..42ccba269b6e91bef12ad0fa18be651b5ef0ee68 GIT binary patch literal 105 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjgAK^akKnouqzpV=978O6-=0?FV^9z|eBtf= z|7WztIJ;WT>{+tN>ySr~=F{k$>;_x^_y?afmf9pRKH0)6?eSP?3s5hEr>mdKI;Vst E0O;M1& literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/themes/base/images/ui-bg_glass_75_dadada_1x400.png b/SignalR.Samples/Content/themes/base/images/ui-bg_glass_75_dadada_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..5a46b47cb16631068aee9e0bd61269fc4e95e5cd GIT binary patch literal 111 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjgAK^akKnouq|7{B978O6lPf+wIa#m9#>Unb zm^4K~wN3Zq+uP{vDV26o)#~38k_!`W=^oo1w6ixmPC4R1b Tyd6G3lNdZ*{an^LB{Ts5`idse literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/SignalR.Samples/Content/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png new file mode 100644 index 0000000000000000000000000000000000000000..7c9fa6c6edcfcdd3e5b77e6f547b719e6fc66e30 GIT binary patch literal 101 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 literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/themes/base/images/ui-icons_2e83ff_256x240.png b/SignalR.Samples/Content/themes/base/images/ui-icons_2e83ff_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..45e8928e5284adacea3f9ec07b9b50667d2ac65f GIT binary patch literal 4369 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 literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/themes/base/images/ui-icons_454545_256x240.png b/SignalR.Samples/Content/themes/base/images/ui-icons_454545_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..7ec70d11bfb2f77374dfd00ef61ba0c3647b5a0c GIT binary patch literal 4369 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 k*Kv;w;*%(i9W@fAqs5i2wiq literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/themes/base/images/ui-icons_888888_256x240.png b/SignalR.Samples/Content/themes/base/images/ui-icons_888888_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..5ba708c39172a69e069136bd1309c4322c61f571 GIT binary patch literal 4369 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 literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/themes/base/jquery.ui.accordion.css b/SignalR.Samples/Content/themes/base/jquery.ui.accordion.css new file mode 100644 index 000000000..93e128a3d --- /dev/null +++ b/SignalR.Samples/Content/themes/base/jquery.ui.accordion.css @@ -0,0 +1,19 @@ +/* + * jQuery UI Accordion 1.8.13 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Accordion#theming + */ +/* IE/Win - Fix animation bug - #4615 */ +.ui-accordion { width: 100%; } +.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; } +.ui-accordion .ui-accordion-li-fix { display: inline; } +.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; } +.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; } +.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; } +.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } +.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } +.ui-accordion .ui-accordion-content-active { display: block; } diff --git a/SignalR.Samples/Content/themes/base/jquery.ui.all.css b/SignalR.Samples/Content/themes/base/jquery.ui.all.css new file mode 100644 index 000000000..603aafd46 --- /dev/null +++ b/SignalR.Samples/Content/themes/base/jquery.ui.all.css @@ -0,0 +1,11 @@ +/* + * jQuery UI CSS Framework 1.8.13 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming + */ +@import "jquery.ui.base.css"; +@import "jquery.ui.theme.css"; diff --git a/SignalR.Samples/Content/themes/base/jquery.ui.autocomplete.css b/SignalR.Samples/Content/themes/base/jquery.ui.autocomplete.css new file mode 100644 index 000000000..d90fc300a --- /dev/null +++ b/SignalR.Samples/Content/themes/base/jquery.ui.autocomplete.css @@ -0,0 +1,53 @@ +/* + * jQuery UI Autocomplete 1.8.13 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Autocomplete#theming + */ +.ui-autocomplete { position: absolute; cursor: default; } + +/* workarounds */ +* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ + +/* + * jQuery UI Menu 1.8.13 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Menu#theming + */ +.ui-menu { + list-style:none; + padding: 2px; + margin: 0; + display:block; + float: left; +} +.ui-menu .ui-menu { + margin-top: -3px; +} +.ui-menu .ui-menu-item { + margin:0; + padding: 0; + zoom: 1; + float: left; + clear: left; + width: 100%; +} +.ui-menu .ui-menu-item a { + text-decoration:none; + display:block; + padding:.2em .4em; + line-height:1.5; + zoom:1; +} +.ui-menu .ui-menu-item a.ui-state-hover, +.ui-menu .ui-menu-item a.ui-state-active { + font-weight: normal; + margin: -1px; +} diff --git a/SignalR.Samples/Content/themes/base/jquery.ui.base.css b/SignalR.Samples/Content/themes/base/jquery.ui.base.css new file mode 100644 index 000000000..f52ee39b9 --- /dev/null +++ b/SignalR.Samples/Content/themes/base/jquery.ui.base.css @@ -0,0 +1,11 @@ +@import url("jquery.ui.core.css"); +@import url("jquery.ui.resizable.css"); +@import url("jquery.ui.selectable.css"); +@import url("jquery.ui.accordion.css"); +@import url("jquery.ui.autocomplete.css"); +@import url("jquery.ui.button.css"); +@import url("jquery.ui.dialog.css"); +@import url("jquery.ui.slider.css"); +@import url("jquery.ui.tabs.css"); +@import url("jquery.ui.datepicker.css"); +@import url("jquery.ui.progressbar.css"); \ No newline at end of file diff --git a/SignalR.Samples/Content/themes/base/jquery.ui.button.css b/SignalR.Samples/Content/themes/base/jquery.ui.button.css new file mode 100644 index 000000000..e887fb643 --- /dev/null +++ b/SignalR.Samples/Content/themes/base/jquery.ui.button.css @@ -0,0 +1,38 @@ +/* + * jQuery UI Button 1.8.13 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Button#theming + */ +.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */ +.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */ +button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */ +.ui-button-icons-only { width: 3.4em; } +button.ui-button-icons-only { width: 3.7em; } + +/*button text element */ +.ui-button .ui-button-text { display: block; line-height: 1.4; } +.ui-button-text-only .ui-button-text { padding: .4em 1em; } +.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; } +.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; } +.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; } +.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; } +/* no icon support for input elements, provide padding by default */ +input.ui-button { padding: .4em 1em; } + +/*button icon element(s) */ +.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; } +.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; } +.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; } +.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } +.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } + +/*button sets*/ +.ui-buttonset { margin-right: 7px; } +.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; } + +/* workarounds */ +button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */ diff --git a/SignalR.Samples/Content/themes/base/jquery.ui.core.css b/SignalR.Samples/Content/themes/base/jquery.ui.core.css new file mode 100644 index 000000000..ea82f08a8 --- /dev/null +++ b/SignalR.Samples/Content/themes/base/jquery.ui.core.css @@ -0,0 +1,41 @@ +/* + * jQuery UI CSS Framework 1.8.13 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { display: none; } +.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } +.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } +.ui-helper-clearfix { display: inline-block; } +/* required comment for clearfix to work in Opera \*/ +* html .ui-helper-clearfix { height:1%; } +.ui-helper-clearfix { display:block; } +/* end clearfix */ +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { cursor: default !important; } + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } diff --git a/SignalR.Samples/Content/themes/base/jquery.ui.datepicker.css b/SignalR.Samples/Content/themes/base/jquery.ui.datepicker.css new file mode 100644 index 000000000..b370444ec --- /dev/null +++ b/SignalR.Samples/Content/themes/base/jquery.ui.datepicker.css @@ -0,0 +1,68 @@ +/* + * jQuery UI Datepicker 1.8.13 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Datepicker#theming + */ +.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; } +.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } +.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } +.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } +.ui-datepicker .ui-datepicker-prev { left:2px; } +.ui-datepicker .ui-datepicker-next { right:2px; } +.ui-datepicker .ui-datepicker-prev-hover { left:1px; } +.ui-datepicker .ui-datepicker-next-hover { right:1px; } +.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } +.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } +.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } +.ui-datepicker select.ui-datepicker-month-year {width: 100%;} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { width: 49%;} +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } +.ui-datepicker td { border: 0; padding: 1px; } +.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } +.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } +.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { width:auto; } +.ui-datepicker-multi .ui-datepicker-group { float:left; } +.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } +.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } +.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } +.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } +.ui-datepicker-row-break { clear:both; width:100%; } + +/* RTL support */ +.ui-datepicker-rtl { direction: rtl; } +.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } +.ui-datepicker-rtl .ui-datepicker-group { float:right; } +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } + +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ +.ui-datepicker-cover { + display: none; /*sorry for IE5*/ + display/**/: block; /*sorry for IE5*/ + position: absolute; /*must have*/ + z-index: -1; /*must have*/ + filter: mask(); /*must have*/ + top: -4px; /*must have*/ + left: -4px; /*must have*/ + width: 200px; /*must have*/ + height: 200px; /*must have*/ +} \ No newline at end of file diff --git a/SignalR.Samples/Content/themes/base/jquery.ui.dialog.css b/SignalR.Samples/Content/themes/base/jquery.ui.dialog.css new file mode 100644 index 000000000..722203e82 --- /dev/null +++ b/SignalR.Samples/Content/themes/base/jquery.ui.dialog.css @@ -0,0 +1,21 @@ +/* + * jQuery UI Dialog 1.8.13 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Dialog#theming + */ +.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; } +.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; } +.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; } +.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; } +.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; } +.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; } +.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; } +.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; } +.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; } +.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; } +.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } +.ui-draggable .ui-dialog-titlebar { cursor: move; } diff --git a/SignalR.Samples/Content/themes/base/jquery.ui.progressbar.css b/SignalR.Samples/Content/themes/base/jquery.ui.progressbar.css new file mode 100644 index 000000000..d896b2a06 --- /dev/null +++ b/SignalR.Samples/Content/themes/base/jquery.ui.progressbar.css @@ -0,0 +1,11 @@ +/* + * jQuery UI Progressbar 1.8.13 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Progressbar#theming + */ +.ui-progressbar { height:2em; text-align: left; } +.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; } \ No newline at end of file diff --git a/SignalR.Samples/Content/themes/base/jquery.ui.resizable.css b/SignalR.Samples/Content/themes/base/jquery.ui.resizable.css new file mode 100644 index 000000000..bb0ee87d1 --- /dev/null +++ b/SignalR.Samples/Content/themes/base/jquery.ui.resizable.css @@ -0,0 +1,25 @@ +/* + * jQuery UI Resizable 1.8.13 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Resizable#theming + */ +.ui-resizable { position: relative;} +.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; + /* http://bugs.jqueryui.com/ticket/7233 + - Resizable: resizable handles fail to work in IE if transparent and content overlaps + */ + background-image:url(data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=); +} +.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } +.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; } +.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; } +.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; } +.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; } +.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } +.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } +.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } +.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;} \ No newline at end of file diff --git a/SignalR.Samples/Content/themes/base/jquery.ui.selectable.css b/SignalR.Samples/Content/themes/base/jquery.ui.selectable.css new file mode 100644 index 000000000..47db29ef7 --- /dev/null +++ b/SignalR.Samples/Content/themes/base/jquery.ui.selectable.css @@ -0,0 +1,10 @@ +/* + * jQuery UI Selectable 1.8.13 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Selectable#theming + */ +.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; } diff --git a/SignalR.Samples/Content/themes/base/jquery.ui.slider.css b/SignalR.Samples/Content/themes/base/jquery.ui.slider.css new file mode 100644 index 000000000..74b9d52f4 --- /dev/null +++ b/SignalR.Samples/Content/themes/base/jquery.ui.slider.css @@ -0,0 +1,24 @@ +/* + * jQuery UI Slider 1.8.13 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Slider#theming + */ +.ui-slider { position: relative; text-align: left; } +.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; } +.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; } + +.ui-slider-horizontal { height: .8em; } +.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; } +.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } +.ui-slider-horizontal .ui-slider-range-min { left: 0; } +.ui-slider-horizontal .ui-slider-range-max { right: 0; } + +.ui-slider-vertical { width: .8em; height: 100px; } +.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; } +.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } +.ui-slider-vertical .ui-slider-range-min { bottom: 0; } +.ui-slider-vertical .ui-slider-range-max { top: 0; } \ No newline at end of file diff --git a/SignalR.Samples/Content/themes/base/jquery.ui.tabs.css b/SignalR.Samples/Content/themes/base/jquery.ui.tabs.css new file mode 100644 index 000000000..1ed9c22d0 --- /dev/null +++ b/SignalR.Samples/Content/themes/base/jquery.ui.tabs.css @@ -0,0 +1,18 @@ +/* + * jQuery UI Tabs 1.8.13 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Tabs#theming + */ +.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ +.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; } +.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; } +.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; } +.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ +.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; } +.ui-tabs .ui-tabs-hide { display: none !important; } diff --git a/SignalR.Samples/Content/themes/base/jquery.ui.theme.css b/SignalR.Samples/Content/themes/base/jquery.ui.theme.css new file mode 100644 index 000000000..c1018644e --- /dev/null +++ b/SignalR.Samples/Content/themes/base/jquery.ui.theme.css @@ -0,0 +1,252 @@ +/* + * jQuery UI CSS Framework 1.8.13 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + * + * To view and modify this theme, visit http://jqueryui.com/themeroller/ + */ + + +/* Component containers +----------------------------------*/ +.ui-widget { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1.1em/*{fsDefault}*/; } +.ui-widget .ui-widget { font-size: 1em; } +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1em; } +.ui-widget-content { border: 1px solid #aaaaaa/*{borderColorContent}*/; background: #ffffff/*{bgColorContent}*/ url(images/ui-bg_flat_75_ffffff_40x100.png)/*{bgImgUrlContent}*/ 50%/*{bgContentXPos}*/ 50%/*{bgContentYPos}*/ repeat-x/*{bgContentRepeat}*/; color: #222222/*{fcContent}*/; } +.ui-widget-content a { color: #222222/*{fcContent}*/; } +.ui-widget-header { border: 1px solid #aaaaaa/*{borderColorHeader}*/; background: #cccccc/*{bgColorHeader}*/ url(images/ui-bg_highlight-soft_75_cccccc_1x100.png)/*{bgImgUrlHeader}*/ 50%/*{bgHeaderXPos}*/ 50%/*{bgHeaderYPos}*/ repeat-x/*{bgHeaderRepeat}*/; color: #222222/*{fcHeader}*/; font-weight: bold; } +.ui-widget-header a { color: #222222/*{fcHeader}*/; } + +/* Interaction states +----------------------------------*/ +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3/*{borderColorDefault}*/; background: #e6e6e6/*{bgColorDefault}*/ url(images/ui-bg_glass_75_e6e6e6_1x400.png)/*{bgImgUrlDefault}*/ 50%/*{bgDefaultXPos}*/ 50%/*{bgDefaultYPos}*/ repeat-x/*{bgDefaultRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #555555/*{fcDefault}*/; } +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555/*{fcDefault}*/; text-decoration: none; } +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999/*{borderColorHover}*/; background: #dadada/*{bgColorHover}*/ url(images/ui-bg_glass_75_dadada_1x400.png)/*{bgImgUrlHover}*/ 50%/*{bgHoverXPos}*/ 50%/*{bgHoverYPos}*/ repeat-x/*{bgHoverRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcHover}*/; } +.ui-state-hover a, .ui-state-hover a:hover { color: #212121/*{fcHover}*/; text-decoration: none; } +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa/*{borderColorActive}*/; background: #ffffff/*{bgColorActive}*/ url(images/ui-bg_glass_65_ffffff_1x400.png)/*{bgImgUrlActive}*/ 50%/*{bgActiveXPos}*/ 50%/*{bgActiveYPos}*/ repeat-x/*{bgActiveRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcActive}*/; } +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121/*{fcActive}*/; text-decoration: none; } +.ui-widget :active { outline: none; } + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1/*{borderColorHighlight}*/; background: #fbf9ee/*{bgColorHighlight}*/ url(images/ui-bg_glass_55_fbf9ee_1x400.png)/*{bgImgUrlHighlight}*/ 50%/*{bgHighlightXPos}*/ 50%/*{bgHighlightYPos}*/ repeat-x/*{bgHighlightRepeat}*/; color: #363636/*{fcHighlight}*/; } +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636/*{fcHighlight}*/; } +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a/*{borderColorError}*/; background: #fef1ec/*{bgColorError}*/ url(images/ui-bg_glass_95_fef1ec_1x400.png)/*{bgImgUrlError}*/ 50%/*{bgErrorXPos}*/ 50%/*{bgErrorYPos}*/ repeat-x/*{bgErrorRepeat}*/; color: #cd0a0a/*{fcError}*/; } +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a/*{fcError}*/; } +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a/*{fcError}*/; } +.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } +.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; } +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; } +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png)/*{iconsHeader}*/; } +.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png)/*{iconsDefault}*/; } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png)/*{iconsHover}*/; } +.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png)/*{iconsActive}*/; } +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png)/*{iconsHighlight}*/; } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png)/*{iconsError}*/; } + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-off { background-position: -96px -144px; } +.ui-icon-radio-on { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-tl { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; } +.ui-corner-tr { -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; } +.ui-corner-bl { -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; } +.ui-corner-br { -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; } +.ui-corner-top { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; } +.ui-corner-bottom { -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; } +.ui-corner-right { -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; } +.ui-corner-left { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; } +.ui-corner-all { -moz-border-radius: 4px/*{cornerRadius}*/; -webkit-border-radius: 4px/*{cornerRadius}*/; border-radius: 4px/*{cornerRadius}*/; } + +/* Overlays */ +.ui-widget-overlay { background: #aaaaaa/*{bgColorOverlay}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlOverlay}*/ 50%/*{bgOverlayXPos}*/ 50%/*{bgOverlayYPos}*/ repeat-x/*{bgOverlayRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityOverlay}*/; } +.ui-widget-shadow { margin: -8px/*{offsetTopShadow}*/ 0 0 -8px/*{offsetLeftShadow}*/; padding: 8px/*{thicknessShadow}*/; background: #aaaaaa/*{bgColorShadow}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlShadow}*/ 50%/*{bgShadowXPos}*/ 50%/*{bgShadowYPos}*/ repeat-x/*{bgShadowRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityShadow}*/; -moz-border-radius: 8px/*{cornerRadiusShadow}*/; -webkit-border-radius: 8px/*{cornerRadiusShadow}*/; border-radius: 8px/*{cornerRadiusShadow}*/; } \ No newline at end of file diff --git a/SignalR.Samples/Content/themes/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png b/SignalR.Samples/Content/themes/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png new file mode 100644 index 0000000000000000000000000000000000000000..5b5dab2ab7b1c50dea9cfe73dc5a269a92d2d4b4 GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^8bF-F!3HG1q!d*FscKIb$B>N1x91EQ4=4yQ7#`R^ z$vje}bP0l+XkK DSH>_4 literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/themes/smoothness/images/ui-bg_flat_75_ffffff_40x100.png b/SignalR.Samples/Content/themes/smoothness/images/ui-bg_flat_75_ffffff_40x100.png new file mode 100644 index 0000000000000000000000000000000000000000..ac8b229af950c29356abf64a6c4aa894575445f0 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^8bF-F!3HG1q!d*FsY*{5$B>N1x91EQ4=4yQYz+E8 zPo9&<{J;c_6SHRil>2s{Zw^OT)6@jj2u|u!(plXsM>LJD`vD!n;OXk;vd$@?2>^GI BH@yG= literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/themes/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png b/SignalR.Samples/Content/themes/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..ad3d6346e00f246102f72f2e026ed0491988b394 GIT binary patch literal 120 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjgAK^akKnour0hLi978O6-<~(*I$*%ybaDOn z{W;e!B}_MSUQoPXhYd^Y6RUoS1yepnPx`2Kz)7OXQG!!=-jY=F+d2OOy?#DnJ32>z UEim$g7SJdLPgg&ebxsLQ09~*s;{X5v literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/themes/smoothness/images/ui-bg_glass_65_ffffff_1x400.png b/SignalR.Samples/Content/themes/smoothness/images/ui-bg_glass_65_ffffff_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..42ccba269b6e91bef12ad0fa18be651b5ef0ee68 GIT binary patch literal 105 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjgAK^akKnouqzpV=978O6-=0?FV^9z|eBtf= z|7WztIJ;WT>{+tN>ySr~=F{k$>;_x^_y?afmf9pRKH0)6?eSP?3s5hEr>mdKI;Vst E0O;M1& literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/themes/smoothness/images/ui-bg_glass_75_dadada_1x400.png b/SignalR.Samples/Content/themes/smoothness/images/ui-bg_glass_75_dadada_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..5a46b47cb16631068aee9e0bd61269fc4e95e5cd GIT binary patch literal 111 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjgAK^akKnouq|7{B978O6lPf+wIa#m9#>Unb zm^4K~wN3Zq+uP{vDV26o)#~38k_!`W=^oo1w6ixmPC4R1b Tyd6G3lNdZ*{an^LB{Ts5`idse literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/themes/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/SignalR.Samples/Content/themes/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png new file mode 100644 index 0000000000000000000000000000000000000000..7c9fa6c6edcfcdd3e5b77e6f547b719e6fc66e30 GIT binary patch literal 101 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 literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/themes/smoothness/images/ui-icons_2e83ff_256x240.png b/SignalR.Samples/Content/themes/smoothness/images/ui-icons_2e83ff_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..09d1cdc856c292c4ab6dd818c7543ac0828bd616 GIT binary patch literal 4369 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 literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/themes/smoothness/images/ui-icons_454545_256x240.png b/SignalR.Samples/Content/themes/smoothness/images/ui-icons_454545_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..59bd45b907c4fd965697774ce8c5fc6b2fd9c105 GIT binary patch literal 4369 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 oBo%DI*Kv;w;*%(i9W@f3_WCF#rGn literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/themes/smoothness/images/ui-icons_cd0a0a_256x240.png b/SignalR.Samples/Content/themes/smoothness/images/ui-icons_cd0a0a_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..2ab019b73ec11a485fa09378f3a0e155194f6a5d GIT binary patch literal 4369 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 _users = new Dictionary(StringComparer.OrdinalIgnoreCase); + private static readonly Dictionary> _userRooms = new Dictionary>(StringComparer.OrdinalIgnoreCase); + private static readonly Dictionary _rooms = new Dictionary(StringComparer.OrdinalIgnoreCase) { + { "main", new ChatRoom() } + }; + + private static readonly List _contentProviders = new List() { + new ImageContentProvider(), + new YouTubeContentProvider(), + new CollegeHumorContentProvider() + }; + + public bool Join() { + // Check the user id cookie + var cookie = Context.Cookies["userid"]; + if (cookie == null) { + return false; + } + + ChatUser user = _users.Values.FirstOrDefault(u => u.Id == cookie.Value); + + if (user != null) { + // Update the users's client id mapping + user.ClientId = Context.ClientId; + + // Set some client state + Caller.id = user.Id; + Caller.name = user.Name; + Caller.hash = user.Hash; + + // Leave all rooms + HashSet rooms; + if (_userRooms.TryGetValue(user.Name, out rooms)) { + foreach (var room in rooms) { + Clients[room].leave(user); + ChatRoom chatRoom = _rooms[room]; + chatRoom.Users.Remove(user.Name); + } + } + + _userRooms[user.Name] = new HashSet(StringComparer.OrdinalIgnoreCase); + + // Add this user to the list of users + Caller.addUser(user); + return true; + } + + return false; + } + + public void Send(string content) { + content = Sanitizer.GetSafeHtmlFragment(content); + + if (!TryHandleCommand(content)) { + string roomName = Caller.room; + string name = Caller.name; + + EnsureUserAndRoom(); + + HashSet links; + var messageText = Transform(content, out links); + var chatMessage = new ChatMessage(name, messageText); + + _rooms[roomName].Messages.Add(chatMessage); + + Clients[roomName].addMessage(chatMessage.Id, chatMessage.User, chatMessage.Text); + + if (links.Any()) { + // REVIEW: is this safe to do? We're holding on to this instance + // when this should really be a fire and forget. + var contentTasks = links.Select(ExtractContent).ToArray(); + Task.Factory.ContinueWhenAll(contentTasks, tasks => { + foreach (var task in tasks) { + if (String.IsNullOrEmpty(task.Result)) { + continue; + } + + // Try to get content from each url we're resolved in the query + string extractedContent = "

" + task.Result + "

"; + + // If we did get something, update the message and notify all clients + chatMessage.Text += extractedContent; + + Clients[roomName].addMessageContent(chatMessage.Id, extractedContent); + } + }); + } + } + } + + public void Disconnect(string id) { + ChatUser user = _users.Values.FirstOrDefault(u => u.Id == id); + if (user != null) { + _users.Remove(user.Name); + + // Leave all rooms + HashSet rooms; + if (_userRooms.TryGetValue(user.Name, out rooms)) { + foreach (var room in rooms) { + Clients[room].leave(user); + ChatRoom chatRoom = _rooms[room]; + chatRoom.Users.Remove(user.Name); + } + } + + _userRooms.Remove(user.Name); + } + } + + public IEnumerable GetUsers() { + string room = Caller.room; + + if (String.IsNullOrEmpty(room)) { + return Enumerable.Empty(); + } + + return from name in _rooms[room].Users + select _users[name]; + } + + private string GetMD5Hash(string name) { + return String.Join("", MD5.Create() + .ComputeHash(Encoding.Default.GetBytes(name)) + .Select(b => b.ToString("x2"))); + } + + private bool TryHandleCommand(string message) { + string room = Caller.room; + string name = Caller.name; + + message = message.Trim(); + if (message.StartsWith("/")) { + string[] parts = message.Substring(1).Split(' '); + string commandName = parts[0]; + + if (commandName.Equals("nick", StringComparison.OrdinalIgnoreCase)) { + string newUserName = String.Join(" ", parts.Skip(1)); + + if (String.IsNullOrEmpty(newUserName)) { + throw new InvalidOperationException("No username specified!"); + } + + if (newUserName.Equals(name, StringComparison.OrdinalIgnoreCase)) { + throw new InvalidOperationException("That's already your username..."); + } + + if (!_users.ContainsKey(newUserName)) { + if (String.IsNullOrEmpty(name) || !_users.ContainsKey(name)) { + var user = AddUser(newUserName); + } + else { + var oldUser = _users[name]; + var newUser = new ChatUser { + Name = newUserName, + Hash = GetMD5Hash(newUserName), + Id = oldUser.Id + }; + + _users[newUserName] = newUser; + _userRooms[newUserName] = new HashSet(_userRooms[name]); + + if (_userRooms[name].Any()) { + foreach (var r in _userRooms[name]) { + _rooms[r].Users.Remove(name); + _rooms[r].Users.Add(newUserName); + Clients[r].changeUserName(oldUser, newUser); + } + } + + _userRooms.Remove(name); + _users.Remove(name); + + Caller.hash = newUser.Hash; + Caller.name = newUser.Name; + } + } + else { + throw new InvalidOperationException(String.Format("Username '{0}' is already taken!", newUserName)); + } + + return true; + } + else { + EnsureUser(); + if (commandName.Equals("join", StringComparison.OrdinalIgnoreCase)) { + if (parts.Length == 1) { + throw new InvalidOperationException("Join which room?"); + } + + // Only support one room at a time for now + + string newRoom = parts[1]; + ChatRoom chatRoom; + // Create the room if it doesn't exist + if (!_rooms.TryGetValue(newRoom, out chatRoom)) { + chatRoom = new ChatRoom(); + _rooms.Add(newRoom, chatRoom); + } + + // Remove the old room + if (!String.IsNullOrEmpty(room)) { + _userRooms[name].Remove(room); + _rooms[room].Users.Remove(name); + + Clients[room].leave(_users[name]); + RemoveFromGroup(room); + } + + _userRooms[name].Add(newRoom); + if (!chatRoom.Users.Add(name)) { + throw new InvalidOperationException("You're already in that room!"); + } + + Clients[newRoom].addUser(_users[name]); + + // Set the room on the caller + Caller.room = newRoom; + + AddToGroup(newRoom); + + Caller.refreshRoom(newRoom); + + return true; + } + else if (commandName.Equals("msg", StringComparison.OrdinalIgnoreCase)) { + if (_users.Count == 1) { + throw new InvalidOperationException("You're the only person in here..."); + } + + if (parts.Length < 2) { + throw new InvalidOperationException("Who are you trying send a private message to?"); + } + + string to = parts[1]; + if (to.Equals(name, StringComparison.OrdinalIgnoreCase)) { + throw new InvalidOperationException("You can't private message yourself!"); + } + + if (!_users.ContainsKey(to)) { + throw new InvalidOperationException(String.Format("Couldn't find any user named '{0}'.", to)); + } + + string messageText = String.Join(" ", parts.Skip(2)).Trim(); + + if (String.IsNullOrEmpty(messageText)) { + throw new InvalidOperationException(String.Format("What did you want to say to '{0}'.", to)); + } + + string recipientId = _users[to].ClientId; + // Send a message to the sender and the sendee + Clients[recipientId].sendPrivateMessage(name, to, messageText); + Caller.sendPrivateMessage(name, to, messageText); + + return true; + } + else { + EnsureUserAndRoom(); + if (commandName.Equals("me", StringComparison.OrdinalIgnoreCase)) { + if (parts.Length == 1) { + throw new InvalidProgramException("You what?"); + } + var content = String.Join(" ", parts.Skip(1)); + + Clients[room].sendMeMessage(name, content); + return true; + } + else if (commandName.Equals("leave", StringComparison.OrdinalIgnoreCase)) { + ChatRoom chatRoom; + if (_rooms.TryGetValue(room, out chatRoom)) { + chatRoom.Users.Remove(name); + _userRooms[name].Remove(room); + + Clients[room].leave(_users[name]); + } + + RemoveFromGroup(room); + + Caller.room = null; + + return true; + } + + throw new InvalidOperationException(String.Format("'{0}' is not a valid command.", parts[0])); + } + } + } + return false; + } + + private ChatUser AddUser(string newUserName) { + var user = new ChatUser(newUserName, GetMD5Hash(newUserName)); + user.ClientId = Context.ClientId; + _users[newUserName] = user; + _userRooms[newUserName] = new HashSet(StringComparer.OrdinalIgnoreCase); + + Caller.name = user.Name; + Caller.hash = user.Hash; + Caller.id = user.Id; + + Caller.addUser(user); + + return user; + } + + private void EnsureUserAndRoom() { + EnsureUser(); + + // TODO: Restore when groups work + string room = Caller.room; + string name = Caller.name; + + if (String.IsNullOrEmpty(room) || !_rooms.ContainsKey(room)) { + throw new InvalidOperationException("Use '/join room' to join a room."); + } + + HashSet rooms; + if (!_userRooms.TryGetValue(name, out rooms) || !rooms.Contains(room)) { + throw new InvalidOperationException(String.Format("You're not in '{0}'. Use '/join {0}' to join it.", room)); + } + } + + private void EnsureUser() { + string name = Caller.name; + if (String.IsNullOrEmpty(name) || !_users.ContainsKey(name)) { + throw new InvalidOperationException("No name choson. Pick a name using '/nick nickname'."); + } + } + + private string Transform(string message, out HashSet extractedUrls) { + const string urlPattern = @"((https?|ftp)://|www\.)[\w]+(.[\w]+)([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])"; + + var urls = new HashSet(StringComparer.OrdinalIgnoreCase); + message = Regex.Replace(message, urlPattern, m => { + string httpPortion = String.Empty; + if (!m.Value.Contains("://")) { + httpPortion = "http://"; + } + + string url = httpPortion + m.Value; + + urls.Add(url); + + return String.Format(CultureInfo.InvariantCulture, + "
{1}", + url, m.Value); + }); + + extractedUrls = urls; + return message; + } + + private Task ExtractContent(string url) { + var request = (HttpWebRequest)HttpWebRequest.Create(url); + var requestTask = Task.Factory.FromAsync((cb, state) => request.BeginGetResponse(cb, state), ar => request.EndGetResponse(ar), null); + return requestTask.ContinueWith(task => ExtractContent((HttpWebResponse)task.Result)); + } + + private string ExtractContent(HttpWebResponse response) { + return _contentProviders.Select(c => c.GetContent(response)) + .FirstOrDefault(content => content != null); + } + + [Serializable] + public class ChatMessage { + public string Id { get; private set; } + public string User { get; set; } + public string Text { get; set; } + public ChatMessage(string user, string text) { + User = user; + Text = text; + Id = Guid.NewGuid().ToString("d"); + } + } + + [Serializable] + public class ChatUser { + public string ClientId { get; set; } + public string Id { get; set; } + public string Name { get; set; } + public string Hash { get; set; } + + public ChatUser() { + } + + public ChatUser(string name, string hash) { + Name = name; + Hash = hash; + Id = Guid.NewGuid().ToString("d"); + } + } + + public class ChatRoom { + public List Messages { get; set; } + public HashSet Users { get; set; } + + public ChatRoom() { + Messages = new List(); + Users = new HashSet(StringComparer.OrdinalIgnoreCase); + } + } + } +} \ No newline at end of file diff --git a/SignalR.Samples/Hubs/Chat/Chat.css b/SignalR.Samples/Hubs/Chat/Chat.css new file mode 100644 index 000000000..10b706dfc --- /dev/null +++ b/SignalR.Samples/Hubs/Chat/Chat.css @@ -0,0 +1,75 @@ +body +{ + font-family: Trebuchet MS; + font-size: 13px; +} + +#messages +{ + width: 70%; + border: 1px solid #ccc; + height: 500px; + float: left; + overflow: auto; + margin-left: 0px; + padding-left: 0px; +} + +#messages li +{ + list-style-type: none; + padding: 3px; +} + +#users +{ + width: 17%; + height: 500px; + border-right: 1px solid #ccc; + border-top: 1px solid #ccc; + border-bottom: 1px solid #ccc; + float: left; + margin-left: 0px; + padding-left: 0px; + overflow: auto; + font-size: small; + white-space: nowrap; +} + +#users li +{ + padding: 4px; +} + +.clear +{ + clear: both; +} + +#new-message +{ + font-family: Trebuchet MS; + width: 70%; + padding: 5px; + border: 1px solid #ccc; +} + +.error +{ + color:Red; +} + +.pm +{ + color:Red; +} + +.notification +{ + color:#bbb; +} + +.message +{ + +} \ No newline at end of file diff --git a/SignalR.Samples/Hubs/Chat/Chat.js b/SignalR.Samples/Hubs/Chat/Chat.js new file mode 100644 index 000000000..98858ba7d --- /dev/null +++ b/SignalR.Samples/Hubs/Chat/Chat.js @@ -0,0 +1,180 @@ +/// +/// +/// + +$(function () { + var chat = signalR.chat; + + function clearMessages() { + $('#messages').html(''); + } + + function clearUsers() { + $('#users').html(''); + } + + function addMessage(content, type) { + var e = $('
  • ').html(content).appendTo($('#messages')); + if (type) { + e.addClass(type); + } + updateUnread(); + e[0].scrollIntoView(); + return e; + } + + chat.refreshRoom = function (room) { + clearMessages(); + clearUsers(); + + chat.getUsers() + .done(function (users) { + $.each(users, function () { + chat.addUser(this, true); + }); + + $('#new-message').focus(); + }); + + addMessage('Entered ' + room, 'notification'); + }; + + chat.addMessageContent = function (id, content) { + var e = $('#m-' + id).append(content); + updateUnread(); + e[0].scrollIntoView(); + }; + + chat.addMessage = function (id, name, message) { + var data = { + name: name, + message: message, + id: id + }; + + var e = $('#new-message-template').tmpl(data) + .appendTo($('#messages')); + updateUnread(); + e[0].scrollIntoView(); + }; + + chat.addUser = function (user, exists) { + var id = 'u-' + user.Name; + if (document.getElementById(id)) { + return; + } + + var data = { + name: user.Name, + hash: user.Hash + }; + + var e = $('#new-user-template').tmpl(data) + .appendTo($('#users')); + + if (!exists && this.state.name != user.Name) { + addMessage(user.Name + ' just entered ' + this.state.room, 'notification'); + e.hide().fadeIn('slow'); + } + + updateCookie(); + }; + + chat.changeUserName = function (oldUser, newUser) { + $('#u-' + oldUser.Name).replaceWith( + $('#new-user-template').tmpl({ + name: newUser.Name, + hash: newUser.Hash + }) + ); + + if (oldUser.Name === this.state.name) { + addMessage('Your name is now ' + newUser.Name, 'notification'); + updateCookie(); + } + else { + addMessage(oldUser.Name + '\'s nick has changed to ' + newUser.Name, 'notification'); + } + }; + + chat.sendMeMessage = function (name, message) { + addMessage('*' + name + '* ' + message, 'notification'); + }; + + chat.sendPrivateMessage = function (from, to, message) { + addMessage('*' + from + '* ' + message, 'pm'); + }; + + chat.leave = function (user) { + if (this.state.id != user.Id) { + $('#u-' + user.Name).fadeOut('slow', function () { + $(this).remove(); + }); + + addMessage(user.Name + ' left ' + this.state.room, 'notification'); + } + }; + + $('#send-message').submit(function () { + var command = $('#new-message').val(); + + chat.send(command) + .fail(function (e) { + addMessage(e, 'error'); + }); + + $('#new-message').val(''); + $('#new-message').focus(); + + return false; + }); + + $(window).blur(function () { + chat.state.focus = false; + }); + + $(window).focus(function () { + chat.state.focus = true; + chat.state.unread = 0; + document.title = 'SignalR Chat'; + }); + + function updateUnread() { + if (!chat.state.focus) { + if (!chat.state.unread) { + chat.state.unread = 0; + } + chat.state.unread++; + } + updateTitle(); + } + + function updateTitle() { + if (chat.state.unread == 0) { + document.title = 'SignalR Chat'; + } + else { + document.title = 'SignalR Chat (' + chat.state.unread + ')'; + } + } + + function updateCookie() { + $.cookie('userid', chat.state.id, { path: '/', expires: 30 }); + } + + addMessage('Welcome to the SignalR IRC clone', 'notification'); + + $('#new-message').val(''); + $('#new-message').focus(); + + signalR.hub.start(function () { + chat.join() + .done(function (success) { + if (success === false) { + $.cookie('userid', '') + addMessage('Choose a name using "/nick nickname".', 'notification'); + } + addMessage('After that, join a room using "/join roomname".', 'notification'); + }); + }); +}); \ No newline at end of file diff --git a/SignalR.Samples/Hubs/Chat/ContentProviders/CollegeHumorContentProvider.cs b/SignalR.Samples/Hubs/Chat/ContentProviders/CollegeHumorContentProvider.cs new file mode 100644 index 000000000..4b0a5a447 --- /dev/null +++ b/SignalR.Samples/Hubs/Chat/ContentProviders/CollegeHumorContentProvider.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace SignalR.Samples.Hubs.Chat.ContentProviders { + public class CollegeHumorContentProvider : EmbedContentProvider { + private static readonly Regex _videoIdRegex = new Regex(@".*video/(\d+).*"); + + public override Regex MediaUrlRegex { + get { + return _videoIdRegex; + } + } + + public override IEnumerable Domains { + get { yield return "http://www.collegehumor.com"; } + } + + public override string MediaFormatSting { + get { + return @""; + } + } + } +} \ No newline at end of file diff --git a/SignalR.Samples/Hubs/Chat/ContentProviders/EmbedContentProvider.cs b/SignalR.Samples/Hubs/Chat/ContentProviders/EmbedContentProvider.cs new file mode 100644 index 000000000..e699e8e27 --- /dev/null +++ b/SignalR.Samples/Hubs/Chat/ContentProviders/EmbedContentProvider.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text.RegularExpressions; + +namespace SignalR.Samples.Hubs.Chat.ContentProviders { + public abstract class EmbedContentProvider : IContentProvider { + public virtual Regex MediaUrlRegex { + get { + return null; + } + } + public abstract IEnumerable Domains { get; } + public abstract string MediaFormatSting { get; } + + protected virtual IEnumerable ExtractParameters(Uri responseUri) { + if (MediaUrlRegex != null) { + return MediaUrlRegex.Match(responseUri.AbsoluteUri) + .Groups + .Cast() + .Skip(1) + .Select(g => g.Value) + .Where(v => !String.IsNullOrEmpty(v)); + } + return null; + } + + public string GetContent(HttpWebResponse response) { + if (Domains.Any(d => response.ResponseUri.AbsoluteUri.StartsWith(d, StringComparison.OrdinalIgnoreCase))) { + var args = ExtractParameters(response.ResponseUri); + if (args == null || !args.Any()) { + return null; + } + + return String.Format(MediaFormatSting, args.ToArray()); + } + return null; + } + } +} \ No newline at end of file diff --git a/SignalR.Samples/Hubs/Chat/ContentProviders/IContentProvider.cs b/SignalR.Samples/Hubs/Chat/ContentProviders/IContentProvider.cs new file mode 100644 index 000000000..88584ccec --- /dev/null +++ b/SignalR.Samples/Hubs/Chat/ContentProviders/IContentProvider.cs @@ -0,0 +1,7 @@ +using System.Net; + +namespace SignalR.Samples.Hubs.Chat { + public interface IContentProvider { + string GetContent(HttpWebResponse response); + } +} \ No newline at end of file diff --git a/SignalR.Samples/Hubs/Chat/ContentProviders/ImageContentProvider.cs b/SignalR.Samples/Hubs/Chat/ContentProviders/ImageContentProvider.cs new file mode 100644 index 000000000..8f7c3cea0 --- /dev/null +++ b/SignalR.Samples/Hubs/Chat/ContentProviders/ImageContentProvider.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Net; + +namespace SignalR.Samples.Hubs.Chat.ContentProviders { + public class ImageContentProvider : IContentProvider { + private static readonly HashSet _imageMimeTypes = new HashSet(StringComparer.OrdinalIgnoreCase) { + "image/png", + "image/jpg", + "image/jpeg", + "image/bmp", + "image/gif", + }; + + public string GetContent(HttpWebResponse response) { + if (!String.IsNullOrEmpty(response.ContentType) && + _imageMimeTypes.Contains(response.ContentType)) { + return String.Format(@"", response.ResponseUri); + } + return null; + } + } +} \ No newline at end of file diff --git a/SignalR.Samples/Hubs/Chat/ContentProviders/YoutubeContentProvider.cs b/SignalR.Samples/Hubs/Chat/ContentProviders/YoutubeContentProvider.cs new file mode 100644 index 000000000..9972375d4 --- /dev/null +++ b/SignalR.Samples/Hubs/Chat/ContentProviders/YoutubeContentProvider.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Web; + +namespace SignalR.Samples.Hubs.Chat.ContentProviders { + public class YouTubeContentProvider : EmbedContentProvider { + public override string MediaFormatSting { + get { + return @""; + } + } + + public override IEnumerable Domains { + get { yield return "http://www.youtube.com"; } + } + + protected override IEnumerable ExtractParameters(Uri responseUri) { + var queryString = HttpUtility.ParseQueryString(responseUri.Query); + string videoId = queryString["v"]; + if (!String.IsNullOrEmpty(videoId)) { + yield return videoId; + } + } + } +} \ No newline at end of file diff --git a/SignalR.Samples/Hubs/Chat/index.htm b/SignalR.Samples/Hubs/Chat/index.htm new file mode 100644 index 000000000..681f1cae0 --- /dev/null +++ b/SignalR.Samples/Hubs/Chat/index.htm @@ -0,0 +1,39 @@ + + + + SignalR Chat + + + + + + + + + + + + +
    +
      +
    +
      +
    +
    +
    +
    + + +
    +
    + + \ No newline at end of file diff --git a/SignalR.Samples/Hubs/DemoHub/DemoHub.cs b/SignalR.Samples/Hubs/DemoHub/DemoHub.cs new file mode 100644 index 000000000..c6062361a --- /dev/null +++ b/SignalR.Samples/Hubs/DemoHub/DemoHub.cs @@ -0,0 +1,38 @@ +using System.Threading; +using System.Threading.Tasks; +using SignalR.Hubs; + +namespace SignalR.Samples.Hubs.DemoHub { + public class DemoHub : Hub { + public Task GetValue() { + return Task.Factory.StartNew(() => { + Thread.Sleep(5000); + return 10; + }); + } + + public Person ComplexType(Person p) { + Caller.person = p; + return p; + } + + public void MultipleCalls() { + for (int i = 0; i < 10; i++) { + Caller.index = i + 1; + Caller.invoke(i); + Thread.Sleep(1000); + } + } + + public class Person { + public string Name { get; set; } + public int Age { get; set; } + public Address Address { get; set; } + } + + public class Address { + public string Street { get; set; } + public string Zip { get; set; } + } + } +} \ No newline at end of file diff --git a/SignalR.Samples/Hubs/DemoHub/DemoHub.js b/SignalR.Samples/Hubs/DemoHub/DemoHub.js new file mode 100644 index 000000000..d80f95e99 --- /dev/null +++ b/SignalR.Samples/Hubs/DemoHub/DemoHub.js @@ -0,0 +1,24 @@ +$(function () { + + signalR.demoHub.invoke = function (index) { + $('#msg').append('
  • ' + index + ' client state index ->' + this.state.index + '
  • '); + }; + + signalR.hub.start(function () { + signalR.demoHub.getValue(function (value) { + $('#value').html('The value is ' + value + ' after 5 seconds'); + }); + + var p = { + Name: "Foo", + Age: 20, + Address: { Street: "One Microsoft Way", Zip: "98052" } + }; + + signalR.demoHub.complexType(p, function () { + $('#value').html('Complex Type ->' + window.JSON.stringify(this.state.person)); + }); + + signalR.demoHub.multipleCalls(); + }); +}); \ No newline at end of file diff --git a/SignalR.Samples/Hubs/DemoHub/index.htm b/SignalR.Samples/Hubs/DemoHub/index.htm new file mode 100644 index 000000000..fe7000055 --- /dev/null +++ b/SignalR.Samples/Hubs/DemoHub/index.htm @@ -0,0 +1,19 @@ + + + + + + + + + + + + +
    +
    + +
      +
    + + diff --git a/SignalR.Samples/Hubs/MouseTracking/MouseTracking.cs b/SignalR.Samples/Hubs/MouseTracking/MouseTracking.cs new file mode 100644 index 000000000..0c03493cc --- /dev/null +++ b/SignalR.Samples/Hubs/MouseTracking/MouseTracking.cs @@ -0,0 +1,16 @@ +using System.Threading; +using SignalR.Hubs; + +namespace SignalR.Samples.Hubs.MouseTracking { + public class MouseTracking : Hub { + private static long _id; + + public void Join() { + Caller.id = Interlocked.Increment(ref _id); + } + + public void Move(int x, int y) { + Clients.moveMouse(Caller.id, x, y); + } + } +} \ No newline at end of file diff --git a/SignalR.Samples/Hubs/MouseTracking/MouseTracking.css b/SignalR.Samples/Hubs/MouseTracking/MouseTracking.css new file mode 100644 index 000000000..83869f4a4 --- /dev/null +++ b/SignalR.Samples/Hubs/MouseTracking/MouseTracking.css @@ -0,0 +1,18 @@ +body +{ + font-family: Trebuchet MS; + font-size: 13px; +} + +.icon +{ + background-repeat: no-repeat; + background-position: 0 50%; + padding-left: 20px; + line-height: 20px; +} + +.icon-wand +{ + background-image: url('images/wand.png'); +} diff --git a/SignalR.Samples/Hubs/MouseTracking/MouseTracking.js b/SignalR.Samples/Hubs/MouseTracking/MouseTracking.js new file mode 100644 index 000000000..53bd0fb44 --- /dev/null +++ b/SignalR.Samples/Hubs/MouseTracking/MouseTracking.js @@ -0,0 +1,35 @@ +$(function () { + // Pure client side hub + signalR.mouseTracking.moveMouse = function (id, x, y) { + if (id == this.state.id) { + return; + } + + updateCursor(id, x, y); + }; + + function updateCursor(id, x, y) { + var e = document.getElementById(id); + if (!e) { + e = $('
    ' + id + '
    ').appendTo(document.body); + e.css('position', 'absolute'); + } + else { + e = $(e); + } + e.css('left', x); + e.css('top', y); + } + + window.setTimeout(function () { + signalR.hub.start(function () { + signalR.mouseTracking.join(function () { + + $(document).mousemove(function (e) { + signalR.mouseTracking.move(e.pageX, e.pageY); + updateCursor(signalR.mouseTracking.state.id, e.pageX, e.pageY); + }); + }); + }); + }, 0); +}); \ No newline at end of file diff --git a/SignalR.Samples/Hubs/MouseTracking/images/wand.png b/SignalR.Samples/Hubs/MouseTracking/images/wand.png new file mode 100644 index 0000000000000000000000000000000000000000..44ccbf812879c42cb1f9587d865bcfc337ce6361 GIT binary patch literal 570 zcmV-A0>%A_P)hUn2-Navsqo?5A8 zh}}V^=%7@_%C;=H{tGpIj5CMu*>5J=i;m^t2QTySd)}9aAppozC}++wDz`eOViU-dbRRoz=JsVlZk>N%^azhi%=xTCt9`LQjtqNFW~e|R=r9= z`@I3J^#z@aD5yBuq2DLQO#|4uFW6R5kzPZ+h&6Af&5}POarL&lA~3t5R1i7uh*ffDw@qEs=HBW ze?CI~MkvG6H-MF7r{Yv4kw_q&PNP^XqFgTHXlH@RpLO}3aV{T{Ez=8lo;PHV$Ads1 zfOtF(s5%5V>3qE|&{lueV1Y1j%GF zXt&#NI-LlGLPN>_18TJzN~Mxf*f6pY5Dteo^|P&3>(H{!KTr&_wGRQWb^rhX07*qo IM6N<$g7V1&P5=M^ literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Hubs/MouseTracking/index.htm b/SignalR.Samples/Hubs/MouseTracking/index.htm new file mode 100644 index 000000000..878972f30 --- /dev/null +++ b/SignalR.Samples/Hubs/MouseTracking/index.htm @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/SignalR.Samples/Hubs/ShapeShare/ShapeShare.cs b/SignalR.Samples/Hubs/ShapeShare/ShapeShare.cs new file mode 100644 index 000000000..09be21080 --- /dev/null +++ b/SignalR.Samples/Hubs/ShapeShare/ShapeShare.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using SignalR.Hubs; + +namespace SignalR.Samples.Hubs.ShapeShare { + public class ShapeShare : Hub { + private static ConcurrentDictionary _users = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private static List _shapes = new List(); + private static Random _userNameGenerator = new Random(); + + public IEnumerable GetShapes() { + return _shapes; + } + + public void Join(string userName) { + User user = null; + if (string.IsNullOrWhiteSpace(userName)) { + user = new User(); + do { + user.Name = "User" + _userNameGenerator.Next(1000); + } while (!_users.TryAdd(user.Name, user)); + } + else if (!_users.TryGetValue(userName, out user)) { + user = new User { Name = userName }; + _users.TryAdd(userName, user); + } + Caller.user = user; + } + + public void ChangeUserName(string currentUserName, string newUserName) { + User user; + if (!string.IsNullOrEmpty(newUserName) && _users.TryGetValue(currentUserName, out user)) { + user.Name = newUserName; + User oldUser; + _users.TryRemove(currentUserName, out oldUser); + _users.TryAdd(newUserName, user); + Clients.userNameChanged(currentUserName, newUserName); + Caller.user = user; + } + } + + public void CreateShape(string type = "rectangle") { + var user = _users[Caller.user["Name"]]; + var shape = Shape.Create(type); + shape.ChangedBy = user; + _shapes.Add(shape); + Clients.shapeAdded(shape); + } + + public void ChangeShape(string id, int x, int y, int w, int h) { + if (w <= 0 || h <= 0) return; + + var shape = FindShape(id); + if (shape == null) { + return; + } + + var user = _users[Caller.user["Name"]]; + + shape.Width = w; + shape.Height = h; + shape.Location.X = x; + shape.Location.Y = y; + shape.ChangedBy = user; + + Clients.shapeChanged(shape); + } + + public void DeleteShape(string id) { + var shape = FindShape(id); + if (shape == null) { + return; + } + + _shapes.Remove(shape); + + Clients.shapeDeleted(id); + } + + public void DeleteAllShapes() { + var shapes = _shapes.Select(s => new { id = s.ID }).ToList(); + + _shapes.Clear(); + + Clients.shapesDeleted(shapes); + } + + private Shape FindShape(string id) { + return _shapes.Find(s => s.ID.Equals(id, StringComparison.OrdinalIgnoreCase)); + } + } + + public class User { + public string ID { get; private set; } + public string Name { get; set; } + + public User() { + ID = Guid.NewGuid().ToString("d"); + } + } + + public abstract class Shape { + public string ID { get; private set; } + public Point Location { get; set; } + public virtual int Width { get; set; } + public virtual int Height { get; set; } + public string Type { get { return GetType().Name.ToLower(); } } + public User ChangedBy { get; set; } + + public Shape() { + ID = Guid.NewGuid().ToString("d"); + Location = new Point(20, 20); + } + + public static Shape Create(string type) { + switch (type) { + case "picture": + return new Picture(); + case "circle": + return new Circle(); + case "square": + return new Square(); + case "rectangle": + default: + return new Rectangle(); + } + } + } + + public abstract class WidthHeightFixed : Shape { + public override int Height { + get { + return base.Height; + } + set { + base.Height = value; + base.Width = value; + } + } + + public override int Width { + get { + return base.Width; + } + set { + base.Width = value; + base.Height = value; + } + } + } + + public class Rectangle : Shape { + public Rectangle() + : base() { + Width = 160; + Height = 100; + } + } + + public class Square : WidthHeightFixed { + public Square() + : base() { + Width = 100; + } + } + + public class Circle : WidthHeightFixed { + public int Radius { + get { return base.Width / 2; } + set { + base.Height = value * 2; + base.Width = value * 2; + } + } + + public Circle() + : base() { + Width = 100; + } + } + + public class Picture : Shape { + public string Src { get; set; } + + public Picture() + : base() { + Src = "http://www.w3.org/html/logo/badge/html5-badge-h-css3-semantics.png"; + Width = 165; + Height = 64; + } + } + + public class Point { + public int X { get; set; } + public int Y { get; set; } + + public Point(int x, int y) { + X = x; + Y = y; + } + } +} \ No newline at end of file diff --git a/SignalR.Samples/Hubs/ShapeShare/ShapeShare.css b/SignalR.Samples/Hubs/ShapeShare/ShapeShare.css new file mode 100644 index 000000000..b5a547be3 --- /dev/null +++ b/SignalR.Samples/Hubs/ShapeShare/ShapeShare.css @@ -0,0 +1,65 @@ +@import "../../Content/themes/base/jquery.ui.all.css"; + +body { + font-family: "Segoe UI"; +} + +#toolbar, #toolbar menu { + list-style-type: none; + margin: 0; padding: 0; + overflow: hidden; +} + + #toolbar li { + float: left; + } + + #toolbar li a { + display: block; + width: 22px; + height: 22px; + line-height: 22px; + margin: 0 6px 6px 0; + text-decoration: none; + text-align: center; + color: #000; + background: silver; + } + +#shapes { + list-style-type: none; + margin: 0; padding: 0; + width: 100%; + height: 500px; + border: 1px solid #555; + position: relative; + overflow: hidden; +} + +.shape { + position: absolute; + cursor: move; +} + + .shape:hover { + outline: 2px solid #bbb; + } + + .shape.square { + background: #99ccff; + } + + .shape.rectangle { + background: #ccff99; + } + + .shape img { + width: 100%; + height: 100%; + } + +.user-info { + color: #555; + font-size: 11px; + padding: 3px 5px; +} \ No newline at end of file diff --git a/SignalR.Samples/Hubs/ShapeShare/ShapeShare.js b/SignalR.Samples/Hubs/ShapeShare/ShapeShare.js new file mode 100644 index 000000000..6d5517cf3 --- /dev/null +++ b/SignalR.Samples/Hubs/ShapeShare/ShapeShare.js @@ -0,0 +1,150 @@ +/// +/// +/// +/// +/// + +$(function () { + 'use strict'; + + var shapeShare = signalR.shapeShare; + + function changeShape(el) { + shapeShare.changeShape($(el).data("shapeId"), el.offsetLeft, el.offsetTop || 0, el.clientWidth, el.clientHeight); + } + + function makeShape(shape) { + /// + var el; + switch (shape.Type) { + case "picture": + el = $("
    "); + break; + case "circle": + el = $("
    "); + break; + case "square": + el = $("
    "); + case "rectangle": + default: + el = $("
    "); + break; + } + el + .css({ + width: shape.Width, + height: shape.Height, + left: shape.Location.X, + top: shape.Location.Y + }) + .addClass("shape") + .addClass(shape.Type) + .attr("id", "s-" + shape.ID) + .data("shapeId", shape.ID) + .append(""); + return el; + } + + shapeShare.shapeAdded = function (shape) { + makeShape(shape) + .hide() + .appendTo("#shapes") + .draggable({ + containment: "parent", + drag: function () { + changeShape(this); + } + }) + .resizable({ + handles: "all", + containment: "parent", + aspectRatio: + shape.Type === "picture" || + shape.Type === "square" || + shape.Type === "circle", + minHeight: 50, + minWidth: 50, + autoHide: true, + resize: function () { + changeShape(this); + } + }) + .fadeIn(); + }; + + shapeShare.shapeChanged = function (shape) { + if (this.state.user.ID === shape.ChangedBy.ID) { + $("#u-" + shape.ID).text(""); + return; + } + + $("#s-" + shape.ID).css({ + top: shape.Location.Y, + left: shape.Location.X, + width: shape.Width, + height: shape.Height + }); + + $("#u-" + shape.ID) + .text("changed by " + shape.ChangedBy.Name); + }; + + shapeShare.shapeDeleted = function (id) { + $("#s-" + id).fadeOut(250, function () { + $(this).remove(); + }); + }; + + shapeShare.shapesDeleted = function (shapes) { + $.each(shapes, function () { + shapeShare.shapeDeleted(this.id); + }); + }; + + $('#toolbar') + .delegate("menu[label=add] a", "click", function (e) { + e.preventDefault(); + + shapeShare.createShape(this.className.toLowerCase()); + + // switch (this.className.toLowerCase()) { + // case "square": + // shapeShare.createShape("square"); + // break; + // case "picture": + // shapeShare.createShape("picture"); + // break; + // } + + return false; + }) + .delegate("a.delete", "click", function (e) { + e.preventDefault(); + shapeShare.deleteAllShapes(); + return false; + }); + + $("#user").change(function () { + shapeShare.changeUserName(shapeShare.state.user.Name, $(this).val(), function () { + $.cookie("userName", shapeShare.state.user.Name, { expires: 30 }) + $("#user").val(shapeShare.state.user.Name); + }); + }); + + function load() { + shapeShare.getShapes(function (shapes) { + $.each(shapes, function () { + shapeShare.shapeAdded(this); + }); + }); + } + + signalR.hub.start(function () { + shapeShare.join($.cookie("userName"), function () { + $.cookie("userName", shapeShare.state.user.Name, { expires: 30 }); + $("#user").val(shapeShare.state.user.Name); + load(); + }); + }); + +}); \ No newline at end of file diff --git a/SignalR.Samples/Hubs/ShapeShare/index.htm b/SignalR.Samples/Hubs/ShapeShare/index.htm new file mode 100644 index 000000000..d0028ddc3 --- /dev/null +++ b/SignalR.Samples/Hubs/ShapeShare/index.htm @@ -0,0 +1,45 @@ + + + + SignalR Shape Share + + + + + + + + + + + +
    + +
  • + +
  • R
  • +
  • S
  • +
  • C
  • +
  • P
  • +
    +
  • +
  • + D +
  • +
  • + +
  • +
    + +
      + +
    +
    + + \ No newline at end of file diff --git a/SignalR.Samples/Performance/Performance.ashx b/SignalR.Samples/Performance/Performance.ashx new file mode 100644 index 000000000..ec1ab6557 --- /dev/null +++ b/SignalR.Samples/Performance/Performance.ashx @@ -0,0 +1 @@ +<%@ WebHandler Language="C#" CodeBehind="Performance.ashx.cs" Class="SignalR.Samples.Performance.Performance" %> diff --git a/SignalR.Samples/Performance/Performance.ashx.cs b/SignalR.Samples/Performance/Performance.ashx.cs new file mode 100644 index 000000000..810334acf --- /dev/null +++ b/SignalR.Samples/Performance/Performance.ashx.cs @@ -0,0 +1,28 @@ +using System.Threading; +using System.Web; + +namespace SignalR.Samples.Performance { + /// + /// Summary description for Performance + /// + public class Performance : PersistentConnection { + private static int _connectedClients; + + protected override void OnConnected(HttpContextBase context, string clientId) { + Interlocked.Increment(ref _connectedClients); + } + + protected override void OnReceived(string clientId, string data) { + if (data == "reset") { + _connectedClients = 0; + } + else { + Connection.Broadcast(new { data = data, clients = _connectedClients }); + } + } + + protected override void OnDisconnect(string clientId) { + Interlocked.Decrement(ref _connectedClients); + } + } +} \ No newline at end of file diff --git a/SignalR.Samples/Performance/index.html b/SignalR.Samples/Performance/index.html new file mode 100644 index 000000000..508554dbc --- /dev/null +++ b/SignalR.Samples/Performance/index.html @@ -0,0 +1,25 @@ + + + + + + + + + + Connected clients: 0 +
      +
    + + diff --git a/SignalR.Samples/Properties/AssemblyInfo.cs b/SignalR.Samples/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..bc7d58a5a --- /dev/null +++ b/SignalR.Samples/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SignalR.Samples")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("SignalR.Samples")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("11fe5eb3-05a0-48de-aee4-c0eaab706bb9")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SignalR.Samples/Raw/Raw.ashx b/SignalR.Samples/Raw/Raw.ashx new file mode 100644 index 000000000..b9e71f493 --- /dev/null +++ b/SignalR.Samples/Raw/Raw.ashx @@ -0,0 +1 @@ +<%@ WebHandler Language="C#" CodeBehind="Raw.ashx.cs" Class="SignalR.Samples.Raw.Raw" %> diff --git a/SignalR.Samples/Raw/Raw.ashx.cs b/SignalR.Samples/Raw/Raw.ashx.cs new file mode 100644 index 000000000..fb686218b --- /dev/null +++ b/SignalR.Samples/Raw/Raw.ashx.cs @@ -0,0 +1,111 @@ +using System.Collections.Generic; +using System.Web; +using System.Web.Script.Serialization; + +namespace SignalR.Samples.Raw { + public class Raw : PersistentConnection { + private static readonly Dictionary _users = new Dictionary(); + private static readonly Dictionary _clients = new Dictionary(); + + protected override void OnConnected(HttpContextBase context, string clientId) { + var cookie = context.Request.Cookies["user"]; + if (cookie != null) { + _clients[clientId] = cookie.Value; + _users[cookie.Value] = clientId; + } + + Connection.Broadcast(GetUser(clientId) + " joined").Wait(); + } + + protected override void OnDisconnect(string clientId) { + Connection.Broadcast(GetUser(clientId) + " disconnected"); + _users.Remove(clientId); + } + + protected override void OnReceived(string clientId, string data) { + var serializer = new JavaScriptSerializer(); + var message = serializer.Deserialize(data); + + switch (message.Type) { + case MessageType.Broadcast: + Connection.Broadcast(new { + type = MessageType.Broadcast, + from = GetUser(clientId), + data = message.Value + }); + break; + case MessageType.Send: + Send(new { + type = MessageType.Send, + from = GetUser(clientId), + data = message.Value + }); + break; + case MessageType.Join: + string name = message.Value; + _clients[clientId] = name; + _users[name] = clientId; + Send(new { + type = MessageType.Join, + data = message.Value + }); + break; + case MessageType.PrivateMessage: + var parts = message.Value.Split('|'); + string user = parts[0]; + string msg = parts[1]; + string id = GetClient(user); + Send(id, new { + from = GetUser(clientId), + data = msg + }); + break; + case MessageType.AddToGroup: + AddToGroup(clientId, message.Value); + break; + case MessageType.RemoveFromGroup: + RemoveFromGroup(clientId, message.Value); + break; + case MessageType.SendToGroup: + var parts2 = message.Value.Split('|'); + string groupName = parts2[0]; + string val = parts2[1]; + SendToGroup(groupName, val); + break; + default: + break; + } + } + + private string GetUser(string clientId) { + string user; + if (!_clients.TryGetValue(clientId, out user)) { + return clientId; + } + return user; + } + + private string GetClient(string user) { + string clientId; + if (_users.TryGetValue(user, out clientId)) { + return clientId; + } + return null; + } + + enum MessageType { + Send, + Broadcast, + Join, + PrivateMessage, + AddToGroup, + RemoveFromGroup, + SendToGroup + } + + class Message { + public MessageType Type { get; set; } + public string Value { get; set; } + } + } +} \ No newline at end of file diff --git a/SignalR.Samples/Raw/index.htm b/SignalR.Samples/Raw/index.htm new file mode 100644 index 000000000..d6b3adf38 --- /dev/null +++ b/SignalR.Samples/Raw/index.htm @@ -0,0 +1,119 @@ + + + + + SignalR Raw Connection Sample + + + + + + + + +

    To Everybody

    +
    + + + + + +
    + +

    To Me

    +
    + + +
    + +

    Private Message

    + +
    + + + + + + +
    + +
      +
    + + + diff --git a/SignalR.Samples/Scripts/SignalR.js b/SignalR.Samples/Scripts/SignalR.js new file mode 100644 index 000000000..053d728d2 --- /dev/null +++ b/SignalR.Samples/Scripts/SignalR.js @@ -0,0 +1,376 @@ +/// +(function ($, window) { + /// + "use strict"; + if (typeof (window.signalR) === "function") { + return; + } + + if (typeof ($) !== "function") { + // no jQuery! + throw "SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file."; + } + + if (!window.JSON) { + // no JSON! + throw "SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8."; + } + + var signalR; + + signalR = function (url) { + /// Creates a new SignalR connection for the given url + /// The URL of the long polling endpoint + /// + + return new signalR.fn.init(url); + }; + + signalR.fn = signalR.prototype = { + init: function (url) { + this.url = url; + }, + + start: function (options, callback) { + /// Starts the connection + /// Options map + /// A callback function to execute when the connection has started + /// + var connection = this, + config = { + transport: "auto" + }; + + if (connection.transport) { + return connection; + } + + if ($.type(options) === "function") { + // Support calling with single callback parameter + callback = options; + } else if ($.type(options) === "object") { + $.extend(config, options); + if ($.type(config.callback) === "function") { + callback = config.callback; + } + } + + if ($.type(callback) === "function") { + $(connection).bind("onStart", function (e, data) { + callback.call(connection); + }); + } + + var initialize = function (transports, index) { + index = index || 0; + if (index >= transports.length) { + if (!connection.transport) { + // No transport initialized successfully + throw "SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization."; + } + return; + } + + var transportName = transports[index], + transport = $.type(transportName) === "object" ? transportName : signalR.transports[transportName]; + + transport.start(connection, function () { + connection.transport = transport; + $(connection).trigger("onStart"); + }, function () { + initialize(transports, index + 1); + }); + }; + + window.setTimeout(function () { + $.post(connection.url + '/negotiate', {}, function (res) { + connection.appRelativeUrl = res.Url; + connection.clientId = res.ClientId; + + $(connection).trigger("onStarting"); + + var transports = [], + supportedTransports = []; + + $.each(signalR.transports, function (key) { + supportedTransports.push(key); + }); + + if ($.isArray(config.transport)) { + // ordered list provided + $.each(config.transport, function () { + var transport = this; + if ($.type(transport) === "object" || ($.type(transport) === "string" && $.inArray("" + transport, supportedTransports) >= 0)) { + transports.push($.type(transport) === "string" ? "" + transport : transport); + } + }); + } else if ($.type(config.transport) === "object" || + $.inArray(config.transport, supportedTransports) >= 0) { + // specific transport provided, as object or a named transport, e.g. "longPolling" + transports.push(config.transport); + } else { // default "auto" + transports = supportedTransports; + } + + initialize(transports); + }); + }, 0); + + return connection; + }, + + starting: function (callback) { + /// Adds a callback that will be invoked before the connection is started + /// A callback function to execute when the connection is starting + /// + var connection = this; + + $(connection).bind("onStarting", function (e, data) { + callback.call(connection); + }); + + return connection; + }, + + send: function (data) { + /// Sends data over the connection + /// The data to send over the connection + /// + var connection = this; + + if (!connection.transport) { + // Connection hasn't been started yet + throw "SignalR: Connection must be started before data can be sent. Call .start() before .send()"; + } + + connection.transport.send(connection, data); + + return connection; + }, + + sending: function (callback) { + /// Adds a callback that will be invoked before anything is sent over the connection + /// A callback function to execute before each time data is sent on the connection + /// + var connection = this; + $(connection).bind("onSending", function (e, data) { + callback.call(connection); + }); + return connection; + }, + + received: function (callback) { + /// Adds a callback that will be invoked after anything is received over the connection + /// A callback function to execute when any data is received on the connection + /// + var connection = this; + $(connection).bind("onReceived", function (e, data) { + callback.call(connection, data); + }); + return connection; + }, + + error: function (callback) { + /// Adds a callback that will be invoked after an error occurs with the connection + /// A callback function to execute when an error occurs on the connection + /// + var connection = this; + $(connection).bind("onError", function (e, data) { + callback.call(connection); + }); + return connection; + }, + + stop: function () { + /// Stops listening + /// + var connection = this; + + if (connection.transport) { + connection.transport.stop(connection); + connection.transport = null; + } + + return connection; + } + }; + + signalR.fn.init.prototype = signalR.fn; + + // Transports + signalR.transports = { + + webSockets: { + send: function (connection, data) { + connection.socket.send(data); + }, + + start: function (connection, onSuccess, onFailed) { + if ($.type(window.WebSocket) !== "object") { + onFailed(); + return; + } + + if (!connection.socket) { + // Build the url + var url = document.location.host + connection.appRelativeUrl; + + $(connection).trigger("onSending"); + if (connection.data) { + url += "?data=" + connection.data + "&transport=webSockets&clientId=" + connection.clientId; + } else { + url += "?transport=webSockets&clientId=" + connection.clientId; + } + + connection.socket = new window.WebSocket("ws://" + url); + var opened = false; + connection.socket.onopen = function () { + opened = true; + if (onSuccess) { + onSuccess(); + } + }; + + connection.socket.onclose = function (event) { + if (!opened) { + if (onFailed) { + onFailed(); + } + } + connection.socket = null; + }; + + connection.socket.onmessage = function (event) { + var data = window.JSON.parse(event.data); + if (data) { + if (data.Messages) { + $.each(data.Messages, function () { + $(connection).trigger("onReceived", [this]); + }); + } else { + $(connection).trigger("onReceived", [data]); + } + } + }; + } + }, + + stop: function (connection) { + if (connection.socket !== null) { + connection.socket.close(); + connection.socket = null; + } + } + }, + + longPolling: { + start: function (connection, onSuccess, onFailed) { + /// Starts the long polling connection + /// The SignalR connection to start + if (connection.pollXhr) { + connection.stop(); + } + + connection.messageId = null; + + window.setTimeout(function () { + (function poll(instance) { + $(instance).trigger("onSending"); + + var messageId = instance.messageId, + connect = (messageId === null), + url = instance.url + (connect ? "/connect" : ""); + + instance.pollXhr = $.ajax(url, { + type: "POST", + data: { + clientId: instance.clientId, + messageId: messageId, + data: instance.data, + transport: "longPolling", + groups: (instance.groups || []).toString() + }, + dataType: "json", + success: function (data) { + var delay = 0; + if (data) { + if (data.Messages) { + $.each(data.Messages, function () { + $(instance).trigger("onReceived", [this]); + }); + } + instance.messageId = data.MessageId; + if ($.type(data.TransportData.LongPollDelay) === "number") { + delay = data.TransportData.LongPollDelay; + } + instance.groups = data.TransportData.Groups; + } + if (delay > 0) { + window.setTimeout(function () { + poll(instance); + }, delay); + } else { + poll(instance); + } + }, + error: function (data, textStatus) { + if (textStatus === "abort") { + return; + } + + $(instance).trigger("onError", [data]); + + window.setTimeout(function () { + poll(instance); + }, 2 * 1000); + } + }); + } (connection)); + + // Now connected + onSuccess(); + + }, 250); // Have to delay initial poll so Chrome doesn't show loader spinner in tab + }, + + send: function (connection, data) { + /// Sends data over this connection + /// The SignalR connection to send data over + /// The data to send + /// A callback to be invoked when the send has completed + $.ajax(connection.url + '/send', { + type: "POST", + dataType: "json", + data: { + data: data, + transport: "longPolling", + clientId: connection.clientId + }, + success: function (result) { + if (result) { + $(connection).trigger("onReceived", [result]); + } + }, + error: function (data, textStatus) { + if (textStatus === "abort") { + return; + } + $(connection).trigger("onError", [data]); + } + }); + }, + + stop: function (connection) { + /// Stops the long polling connection + /// The SignalR connection to stop + if (connection.pollXhr) { + connection.pollXhr.abort(); + connection.pollXhr = null; + } + } + } + }; + + window.signalR = signalR; + +}(window.jQuery, window)); \ No newline at end of file diff --git a/SignalR.Samples/Scripts/SignalR.min.js b/SignalR.Samples/Scripts/SignalR.min.js new file mode 100644 index 000000000..b3b807c32 --- /dev/null +++ b/SignalR.Samples/Scripts/SignalR.min.js @@ -0,0 +1 @@ +(function(n,t){"use strict";if(typeof t.signalR=="function")return;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";var i;i=function(n){return new i.fn.init(n)},i.fn=i.prototype={init:function(n){this.url=n},start:function(r,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(r)==="function"?u=r:n.type(r)==="object"&&(n.extend(e,r),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,r){r=r||0;if(r>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[r],e=n.type(u)==="object"?u:i.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,r+1)})},t.setTimeout(function(){n.post(f.url+"/negotiate",{},function(t){f.appRelativeUrl=t.Url,f.clientId=t.ClientId,n(f).trigger("onStarting");var u=[],r=[];n.each(i.transports,function(n){r.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,r)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,r)<0?u=r:u.push(e.transport),o(u)})},0),f)},starting:function(t){var i=this;return n(i).bind("onStarting",function(){t.call(i)}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),n}},i.fn.init.prototype=i.fn,i.transports={webSockets:{send:function(n,t){n.socket.send(t)},start:function(i,r,u){var f,e;if(n.type(t.WebSocket)!=="object"){u();return}i.socket||(f=document.location.host+i.appRelativeUrl,n(i).trigger("onSending"),f+=i.data?"?data="+i.data+"&transport=webSockets&clientId="+i.clientId:"?transport=webSockets&clientId="+i.clientId,i.socket=new t.WebSocket("ws://"+f),e=!1,i.socket.onopen=function(){e=!0,r&&r()},i.socket.onclose=function(){e||u&&u(),i.socket=null},i.socket.onmessage=function(r){var u=t.JSON.parse(r.data);u&&(u.Messages?n.each(u.Messages,function(){n(i).trigger("onReceived",[this])}):n(i).trigger("onReceived",[u]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},longPolling:{start:function(i,r){i.pollXhr&&i.stop(),i.messageId=null,t.setTimeout(function(){(function u(i){n(i).trigger("onSending");var r=i.messageId,e=r===null,f=i.url+(e?"/connect":"");i.pollXhr=n.ajax(f,{type:"POST",data:{clientId:i.clientId,messageId:r,data:i.data,transport:"longPolling",groups:(i.groups||[]).toString()},dataType:"json",success:function(r){var f=0;r&&(r.Messages&&n.each(r.Messages,function(){n(i).trigger("onReceived",[this])}),i.messageId=r.MessageId,n.type(r.TransportData.LongPollDelay)==="number"&&(f=r.TransportData.LongPollDelay),i.groups=r.TransportData.Groups),f>0?t.setTimeout(function(){u(i)},f):u(i)},error:function(r,f){if(f==="abort")return;n(i).trigger("onError",[r]),t.setTimeout(function(){u(i)},2e3)}})})(i),r()},250)},send:function(t,i){n.ajax(t.url+"/send",{type:"POST",dataType:"json",data:{data:i,transport:"longPolling",clientId:t.clientId},success:function(i){i&&n(t).trigger("onReceived",[i])},error:function(i,r){if(r==="abort")return;n(t).trigger("onError",[i])}})},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},t.signalR=i})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR.Samples/Scripts/jQuery.tmpl.js b/SignalR.Samples/Scripts/jQuery.tmpl.js new file mode 100644 index 000000000..1c5b2d62f --- /dev/null +++ b/SignalR.Samples/Scripts/jQuery.tmpl.js @@ -0,0 +1,486 @@ +/* + * jQuery Templating Plugin + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + */ +(function( jQuery, undefined ){ + var oldManip = jQuery.fn.domManip, tmplItmAtt = "_tmplitem", htmlExpr = /^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /, + newTmplItems = {}, wrappedItems = {}, appendToTmplItems, topTmplItem = { key: 0, data: {} }, itemKey = 0, cloneIndex = 0, stack = []; + + function newTmplItem( options, parentItem, fn, data ) { + // Returns a template item data structure for a new rendered instance of a template (a 'template item'). + // The content field is a hierarchical array of strings and nested items (to be + // removed and replaced by nodes field of dom elements, once inserted in DOM). + var newItem = { + data: data || (parentItem ? parentItem.data : {}), + _wrap: parentItem ? parentItem._wrap : null, + tmpl: null, + parent: parentItem || null, + nodes: [], + calls: tiCalls, + nest: tiNest, + wrap: tiWrap, + html: tiHtml, + update: tiUpdate + }; + if ( options ) { + jQuery.extend( newItem, options, { nodes: [], parent: parentItem } ); + } + if ( fn ) { + // Build the hierarchical content to be used during insertion into DOM + newItem.tmpl = fn; + newItem._ctnt = newItem._ctnt || newItem.tmpl( jQuery, newItem ); + newItem.key = ++itemKey; + // Keep track of new template item, until it is stored as jQuery Data on DOM element + (stack.length ? wrappedItems : newTmplItems)[itemKey] = newItem; + } + return newItem; + } + + // Override appendTo etc., in order to provide support for targeting multiple elements. (This code would disappear if integrated in jquery core). + jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" + }, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var ret = [], insert = jQuery( selector ), elems, i, l, tmplItems, + parent = this.length === 1 && this[0].parentNode; + + appendToTmplItems = newTmplItems || {}; + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + ret = this; + } else { + for ( i = 0, l = insert.length; i < l; i++ ) { + cloneIndex = i; + elems = (i > 0 ? this.clone(true) : this).get(); + jQuery.fn[ original ].apply( jQuery(insert[i]), elems ); + ret = ret.concat( elems ); + } + cloneIndex = 0; + ret = this.pushStack( ret, name, insert.selector ); + } + tmplItems = appendToTmplItems; + appendToTmplItems = null; + jQuery.tmpl.complete( tmplItems ); + return ret; + }; + }); + + jQuery.fn.extend({ + // Use first wrapped element as template markup. + // Return wrapped set of template items, obtained by rendering template against data. + tmpl: function( data, options, parentItem ) { + return jQuery.tmpl( this[0], data, options, parentItem ); + }, + + // Find which rendered template item the first wrapped DOM element belongs to + tmplItem: function() { + return jQuery.tmplItem( this[0] ); + }, + + // Consider the first wrapped element as a template declaration, and get the compiled template or store it as a named template. + template: function( name ) { + return jQuery.template( name, this[0] ); + }, + + domManip: function( args, table, callback, options ) { + // This appears to be a bug in the appendTo, etc. implementation + // it should be doing .call() instead of .apply(). See #6227 + if ( args[0] && args[0].nodeType ) { + var dmArgs = jQuery.makeArray( arguments ), argsLength = args.length, i = 0, tmplItem; + while ( i < argsLength && !(tmplItem = jQuery.data( args[i++], "tmplItem" ))) {} + if ( argsLength > 1 ) { + dmArgs[0] = [jQuery.makeArray( args )]; + } + if ( tmplItem && cloneIndex ) { + dmArgs[2] = function( fragClone ) { + // Handler called by oldManip when rendered template has been inserted into DOM. + jQuery.tmpl.afterManip( this, fragClone, callback ); + }; + } + oldManip.apply( this, dmArgs ); + } else { + oldManip.apply( this, arguments ); + } + cloneIndex = 0; + if ( !appendToTmplItems ) { + jQuery.tmpl.complete( newTmplItems ); + } + return this; + } + }); + + jQuery.extend({ + // Return wrapped set of template items, obtained by rendering template against data. + tmpl: function( tmpl, data, options, parentItem ) { + var ret, topLevel = !parentItem; + if ( topLevel ) { + // This is a top-level tmpl call (not from a nested template using {{tmpl}}) + parentItem = topTmplItem; + tmpl = jQuery.template[tmpl] || jQuery.template( null, tmpl ); + wrappedItems = {}; // Any wrapped items will be rebuilt, since this is top level + } else if ( !tmpl ) { + // The template item is already associated with DOM - this is a refresh. + // Re-evaluate rendered template for the parentItem + tmpl = parentItem.tmpl; + newTmplItems[parentItem.key] = parentItem; + parentItem.nodes = []; + if ( parentItem.wrapped ) { + updateWrapped( parentItem, parentItem.wrapped ); + } + // Rebuild, without creating a new template item + return jQuery( build( parentItem, null, parentItem.tmpl( jQuery, parentItem ) )); + } + if ( !tmpl ) { + return []; // Could throw... + } + if ( typeof data === "function" ) { + data = data.call( parentItem || {} ); + } + if ( options && options.wrapped ) { + updateWrapped( options, options.wrapped ); + } + ret = jQuery.isArray( data ) ? + jQuery.map( data, function( dataItem ) { + return dataItem ? newTmplItem( options, parentItem, tmpl, dataItem ) : null; + }) : + [ newTmplItem( options, parentItem, tmpl, data ) ]; + return topLevel ? jQuery( build( parentItem, null, ret ) ) : ret; + }, + + // Return rendered template item for an element. + tmplItem: function( elem ) { + var tmplItem; + if ( elem instanceof jQuery ) { + elem = elem[0]; + } + while ( elem && elem.nodeType === 1 && !(tmplItem = jQuery.data( elem, "tmplItem" )) && (elem = elem.parentNode) ) {} + return tmplItem || topTmplItem; + }, + + // Set: + // Use $.template( name, tmpl ) to cache a named template, + // where tmpl is a template string, a script element or a jQuery instance wrapping a script element, etc. + // Use $( "selector" ).template( name ) to provide access by name to a script block template declaration. + + // Get: + // Use $.template( name ) to access a cached template. + // Also $( selectorToScriptBlock ).template(), or $.template( null, templateString ) + // will return the compiled template, without adding a name reference. + // If templateString includes at least one HTML tag, $.template( templateString ) is equivalent + // to $.template( null, templateString ) + template: function( name, tmpl ) { + if (tmpl) { + // Compile template and associate with name + if ( typeof tmpl === "string" ) { + // This is an HTML string being passed directly in. + tmpl = buildTmplFn( tmpl ) + } else if ( tmpl instanceof jQuery ) { + tmpl = tmpl[0] || {}; + } + if ( tmpl.nodeType ) { + // If this is a template block, use cached copy, or generate tmpl function and cache. + tmpl = jQuery.data( tmpl, "tmpl" ) || jQuery.data( tmpl, "tmpl", buildTmplFn( tmpl.innerHTML )); + } + return typeof name === "string" ? (jQuery.template[name] = tmpl) : tmpl; + } + // Return named compiled template + return name ? (typeof name !== "string" ? jQuery.template( null, name ): + (jQuery.template[name] || + // If not in map, treat as a selector. (If integrated with core, use quickExpr.exec) + jQuery.template( null, htmlExpr.test( name ) ? name : jQuery( name )))) : null; + }, + + encode: function( text ) { + // Do HTML encoding replacing < > & and ' and " by corresponding entities. + return ("" + text).split("<").join("<").split(">").join(">").split('"').join(""").split("'").join("'"); + } + }); + + jQuery.extend( jQuery.tmpl, { + tag: { + "tmpl": { + _default: { $2: "null" }, + open: "if($notnull_1){_=_.concat($item.nest($1,$2));}" + // tmpl target parameter can be of type function, so use $1, not $1a (so not auto detection of functions) + // This means that {{tmpl foo}} treats foo as a template (which IS a function). + // Explicit parens can be used if foo is a function that returns a template: {{tmpl foo()}}. + }, + "wrap": { + _default: { $2: "null" }, + open: "$item.calls(_,$1,$2);_=[];", + close: "call=$item.calls();_=call._.concat($item.wrap(call,_));" + }, + "each": { + _default: { $2: "$index, $value" }, + open: "if($notnull_1){$.each($1a,function($2){with(this){", + close: "}});}" + }, + "if": { + open: "if(($notnull_1) && $1a){", + close: "}" + }, + "else": { + _default: { $1: "true" }, + open: "}else if(($notnull_1) && $1a){" + }, + "html": { + // Unecoded expression evaluation. + open: "if($notnull_1){_.push($1a);}" + }, + "=": { + // Encoded expression evaluation. Abbreviated form is ${}. + _default: { $1: "$data" }, + open: "if($notnull_1){_.push($.encode($1a));}" + }, + "!": { + // Comment tag. Skipped by parser + open: "" + } + }, + + // This stub can be overridden, e.g. in jquery.tmplPlus for providing rendered events + complete: function( items ) { + newTmplItems = {}; + }, + + // Call this from code which overrides domManip, or equivalent + // Manage cloning/storing template items etc. + afterManip: function afterManip( elem, fragClone, callback ) { + // Provides cloned fragment ready for fixup prior to and after insertion into DOM + var content = fragClone.nodeType === 11 ? + jQuery.makeArray(fragClone.childNodes) : + fragClone.nodeType === 1 ? [fragClone] : []; + + // Return fragment to original caller (e.g. append) for DOM insertion + callback.call( elem, fragClone ); + + // Fragment has been inserted:- Add inserted nodes to tmplItem data structure. Replace inserted element annotations by jQuery.data. + storeTmplItems( content ); + cloneIndex++; + } + }); + + //========================== Private helper functions, used by code above ========================== + + function build( tmplItem, nested, content ) { + // Convert hierarchical content into flat string array + // and finally return array of fragments ready for DOM insertion + var frag, ret = content ? jQuery.map( content, function( item ) { + return (typeof item === "string") ? + // Insert template item annotations, to be converted to jQuery.data( "tmplItem" ) when elems are inserted into DOM. + (tmplItem.key ? item.replace( /(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g, "$1 " + tmplItmAtt + "=\"" + tmplItem.key + "\" $2" ) : item) : + // This is a child template item. Build nested template. + build( item, tmplItem, item._ctnt ); + }) : + // If content is not defined, insert tmplItem directly. Not a template item. May be a string, or a string array, e.g. from {{html $item.html()}}. + tmplItem; + if ( nested ) { + return ret; + } + + // top-level template + ret = ret.join(""); + + // Support templates which have initial or final text nodes, or consist only of text + // Also support HTML entities within the HTML markup. + ret.replace( /^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/, function( all, before, middle, after) { + frag = jQuery( middle ).get(); + + storeTmplItems( frag ); + if ( before ) { + frag = unencode( before ).concat(frag); + } + if ( after ) { + frag = frag.concat(unencode( after )); + } + }); + return frag ? frag : unencode( ret ); + } + + function unencode( text ) { + // Use createElement, since createTextNode will not render HTML entities correctly + var el = document.createElement( "div" ); + el.innerHTML = text; + return jQuery.makeArray(el.childNodes); + } + + // Generate a reusable function that will serve to render a template against data + function buildTmplFn( markup ) { + return new Function("jQuery","$item", + "var $=jQuery,call,_=[],$data=$item.data;" + + + // Introduce the data as local variables using with(){} + "with($data){_.push('" + + + // Convert the template into pure JavaScript + jQuery.trim(markup) + .replace( /([\\'])/g, "\\$1" ) + .replace( /[\r\t\n]/g, " " ) + .replace( /\$\{([^\}]*)\}/g, "{{= $1}}" ) + .replace( /\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g, + function( all, slash, type, fnargs, target, parens, args ) { + var tag = jQuery.tmpl.tag[ type ], def, expr, exprAutoFnDetect; + if ( !tag ) { + throw "Template command not found: " + type; + } + def = tag._default || []; + if ( parens && !/\w$/.test(target)) { + target += parens; + parens = ""; + } + if ( target ) { + target = unescape( target ); + args = args ? ("," + unescape( args ) + ")") : (parens ? ")" : ""); + // Support for target being things like a.toLowerCase(); + // In that case don't call with template item as 'this' pointer. Just evaluate... + expr = parens ? (target.indexOf(".") > -1 ? target + parens : ("(" + target + ").call($item" + args)) : target; + exprAutoFnDetect = parens ? expr : "(typeof(" + target + ")==='function'?(" + target + ").call($item):(" + target + "))"; + } else { + exprAutoFnDetect = expr = def.$1 || "null"; + } + fnargs = unescape( fnargs ); + return "');" + + tag[ slash ? "close" : "open" ] + .split( "$notnull_1" ).join( target ? "typeof(" + target + ")!=='undefined' && (" + target + ")!=null" : "true" ) + .split( "$1a" ).join( exprAutoFnDetect ) + .split( "$1" ).join( expr ) + .split( "$2" ).join( fnargs ? + fnargs.replace( /\s*([^\(]+)\s*(\((.*?)\))?/g, function( all, name, parens, params ) { + params = params ? ("," + params + ")") : (parens ? ")" : ""); + return params ? ("(" + name + ").call($item" + params) : all; + }) + : (def.$2||"") + ) + + "_.push('"; + }) + + "');}return _;" + ); + } + function updateWrapped( options, wrapped ) { + // Build the wrapped content. + options._wrap = build( options, true, + // Suport imperative scenario in which options.wrapped can be set to a selector or an HTML string. + jQuery.isArray( wrapped ) ? wrapped : [htmlExpr.test( wrapped ) ? wrapped : jQuery( wrapped ).html()] + ).join(""); + } + + function unescape( args ) { + return args ? args.replace( /\\'/g, "'").replace(/\\\\/g, "\\" ) : null; + } + function outerHtml( elem ) { + var div = document.createElement("div"); + div.appendChild( elem.cloneNode(true) ); + return div.innerHTML; + } + + // Store template items in jQuery.data(), ensuring a unique tmplItem data data structure for each rendered template instance. + function storeTmplItems( content ) { + var keySuffix = "_" + cloneIndex, elem, elems, newClonedItems = {}, i, l, m; + for ( i = 0, l = content.length; i < l; i++ ) { + if ( (elem = content[i]).nodeType !== 1 ) { + continue; + } + elems = elem.getElementsByTagName("*"); + for ( m = elems.length - 1; m >= 0; m-- ) { + processItemKey( elems[m] ); + } + processItemKey( elem ); + } + function processItemKey( el ) { + var pntKey, pntNode = el, pntItem, tmplItem, key; + // Ensure that each rendered template inserted into the DOM has its own template item, + if ( (key = el.getAttribute( tmplItmAtt ))) { + while ( pntNode.parentNode && (pntNode = pntNode.parentNode).nodeType === 1 && !(pntKey = pntNode.getAttribute( tmplItmAtt ))) { } + if ( pntKey !== key ) { + // The next ancestor with a _tmplitem expando is on a different key than this one. + // So this is a top-level element within this template item + // Set pntNode to the key of the parentNode, or to 0 if pntNode.parentNode is null, or pntNode is a fragment. + pntNode = pntNode.parentNode ? (pntNode.nodeType === 11 ? 0 : (pntNode.getAttribute( tmplItmAtt ) || 0)) : 0; + if ( !(tmplItem = newTmplItems[key]) ) { + // The item is for wrapped content, and was copied from the temporary parent wrappedItem. + tmplItem = wrappedItems[key]; + tmplItem = newTmplItem( tmplItem, newTmplItems[pntNode]||wrappedItems[pntNode], null, true ); + tmplItem.key = ++itemKey; + newTmplItems[itemKey] = tmplItem; + } + if ( cloneIndex ) { + cloneTmplItem( key ); + } + } + el.removeAttribute( tmplItmAtt ); + } else if ( cloneIndex && (tmplItem = jQuery.data( el, "tmplItem" )) ) { + // This was a rendered element, cloned during append or appendTo etc. + // TmplItem stored in jQuery data has already been cloned in cloneCopyEvent. We must replace it with a fresh cloned tmplItem. + cloneTmplItem( tmplItem.key ); + newTmplItems[tmplItem.key] = tmplItem; + pntNode = jQuery.data( el.parentNode, "tmplItem" ); + pntNode = pntNode ? pntNode.key : 0; + } + if ( tmplItem ) { + pntItem = tmplItem; + // Find the template item of the parent element. + // (Using !=, not !==, since pntItem.key is number, and pntNode may be a string) + while ( pntItem && pntItem.key != pntNode ) { + // Add this element as a top-level node for this rendered template item, as well as for any + // ancestor items between this item and the item of its parent element + pntItem.nodes.push( el ); + pntItem = pntItem.parent; + } + // Delete content built during rendering - reduce API surface area and memory use, and avoid exposing of stale data after rendering... + delete tmplItem._ctnt; + delete tmplItem._wrap; + // Store template item as jQuery data on the element + jQuery.data( el, "tmplItem", tmplItem ); + } + function cloneTmplItem( key ) { + key = key + keySuffix; + tmplItem = newClonedItems[key] = + (newClonedItems[key] || newTmplItem( tmplItem, newTmplItems[tmplItem.parent.key + keySuffix] || tmplItem.parent, null, true )); + } + } + } + + //---- Helper functions for template item ---- + + function tiCalls( content, tmpl, data, options ) { + if ( !content ) { + return stack.pop(); + } + stack.push({ _: content, tmpl: tmpl, item:this, data: data, options: options }); + } + + function tiNest( tmpl, data, options ) { + // nested template, using {{tmpl}} tag + return jQuery.tmpl( jQuery.template( tmpl ), data, options, this ); + } + + function tiWrap( call, wrapped ) { + // nested template, using {{wrap}} tag + var options = call.options || {}; + options.wrapped = wrapped; + // Apply the template, which may incorporate wrapped content, + return jQuery.tmpl( jQuery.template( call.tmpl ), call.data, options, call.item ); + } + + function tiHtml( filter, textOnly ) { + var wrapped = this._wrap; + return jQuery.map( + jQuery( jQuery.isArray( wrapped ) ? wrapped.join("") : wrapped ).filter( filter || "*" ), + function(e) { + return textOnly ? + e.innerText || e.textContent : + e.outerHTML || outerHtml(e); + }); + } + + function tiUpdate() { + var coll = this.nodes; + jQuery.tmpl( null, null, null, this).insertBefore( coll[0] ); + jQuery( coll ).remove(); + } +})( jQuery ); diff --git a/SignalR.Samples/Scripts/jQuery.tmpl.min.js b/SignalR.Samples/Scripts/jQuery.tmpl.min.js new file mode 100644 index 000000000..f08e81dcc --- /dev/null +++ b/SignalR.Samples/Scripts/jQuery.tmpl.min.js @@ -0,0 +1 @@ +(function(a){var r=a.fn.domManip,d="_tmplitem",q=/^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /,b={},f={},e,p={key:0,data:{}},h=0,c=0,l=[];function g(e,d,g,i){var c={data:i||(d?d.data:{}),_wrap:d?d._wrap:null,tmpl:null,parent:d||null,nodes:[],calls:u,nest:w,wrap:x,html:v,update:t};e&&a.extend(c,e,{nodes:[],parent:d});if(g){c.tmpl=g;c._ctnt=c._ctnt||c.tmpl(a,c);c.key=++h;(l.length?f:b)[h]=c}return c}a.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(f,d){a.fn[f]=function(n){var g=[],i=a(n),k,h,m,l,j=this.length===1&&this[0].parentNode;e=b||{};if(j&&j.nodeType===11&&j.childNodes.length===1&&i.length===1){i[d](this[0]);g=this}else{for(h=0,m=i.length;h0?this.clone(true):this).get();a.fn[d].apply(a(i[h]),k);g=g.concat(k)}c=0;g=this.pushStack(g,f,i.selector)}l=e;e=null;a.tmpl.complete(l);return g}});a.fn.extend({tmpl:function(d,c,b){return a.tmpl(this[0],d,c,b)},tmplItem:function(){return a.tmplItem(this[0])},template:function(b){return a.template(b,this[0])},domManip:function(d,l,j){if(d[0]&&d[0].nodeType){var f=a.makeArray(arguments),g=d.length,i=0,h;while(i1)f[0]=[a.makeArray(d)];if(h&&c)f[2]=function(b){a.tmpl.afterManip(this,b,j)};r.apply(this,f)}else r.apply(this,arguments);c=0;!e&&a.tmpl.complete(b);return this}});a.extend({tmpl:function(d,h,e,c){var j,k=!c;if(k){c=p;d=a.template[d]||a.template(null,d);f={}}else if(!d){d=c.tmpl;b[c.key]=c;c.nodes=[];c.wrapped&&n(c,c.wrapped);return a(i(c,null,c.tmpl(a,c)))}if(!d)return[];if(typeof h==="function")h=h.call(c||{});e&&e.wrapped&&n(e,e.wrapped);j=a.isArray(h)?a.map(h,function(a){return a?g(e,c,d,a):null}):[g(e,c,d,h)];return k?a(i(c,null,j)):j},tmplItem:function(b){var c;if(b instanceof a)b=b[0];while(b&&b.nodeType===1&&!(c=a.data(b,"tmplItem"))&&(b=b.parentNode));return c||p},template:function(c,b){if(b){if(typeof b==="string")b=o(b);else if(b instanceof a)b=b[0]||{};if(b.nodeType)b=a.data(b,"tmpl")||a.data(b,"tmpl",o(b.innerHTML));return typeof c==="string"?(a.template[c]=b):b}return c?typeof c!=="string"?a.template(null,c):a.template[c]||a.template(null,q.test(c)?c:a(c)):null},encode:function(a){return(""+a).split("<").join("<").split(">").join(">").split('"').join(""").split("'").join("'")}});a.extend(a.tmpl,{tag:{tmpl:{_default:{$2:"null"},open:"if($notnull_1){_=_.concat($item.nest($1,$2));}"},wrap:{_default:{$2:"null"},open:"$item.calls(_,$1,$2);_=[];",close:"call=$item.calls();_=call._.concat($item.wrap(call,_));"},each:{_default:{$2:"$index, $value"},open:"if($notnull_1){$.each($1a,function($2){with(this){",close:"}});}"},"if":{open:"if(($notnull_1) && $1a){",close:"}"},"else":{_default:{$1:"true"},open:"}else if(($notnull_1) && $1a){"},html:{open:"if($notnull_1){_.push($1a);}"},"=":{_default:{$1:"$data"},open:"if($notnull_1){_.push($.encode($1a));}"},"!":{open:""}},complete:function(){b={}},afterManip:function(f,b,d){var e=b.nodeType===11?a.makeArray(b.childNodes):b.nodeType===1?[b]:[];d.call(f,b);m(e);c++}});function i(e,g,f){var b,c=f?a.map(f,function(a){return typeof a==="string"?e.key?a.replace(/(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g,"$1 "+d+'="'+e.key+'" $2'):a:i(a,e,a._ctnt)}):e;if(g)return c;c=c.join("");c.replace(/^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/,function(f,c,e,d){b=a(e).get();m(b);if(c)b=j(c).concat(b);if(d)b=b.concat(j(d))});return b?b:j(c)}function j(c){var b=document.createElement("div");b.innerHTML=c;return a.makeArray(b.childNodes)}function o(b){return new Function("jQuery","$item","var $=jQuery,call,_=[],$data=$item.data;with($data){_.push('"+a.trim(b).replace(/([\\'])/g,"\\$1").replace(/[\r\t\n]/g," ").replace(/\$\{([^\}]*)\}/g,"{{= $1}}").replace(/\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g,function(m,l,j,d,b,c,e){var i=a.tmpl.tag[j],h,f,g;if(!i)throw"Template command not found: "+j;h=i._default||[];if(c&&!/\w$/.test(b)){b+=c;c=""}if(b){b=k(b);e=e?","+k(e)+")":c?")":"";f=c?b.indexOf(".")>-1?b+c:"("+b+").call($item"+e:b;g=c?f:"(typeof("+b+")==='function'?("+b+").call($item):("+b+"))"}else g=f=h.$1||"null";d=k(d);return"');"+i[l?"close":"open"].split("$notnull_1").join(b?"typeof("+b+")!=='undefined' && ("+b+")!=null":"true").split("$1a").join(g).split("$1").join(f).split("$2").join(d?d.replace(/\s*([^\(]+)\s*(\((.*?)\))?/g,function(d,c,b,a){a=a?","+a+")":b?")":"";return a?"("+c+").call($item"+a:d}):h.$2||"")+"_.push('"})+"');}return _;")}function n(c,b){c._wrap=i(c,true,a.isArray(b)?b:[q.test(b)?b:a(b).html()]).join("")}function k(a){return a?a.replace(/\\'/g,"'").replace(/\\\\/g,"\\"):null}function s(b){var a=document.createElement("div");a.appendChild(b.cloneNode(true));return a.innerHTML}function m(o){var n="_"+c,k,j,l={},e,p,i;for(e=0,p=o.length;e=0;i--)m(j[i]);m(k)}function m(j){var p,i=j,k,e,m;if(m=j.getAttribute(d)){while(i.parentNode&&(i=i.parentNode).nodeType===1&&!(p=i.getAttribute(d)));if(p!==m){i=i.parentNode?i.nodeType===11?0:i.getAttribute(d)||0:0;if(!(e=b[m])){e=f[m];e=g(e,b[i]||f[i],null,true);e.key=++h;b[h]=e}c&&o(m)}j.removeAttribute(d)}else if(c&&(e=a.data(j,"tmplItem"))){o(e.key);b[e.key]=e;i=a.data(j.parentNode,"tmplItem");i=i?i.key:0}if(e){k=e;while(k&&k.key!=i){k.nodes.push(j);k=k.parent}delete e._ctnt;delete e._wrap;a.data(j,"tmplItem",e)}function o(a){a=a+n;e=l[a]=l[a]||g(e,b[e.parent.key+n]||e.parent,null,true)}}}function u(a,d,c,b){if(!a)return l.pop();l.push({_:a,tmpl:d,item:this,data:c,options:b})}function w(d,c,b){return a.tmpl(a.template(d),c,b,this)}function x(b,d){var c=b.options||{};c.wrapped=d;return a.tmpl(a.template(b.tmpl),b.data,c,b.item)}function v(d,c){var b=this._wrap;return a.map(a(a.isArray(b)?b.join(""):b).filter(d||"*"),function(a){return c?a.innerText||a.textContent:a.outerHTML||s(a)})}function t(){var b=this.nodes;a.tmpl(null,null,null,this).insertBefore(b[0]);a(b).remove()}})(jQuery) \ No newline at end of file diff --git a/SignalR.Samples/Scripts/jquery-1.6.2-vsdoc.js b/SignalR.Samples/Scripts/jquery-1.6.2-vsdoc.js new file mode 100644 index 000000000..7b4fad84a --- /dev/null +++ b/SignalR.Samples/Scripts/jquery-1.6.2-vsdoc.js @@ -0,0 +1,6987 @@ +/* +* This file has been generated to support Visual Studio IntelliSense. +* You should not use this file at runtime inside the browser--it is only +* intended to be used only for design-time IntelliSense. Please use the +* standard jQuery library for all production use. +* +* Comment version: 1.6.2 +*/ + +/*! +* jQuery JavaScript Library v1.6.2 +* http://jquery.com/ +* +* Distributed in whole under the terms of the MIT +* +* Copyright 2010, John Resig +* +* 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. +* +* Includes Sizzle.js +* http://sizzlejs.com/ +* Copyright 2010, The Dojo Foundation +* Released under the MIT and BSD Licenses. +*/ + +(function ( window, undefined ) { +var jQuery = function( selector, context ) { +/// +/// 1: Accepts a string containing a CSS selector which is then used to match a set of elements. +/// 1.1 - $(selector, context) +/// 1.2 - $(element) +/// 1.3 - $(elementArray) +/// 1.4 - $(jQuery object) +/// 1.5 - $() +/// 2: Creates DOM elements on the fly from the provided string of raw HTML. +/// 2.1 - $(html, ownerDocument) +/// 2.2 - $(html, props) +/// 3: Binds a function to be executed when the DOM has finished loading. +/// 3.1 - $(callback) +/// +/// +/// A string containing a selector expression +/// +/// +/// A DOM Element, Document, or jQuery to use as context +/// +/// + + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }; +jQuery.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 ]( 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; + }; +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; +}; +jQuery._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; + }; +jQuery._data = function( elem, name, data ) { + + return jQuery.data( elem, name, data, true ); + }; +jQuery._mark = function( elem, type ) { + + if ( elem ) { + type = (type || "fx") + "mark"; + jQuery.data( elem, type, (jQuery.data(elem,type,undefined,true) || 0) + 1, true ); + } + }; +jQuery._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" ); + } + } + }; +jQuery.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.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; + }; +jQuery.active = 0; +jQuery.ajax = function( url, options ) { +/// +/// Perform an asynchronous HTTP (Ajax) request. +/// 1 - jQuery.ajax(url, settings) +/// 2 - jQuery.ajax(settings) +/// +/// +/// A string containing the URL to which the request is sent. +/// +/// +/// A set of key/value pairs that configure the Ajax request. All settings are optional. A default can be set for any option with $.ajaxSetup(). See jQuery.ajax( settings ) below for a complete list of all settings. +/// + + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + // Callbacks context + callbackContext = s.context || s, + // Context for global events + // It's the callbackContext if one was provided in the options + // and if it's a DOM node or a jQuery collection + globalEventContext = callbackContext !== s && + ( callbackContext.nodeType || callbackContext instanceof jQuery ) ? + jQuery( callbackContext ) : jQuery.event, + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery._Deferred(), + // Status-dependent callbacks + statusCode = s.statusCode || {}, + // ifModified key + ifModifiedKey, + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + // Response headers + responseHeadersString, + responseHeaders, + // transport + transport, + // timeout handle + timeoutTimer, + // Cross-domain detection vars + parts, + // The jqXHR state + state = 0, + // To know if global events are to be dispatched + fireGlobals, + // Loop variable + i, + // Fake xhr + jqXHR = { + + readyState: 0, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( !state ) { + var lname = name.toLowerCase(); + name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Raw string + getAllResponseHeaders: function() { + return state === 2 ? responseHeadersString : null; + }, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( state === 2 ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[1].toLowerCase() ] = match[ 2 ]; + } + } + match = responseHeaders[ key.toLowerCase() ]; + } + return match === undefined ? null : match; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( !state ) { + s.mimeType = type; + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + statusText = statusText || "abort"; + if ( transport ) { + transport.abort( statusText ); + } + done( 0, statusText ); + return this; + } + }; + + // Callback for when everything is done + // It is defined here because jslint complains if it is declared + // at the end of the function (which would be more logical and readable) + function done( status, statusText, responses, headers ) { + + // Called once + if ( state === 2 ) { + return; + } + + // State is "done" now + state = 2; + + // Clear timeout if it exists + if ( timeoutTimer ) { + clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status ? 4 : 0; + + var isSuccess, + success, + error, + response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined, + lastModified, + etag; + + // If successful, handle type chaining + if ( status >= 200 && status < 300 || status === 304 ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + + if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) { + jQuery.lastModified[ ifModifiedKey ] = lastModified; + } + if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) { + jQuery.etag[ ifModifiedKey ] = etag; + } + } + + // If not modified + if ( status === 304 ) { + + statusText = "notmodified"; + isSuccess = true; + + // If we have data + } else { + + try { + success = ajaxConvert( s, response ); + statusText = "success"; + isSuccess = true; + } catch(e) { + // We have a parsererror + statusText = "parsererror"; + error = e; + } + } + } else { + // We extract error from statusText + // then normalize statusText and status for non-aborts + error = statusText; + if( !statusText || status ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = statusText; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ), + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.resolveWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s] ); + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + // Attach deferreds + deferred.promise( jqXHR ); + jqXHR.success = jqXHR.done; + jqXHR.error = jqXHR.fail; + jqXHR.complete = completeDeferred.done; + + // Status-dependent callbacks + jqXHR.statusCode = function( map ) { + if ( map ) { + var tmp; + if ( state < 2 ) { + for( tmp in map ) { + statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ]; + } + } else { + tmp = map[ jqXHR.status ]; + jqXHR.then( tmp, tmp ); + } + } + return this; + }; + + // Remove hash character (#7531: and string promotion) + // Add protocol if not provided (#5866: IE7 issue with protocol-less urls) + // We also use the url parameter if available + s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); + + // Extract dataTypes list + s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax ); + + // Determine if a cross-domain request is in order + if ( s.crossDomain == null ) { + parts = rurl.exec( s.url.toLowerCase() ); + s.crossDomain = !!( parts && + ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] || + ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) != + ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) ) + ); + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefiler, stop there + if ( state === 2 ) { + return false; + } + + // We can fire global events as of now if asked to + fireGlobals = s.global; + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // If data is available, append data to url + if ( s.data ) { + s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data; + } + + // Get ifModifiedKey before adding the anti-cache parameter + ifModifiedKey = s.url; + + // Add anti-cache in url if needed + if ( s.cache === false ) { + + var ts = jQuery.now(), + // try replacing _= if it is there + ret = s.url.replace( rts, "$1_=" + ts ); + + // if nothing was replaced, add timestamp to the end + s.url = ret + ( (ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + ifModifiedKey = ifModifiedKey || s.url; + if ( jQuery.lastModified[ ifModifiedKey ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] ); + } + if ( jQuery.etag[ ifModifiedKey ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] ); + } + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ? + s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", */*; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { + // Abort if not done already + jqXHR.abort(); + return false; + + } + + // Install callbacks on deferreds + for ( i in { success: 1, error: 1, complete: 1 } ) { + jqXHR[ i ]( s[ i ] ); + } + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = setTimeout( function(){ + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + state = 1; + transport.send( requestHeaders, done ); + } catch (e) { + // Propagate exception as error if not done + if ( status < 2 ) { + done( -1, e ); + // Simply rethrow otherwise + } else { + jQuery.error( e ); + } + } + } + + return jqXHR; + }; +jQuery.ajaxPrefilter = function( dataTypeExpression, func ) { +/// +/// Handle custom Ajax options or modify existing options before each request is sent and before they are processed by $.ajax(). +/// +/// +/// An optional string containing one or more space-separated dataTypes +/// +/// +/// A handler to set default values for future Ajax requests. +/// +/// + + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + if ( jQuery.isFunction( func ) ) { + var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ), + i = 0, + length = dataTypes.length, + dataType, + list, + placeBefore; + + // For each dataType in the dataTypeExpression + for(; i < length; i++ ) { + dataType = dataTypes[ i ]; + // We control if we're asked to add before + // any existing element + placeBefore = /^\+/.test( dataType ); + if ( placeBefore ) { + dataType = dataType.substr( 1 ) || "*"; + } + list = structure[ dataType ] = structure[ dataType ] || []; + // then we add to the structure accordingly + list[ placeBefore ? "unshift" : "push" ]( func ); + } + } + }; +jQuery.ajaxSettings = { "url": 'http://localhost:25813/?ver=1.6.2&newLineMethod=para', +"isLocal": false, +"global": true, +"type": 'GET', +"contentType": 'application/x-www-form-urlencoded', +"processData": true, +"async": true, +"accepts": {}, +"contents": {}, +"responseFields": {}, +"converters": {}, +"jsonp": 'callback' }; +jQuery.ajaxSetup = function ( target, settings ) { +/// +/// Set default values for future Ajax requests. +/// +/// +/// A set of key/value pairs that configure the default Ajax request. All options are optional. +/// + + if ( !settings ) { + // Only one parameter, we extend ajaxSettings + settings = target; + target = jQuery.extend( true, jQuery.ajaxSettings, settings ); + } else { + // target was provided, we extend into it + jQuery.extend( true, target, jQuery.ajaxSettings, settings ); + } + // Flatten fields we don't want deep extended + for( var field in { context: 1, url: 1 } ) { + if ( field in settings ) { + target[ field ] = settings[ field ]; + } else if( field in jQuery.ajaxSettings ) { + target[ field ] = jQuery.ajaxSettings[ field ]; + } + } + return target; + }; +jQuery.ajaxTransport = function( dataTypeExpression, func ) { + + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + if ( jQuery.isFunction( func ) ) { + var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ), + i = 0, + length = dataTypes.length, + dataType, + list, + placeBefore; + + // For each dataType in the dataTypeExpression + for(; i < length; i++ ) { + dataType = dataTypes[ i ]; + // We control if we're asked to add before + // any existing element + placeBefore = /^\+/.test( dataType ); + if ( placeBefore ) { + dataType = dataType.substr( 1 ) || "*"; + } + list = structure[ dataType ] = structure[ dataType ] || []; + // then we add to the structure accordingly + list[ placeBefore ? "unshift" : "push" ]( func ); + } + } + }; +jQuery.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 formHook for forms and if the name contains certain characters + } else if ( formHook && name !== "className" && + (jQuery.nodeName( elem, "form" ) || rinvalidChar.test( name )) ) { + + hooks = formHook; + } + } + } + + 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; + } + }; +jQuery.attrFix = { "tabindex": 'tabIndex' }; +jQuery.attrFn = { "val": true, +"css": true, +"html": true, +"text": true, +"data": true, +"width": true, +"height": true, +"offset": true, +"blur": true, +"focus": true, +"focusin": true, +"focusout": true, +"load": true, +"resize": true, +"scroll": true, +"unload": true, +"click": true, +"dblclick": true, +"mousedown": true, +"mouseup": true, +"mousemove": true, +"mouseover": true, +"mouseout": true, +"mouseenter": true, +"mouseleave": true, +"change": true, +"select": true, +"submit": true, +"keydown": true, +"keypress": true, +"keyup": true, +"error": true }; +jQuery.attrHooks = { "type": {}, +"tabIndex": {}, +"value": {} }; +jQuery.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(); + } + } + }; +jQuery.boxModel = true; +jQuery.browser = { "msie": true, +"version": '9.0' }; +jQuery.buildFragment = function( args, nodes, scripts ) { + + var fragment, cacheable, cacheresults, doc; + + // nodes may contain either an explicit document object, + // a jQuery collection or context object. + // If nodes[0] contains a valid object to assign to doc + if ( nodes && nodes[0] ) { + doc = nodes[0].ownerDocument || nodes[0]; + } + + // Ensure that an attr object doesn't incorrectly stand in as a document object + // Chrome and Firefox seem to allow this to occur and will throw exception + // Fixes #8950 + if ( !doc.createDocumentFragment ) { + doc = document; + } + + // Only cache "small" (1/2 KB) HTML strings that are associated with the main document + // Cloning options loses the selected state, so don't cache them + // IE 6 doesn't like it when you put or elements in a fragment + // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache + if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document && + args[0].charAt(0) === "<" && !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) { + + cacheable = true; + + cacheresults = jQuery.fragments[ args[0] ]; + if ( cacheresults && cacheresults !== 1 ) { + fragment = cacheresults; + } + } + + if ( !fragment ) { + fragment = doc.createDocumentFragment(); + jQuery.clean( args, doc, fragment, scripts ); + } + + if ( cacheable ) { + jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1; + } + + return { fragment: fragment, cacheable: cacheable }; +}; +jQuery.cache = {}; +jQuery.camelCase = function( string ) { + + return string.replace( rdashAlpha, fcamelCase ); + }; +jQuery.clean = function( elems, context, fragment, scripts ) { + + var checkScriptType; + + context = context || document; + + // !context.createElement fails in IE with an error but returns typeof 'object' + if ( typeof context.createElement === "undefined" ) { + context = context.ownerDocument || context[0] && context[0].ownerDocument || document; + } + + var ret = [], j; + + for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { + if ( typeof elem === "number" ) { + elem += ""; + } + + if ( !elem ) { + continue; + } + + // Convert html string into DOM nodes + if ( typeof elem === "string" ) { + if ( !rhtml.test( elem ) ) { + elem = context.createTextNode( elem ); + } else { + // Fix "XHTML"-style tags in all browsers + elem = elem.replace(rxhtmlTag, "<$1>"); + + // Trim whitespace, otherwise indexOf won't work as expected + var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(), + wrap = wrapMap[ tag ] || wrapMap._default, + depth = wrap[0], + div = context.createElement("div"); + + // Go to html and back, then peel off extra wrappers + div.innerHTML = wrap[1] + elem + wrap[2]; + + // Move to the right depth + while ( depth-- ) { + div = div.lastChild; + } + + // Remove IE's autoinserted from table fragments + if ( !jQuery.support.tbody ) { + + // String was a , *may* have spurious + var hasBody = rtbody.test(elem), + tbody = tag === "table" && !hasBody ? + div.firstChild && div.firstChild.childNodes : + + // String was a bare or + wrap[1] === "
    " && !hasBody ? + div.childNodes : + []; + + for ( j = tbody.length - 1; j >= 0 ; --j ) { + if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) { + tbody[ j ].parentNode.removeChild( tbody[ j ] ); + } + } + } + + // IE completely kills leading whitespace when innerHTML is used + if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild ); + } + + elem = div.childNodes; + } + } + + // Resets defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + var len; + if ( !jQuery.support.appendChecked ) { + if ( elem[0] && typeof (len = elem.length) === "number" ) { + for ( j = 0; j < len; j++ ) { + findInputs( elem[j] ); + } + } else { + findInputs( elem ); + } + } + + if ( elem.nodeType ) { + ret.push( elem ); + } else { + ret = jQuery.merge( ret, elem ); + } + } + + if ( fragment ) { + checkScriptType = function( elem ) { + return !elem.type || rscriptType.test( elem.type ); + }; + for ( i = 0; ret[i]; i++ ) { + if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) { + scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] ); + + } else { + if ( ret[i].nodeType === 1 ) { + var jsTags = jQuery.grep( ret[i].getElementsByTagName( "script" ), checkScriptType ); + + ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) ); + } + fragment.appendChild( ret[i] ); + } + } + } + + return ret; + }; +jQuery.cleanData = function( elems ) { + + var data, id, cache = jQuery.cache, internalKey = jQuery.expando, special = jQuery.event.special, + deleteExpando = jQuery.support.deleteExpando; + + for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { + if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) { + continue; + } + + id = elem[ jQuery.expando ]; + + if ( id ) { + data = cache[ id ] && cache[ id ][ internalKey ]; + + if ( data && data.events ) { + for ( var type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + + // Null the DOM reference to avoid IE6/7/8 leak (#7054) + if ( data.handle ) { + data.handle.elem = null; + } + } + + if ( deleteExpando ) { + delete elem[ jQuery.expando ]; + + } else if ( elem.removeAttribute ) { + elem.removeAttribute( jQuery.expando ); + } + + delete cache[ id ]; + } + } + }; +jQuery.clone = function( elem, dataAndEvents, deepDataAndEvents ) { + + var clone = elem.cloneNode(true), + srcElements, + destElements, + i; + + if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + // IE copies events bound via attachEvent when using cloneNode. + // Calling detachEvent on the clone will also remove the events + // from the original. In order to get around this, we use some + // proprietary methods to clear the events. Thanks to MooTools + // guys for this hotness. + + cloneFixAttributes( elem, clone ); + + // Using Sizzle here is crazy slow, so we use getElementsByTagName + // instead + srcElements = getAll( elem ); + destElements = getAll( clone ); + + // Weird iteration because IE will replace the length property + // with an element if you are cloning the body and one of the + // elements on the page has a name or id of "length" + for ( i = 0; srcElements[i]; ++i ) { + cloneFixAttributes( srcElements[i], destElements[i] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + cloneCopyEvent( elem, clone ); + + if ( deepDataAndEvents ) { + srcElements = getAll( elem ); + destElements = getAll( clone ); + + for ( i = 0; srcElements[i]; ++i ) { + cloneCopyEvent( srcElements[i], destElements[i] ); + } + } + } + + srcElements = destElements = null; + + // Return the cloned set + return clone; + }; +jQuery.contains = function( a, b ) { +/// +/// Check to see if a DOM node is within another DOM node. +/// +/// +/// The DOM element that may contain the other element. +/// +/// +/// The DOM node that may be contained by the other element. +/// +/// + + return a !== b && (a.contains ? a.contains(b) : true); + }; +jQuery.css = function( elem, name, extra ) { + + var ret, hooks; + + // Make sure that we're working with the right name + name = jQuery.camelCase( name ); + hooks = jQuery.cssHooks[ name ]; + name = jQuery.cssProps[ name ] || name; + + // cssFloat needs a special treatment + if ( name === "cssFloat" ) { + name = "float"; + } + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) { + return ret; + + // Otherwise, if a way to get the computed value exists, use that + } else if ( curCSS ) { + return curCSS( elem, name ); + } + }; +jQuery.cssHooks = { "opacity": {}, +"height": {}, +"width": {} }; +jQuery.cssNumber = { "fillOpacity": true, +"fontWeight": true, +"lineHeight": true, +"opacity": true, +"orphans": true, +"widows": true, +"zIndex": true, +"zoom": true }; +jQuery.cssProps = { "float": 'cssFloat' }; +jQuery.curCSS = function( elem, name, extra ) { + + var ret, hooks; + + // Make sure that we're working with the right name + name = jQuery.camelCase( name ); + hooks = jQuery.cssHooks[ name ]; + name = jQuery.cssProps[ name ] || name; + + // cssFloat needs a special treatment + if ( name === "cssFloat" ) { + name = "float"; + } + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) { + return ret; + + // Otherwise, if a way to get the computed value exists, use that + } else if ( curCSS ) { + return curCSS( elem, name ); + } + }; +jQuery.data = function( elem, name, data, pvt /* Internal Use Only */ ) { +/// +/// 1: Store arbitrary data associated with the specified element. Returns the value that was set. +/// 1.1 - jQuery.data(element, key, value) +/// 2: Returns value at named data store for the element, as set by jQuery.data(element, name, value), or the full data store for the element. +/// 2.1 - jQuery.data(element, key) +/// 2.2 - jQuery.data(element) +/// +/// +/// The DOM element to associate with the data. +/// +/// +/// A string naming the piece of data to set. +/// +/// +/// The new data value. +/// +/// + + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache, + + // 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 ][ 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; + } + + return getByName ? + // Check for both converted-to-camel and non-converted data property names + thisCache[ jQuery.camelCase( name ) ] || thisCache[ name ] : + thisCache; + }; +jQuery.dequeue = function( elem, type ) { +/// +/// Execute the next function on the queue for the matched element. +/// +/// +/// A DOM element from which to remove and execute a queued function. +/// +/// +/// A string containing the name of the queue. Defaults to fx, the standard effects queue. +/// +/// + + 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.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; + }; +jQuery.each = function( object, callback, args ) { +/// +/// A generic iterator function, which can be used to seamlessly iterate over both objects and arrays. Arrays and array-like objects with a length property (such as a function's arguments object) are iterated by numeric index, from 0 to length-1. Other objects are iterated via their named properties. +/// +/// +/// The object or array to iterate over. +/// +/// +/// The function that will be executed on every object. +/// +/// + + 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; + }; +jQuery.easing = {}; +jQuery.error = function( msg ) { +/// +/// Takes a string and throws an exception containing it. +/// +/// +/// The message to send out. +/// + + throw msg; + }; +jQuery.etag = {}; +jQuery.event = { "global": {}, +"customEvent": {}, +"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'], +"guid": 100000000, +"special": {}, +"triggered": }; +jQuery.expr = { "order": ['ID','CLASS','NAME','TAG'], +"match": {}, +"leftMatch": {}, +"attrMap": {}, +"attrHandle": {}, +"relative": {}, +"find": {}, +"preFilter": {}, +"filters": {}, +"setFilters": {}, +"filter": {}, +":": {} }; +jQuery.extend = function() { +/// +/// Merge the contents of two or more objects together into the first object. +/// 1 - jQuery.extend(target, object1, objectN) +/// 2 - jQuery.extend(deep, target, object1, objectN) +/// +/// +/// If true, the merge becomes recursive (aka. deep copy). +/// +/// +/// The object to extend. It will receive the new properties. +/// +/// +/// An object containing additional properties to merge in. +/// +/// +/// Additional objects containing properties to merge in. +/// +/// + + 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.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); + }; +jQuery.find = 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); + }; +jQuery.fn = { "selector": '', +"jquery": '1.6.2', +"length": 0 }; +jQuery.fragments = {}; +jQuery.fx = function( elem, options, prop ) { + + this.options = options; + this.elem = elem; + this.prop = prop; + + options.orig = options.orig || {}; + }; +jQuery.get = function( url, data, callback, type ) { +/// +/// Load data from the server using a HTTP GET request. +/// +/// +/// A string containing the URL to which the request is sent. +/// +/// +/// A map or string that is sent to the server with the request. +/// +/// +/// A callback function that is executed if the request succeeds. +/// +/// +/// The type of data expected from the server. Default: Intelligent Guess (xml, json, script, or html). +/// + + // shift arguments if data argument was omitted + if ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + return jQuery.ajax({ + type: method, + url: url, + data: data, + success: callback, + dataType: type + }); + }; +jQuery.getJSON = function( url, data, callback ) { +/// +/// Load JSON-encoded data from the server using a GET HTTP request. +/// +/// +/// A string containing the URL to which the request is sent. +/// +/// +/// A map or string that is sent to the server with the request. +/// +/// +/// A callback function that is executed if the request succeeds. +/// + + return jQuery.get( url, data, callback, "json" ); + }; +jQuery.getScript = function( url, callback ) { +/// +/// Load a JavaScript file from the server using a GET HTTP request, then execute it. +/// +/// +/// A string containing the URL to which the request is sent. +/// +/// +/// A callback function that is executed if the request succeeds. +/// +/// + + return jQuery.get( url, undefined, callback, "script" ); + }; +jQuery.globalEval = function( data ) { +/// +/// Execute some JavaScript code globally. +/// +/// +/// The JavaScript code to execute. +/// + + 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 ); + } + }; +jQuery.grep = function( elems, callback, inv ) { +/// +/// Finds the elements of an array which satisfy a filter function. The original array is not affected. +/// +/// +/// The array to search through. +/// +/// +/// The function to process each item against. The first argument to the function is the item, and the second argument is the index. The function should return a Boolean value. this will be the global window object. +/// +/// +/// If "invert" is false, or not provided, then the function returns an array consisting of all elements for which "callback" returns true. If "invert" is true, then the function returns an array consisting of all elements for which "callback" returns false. +/// +/// + + 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; + }; +jQuery.guid = 1; +jQuery.hasData = function( elem ) { +/// +/// Determine whether an element has any jQuery data associated with it. +/// +/// +/// A DOM element to be checked for data. +/// +/// + + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + + return !!elem && !isEmptyDataObject( elem ); + }; +jQuery.holdReady = function( hold ) { +/// +/// Holds or releases the execution of jQuery's ready event. +/// +/// +/// Indicates whether the ready hold is being requested or released +/// +/// + + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }; +jQuery.inArray = function( elem, array ) { +/// +/// Search for a specified value within an array and return its index (or -1 if not found). +/// +/// +/// The value to search for. +/// +/// +/// An array through which to search. +/// +/// + + + 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; + }; +jQuery.isEmptyObject = function( obj ) { +/// +/// Check to see if an object is empty (contains no properties). +/// +/// +/// The object that will be checked to see if it's empty. +/// +/// + + for ( var name in obj ) { + return false; + } + return true; + }; +jQuery.isFunction = function( obj ) { +/// +/// Determine if the argument passed is a Javascript function object. +/// +/// +/// Object to test whether or not it is a function. +/// +/// + + return jQuery.type(obj) === "function"; + }; +jQuery.isNaN = function( obj ) { + + return obj == null || !rdigit.test( obj ) || isNaN( obj ); + }; +jQuery.isPlainObject = function( obj ) { +/// +/// Check to see if an object is a plain object (created using "{}" or "new Object"). +/// +/// +/// The object that will be checked to see if it's a plain object. +/// +/// + + // 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; + } + + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + 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 ); + }; +jQuery.isReady = true; +jQuery.isWindow = function( obj ) { +/// +/// Determine whether the argument is a window. +/// +/// +/// Object to test whether or not it is a window. +/// +/// + + return obj && typeof obj === "object" && "setInterval" in obj; + }; +jQuery.isXMLDoc = function( elem ) { +/// +/// Check to see if a DOM node is within an XML document (or is an XML document). +/// +/// +/// The DOM node that will be checked to see if it's in an XML document. +/// +/// + + // 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; +}; +jQuery.lastModified = {}; +jQuery.makeArray = function( array, results ) { +/// +/// Convert an array-like object into a true JavaScript array. +/// +/// +/// Any object to turn into a native Array. +/// +/// + + 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; + }; +jQuery.map = function( elems, callback, arg ) { +/// +/// Translate all items in an array or object to new array of items. +/// 1 - jQuery.map(array, callback(elementOfArray, indexInArray)) +/// 2 - jQuery.map(arrayOrObject, callback( value, indexOrKey )) +/// +/// +/// The Array to translate. +/// +/// +/// The function to process each item against. The first argument to the function is the array item, the second argument is the index in array The function can return any value. Within the function, this refers to the global (window) object. +/// +/// + + 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 ); + }; +jQuery.merge = function( first, second ) { +/// +/// Merge the contents of two arrays together into the first array. +/// +/// +/// The first array to merge, the elements of second added. +/// +/// +/// The second array to merge into the first, unaltered. +/// +/// + + 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; + }; +jQuery.noConflict = function( deep ) { +/// +/// Relinquish jQuery's control of the $ variable. +/// +/// +/// A Boolean indicating whether to remove all jQuery variables from the global scope (including jQuery itself). +/// +/// + + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }; +jQuery.noData = { "embed": true, +"object": 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000', +"applet": true }; +jQuery.nodeName = function( elem, name ) { + + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }; +jQuery.noop = function() { +/// +/// An empty function. +/// +/// +}; +jQuery.now = function() { +/// +/// Return a number representing the current time. +/// +/// + + return (new Date()).getTime(); + }; +jQuery.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; + }; +jQuery.offset = {}; +jQuery.param = function( a, traditional ) { +/// +/// Create a serialized representation of an array or object, suitable for use in a URL query string or Ajax request. +/// 1 - jQuery.param(obj) +/// 2 - jQuery.param(obj, traditional) +/// +/// +/// An array or object to serialize. +/// +/// +/// A Boolean indicating whether to perform a traditional "shallow" serialization. +/// +/// + + var s = [], + add = function( key, value ) { + // If value is a function, invoke it and return its value + value = jQuery.isFunction( value ) ? value() : value; + s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value ); + }; + + // Set traditional to true for jQuery <= 1.3.2 behavior. + if ( traditional === undefined ) { + traditional = jQuery.ajaxSettings.traditional; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + }); + + } else { + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( var prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ).replace( r20, "+" ); + }; +jQuery.parseJSON = function( data ) { +/// +/// Takes a well-formed JSON string and returns the resulting JavaScript object. +/// +/// +/// The JSON string to parse. +/// +/// + + 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 ); + }; +jQuery.parseXML = function( data , xml , tmp ) { +/// +/// Parses a string into an XML document. +/// +/// +/// a well-formed XML string to be parsed +/// +/// + + + 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 ); + } + + tmp = xml.documentElement; + + if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) { + jQuery.error( "Invalid XML: " + data ); + } + + return xml; + }; +jQuery.post = function( url, data, callback, type ) { +/// +/// Load data from the server using a HTTP POST request. +/// +/// +/// A string containing the URL to which the request is sent. +/// +/// +/// A map or string that is sent to the server with the request. +/// +/// +/// A callback function that is executed if the request succeeds. +/// +/// +/// The type of data expected from the server. Default: Intelligent Guess (xml, json, script, or html). +/// + + // shift arguments if data argument was omitted + if ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + return jQuery.ajax({ + type: method, + url: url, + data: data, + success: callback, + dataType: type + }); + }; +jQuery.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 )) !== undefined ) { + return ret; + + } else { + return elem[ name ]; + } + } + }; +jQuery.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' }; +jQuery.propHooks = { "selected": {} }; +jQuery.proxy = function( fn, context ) { +/// +/// Takes a function and returns a new one that will always have a particular context. +/// 1 - jQuery.proxy(function, context) +/// 2 - jQuery.proxy(context, name) +/// +/// +/// The function whose context will be changed. +/// +/// +/// The object to which the context (this) of the function should be set. +/// +/// + + 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; + }; +jQuery.queue = function( elem, type, data ) { +/// +/// 1: Show the queue of functions to be executed on the matched element. +/// 1.1 - jQuery.queue(element, queueName) +/// 2: Manipulate the queue of functions to be executed on the matched element. +/// 2.1 - jQuery.queue(element, queueName, newQueue) +/// 2.2 - jQuery.queue(element, queueName, callback()) +/// +/// +/// A DOM element where the array of queued functions is attached. +/// +/// +/// A string containing the name of the queue. Defaults to fx, the standard effects queue. +/// +/// +/// An array of functions to replace the current queue contents. +/// +/// + + 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 || []; + } + }; +jQuery.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" ); + } + } + }; +jQuery.readyWait = 0; +jQuery.removeAttr = function( elem, name ) { + + var propName; + if ( elem.nodeType === 1 ) { + name = jQuery.attrFix[ name ] || name; + + if ( jQuery.support.getSetAttribute ) { + // Use removeAttribute in browsers that support it + elem.removeAttribute( name ); + } else { + jQuery.attr( elem, name, "" ); + elem.removeAttributeNode( elem.getAttributeNode( name ) ); + } + + // Set corresponding property to false for boolean attributes + if ( rboolean.test( name ) && (propName = jQuery.propFix[ name ] || name) in elem ) { + elem[ propName ] = false; + } + } + }; +jQuery.removeData = function( elem, name, pvt /* Internal Use Only */ ) { +/// +/// Remove a previously-stored piece of data. +/// +/// +/// A DOM element from which to remove data. +/// +/// +/// A string naming the piece of data to remove. +/// +/// + + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var 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 ) { + var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ]; + + if ( thisCache ) { + 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 + if ( jQuery.support.deleteExpando || cache != window ) { + 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; + } + } + }; +jQuery.removeEvent = function( elem, type, handle ) { + + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + }; +jQuery.sibling = function( n, elem ) { + + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + }; +jQuery.speed = function( speed, easing, fn ) { + + var opt = speed && typeof speed === "object" ? jQuery.extend({}, speed) : { + complete: fn || !fn && easing || + jQuery.isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !jQuery.isFunction(easing) && easing + }; + + opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : + opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[opt.duration] : jQuery.fx.speeds._default; + + // Queueing + opt.old = opt.complete; + opt.complete = function( noUnmark ) { + if ( jQuery.isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue !== false ) { + jQuery.dequeue( this ); + } else if ( noUnmark !== false ) { + jQuery._unmark( this ); + } + }; + + return opt; + }; +jQuery.style = function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, origName = jQuery.camelCase( name ), + style = elem.style, hooks = jQuery.cssHooks[ origName ]; + + name = jQuery.cssProps[ origName ] || origName; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Make sure that NaN and null values aren't set. See: #7116 + if ( type === "number" && isNaN( value ) || value == null ) { + return; + } + + // convert relative number strings (+= or -=) to relative numbers. #7345 + if ( type === "string" && rrelNum.test( value ) ) { + value = +value.replace( rrelNumFilter, "" ) + parseFloat( jQuery.css( elem, name ) ); + // Fixes bug #9237 + type = "number"; + } + + // If a number was passed in, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) { + // Wrapped to prevent IE from throwing errors when 'invalid' values are provided + // Fixes bug #5509 + try { + style[ name ] = value; + } catch(e) {} + } + + } else { + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }; +jQuery.sub = function() { +/// +/// Creates a new copy of jQuery whose properties and methods can be modified without affecting the original jQuery object. +/// +/// + + 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; + }; +jQuery.support = { "leadingWhitespace": true, +"tbody": true, +"htmlSerialize": true, +"style": true, +"hrefNormalized": true, +"opacity": true, +"cssFloat": true, +"checkOn": true, +"optSelected": false, +"getSetAttribute": true, +"submitBubbles": true, +"changeBubbles": true, +"focusinBubbles": true, +"deleteExpando": true, +"noCloneEvent": true, +"inlineBlockNeedsLayout": false, +"shrinkWrapBlocks": false, +"reliableMarginRight": true, +"noCloneChecked": false, +"optDisabled": true, +"radioValue": false, +"checkClone": , +"appendChecked": true, +"boxModel": true, +"reliableHiddenOffsets": true, +"ajax": true, +"cors": false }; +jQuery.swap = function( elem, options, callback ) { + + var old = {}; + + // Remember the old values, and insert the new ones + for ( var name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + }; +jQuery.text = 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; +}; +jQuery.trim = function( text ) { +/// +/// Remove the whitespace from the beginning and end of a string. +/// +/// +/// The string to trim. +/// +/// + + return text == null ? + "" : + trim.call( text ); + }; +jQuery.type = function( obj ) { +/// +/// Determine the internal JavaScript [[Class]] of an object. +/// +/// +/// Object to get the internal JavaScript [[Class]] of. +/// +/// + + return obj == null ? + String( obj ) : + class2type[ toString.call(obj) ] || "object"; + }; +jQuery.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" }; + }; +jQuery.unique = function( results ) { +/// +/// Sorts an array of DOM elements, in place, with the duplicates removed. Note that this only works on arrays of DOM elements, not strings or numbers. +/// +/// +/// The Array of DOM elements. +/// +/// + + 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; +}; +jQuery.uuid = 0; +jQuery.valHooks = { "option": {}, +"select": {}, +"radio": {}, +"checkbox": {} }; +jQuery.when = function( firstParam ) { +/// +/// Provides a way to execute callback functions based on one or more objects, usually Deferred objects that represent asynchronous events. +/// +/// +/// One or more Deferred objects, or plain JavaScript objects. +/// +/// + + 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.Event.prototype.isDefaultPrevented = function returnFalse() { +/// +/// Returns whether event.preventDefault() was ever called on this event object. +/// +/// + + return false; +}; +jQuery.Event.prototype.isImmediatePropagationStopped = function returnFalse() { +/// +/// Returns whether event.stopImmediatePropagation() was ever called on this event object. +/// +/// + + return false; +}; +jQuery.Event.prototype.isPropagationStopped = function returnFalse() { +/// +/// Returns whether event.stopPropagation() was ever called on this event object. +/// +/// + + return false; +}; +jQuery.Event.prototype.preventDefault = function() { +/// +/// If this method is called, the default action of the event will not be triggered. +/// +/// + + 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; + } + }; +jQuery.Event.prototype.stopImmediatePropagation = function() { +/// +/// Keeps the rest of the handlers from being executed and prevents the event from bubbling up the DOM tree. +/// + + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }; +jQuery.Event.prototype.stopPropagation = function() { +/// +/// Prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event. +/// + + 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; + }; +jQuery.prototype._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 ); + }; +jQuery.prototype.add = function( selector, context ) { +/// +/// Add elements to the set of matched elements. +/// 1 - add(selector) +/// 2 - add(elements) +/// 3 - add(html) +/// 4 - add(jQuery object) +/// 5 - add(selector, context) +/// +/// +/// A string representing a selector expression to find additional elements to add to the set of matched elements. +/// +/// +/// The point in the document at which the selector should begin matching; similar to the context argument of the $(selector, context) method. +/// +/// + + 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 ) ); + }; +jQuery.prototype.addClass = function( value ) { +/// +/// Adds the specified class(es) to each of the set of matched elements. +/// 1 - addClass(className) +/// 2 - addClass(function(index, currentClass)) +/// +/// +/// One or more class names to be added to the class attribute of each matched element. +/// +/// + + 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; + }; +jQuery.prototype.after = function() { +/// +/// Insert content, specified by the parameter, after each element in the set of matched elements. +/// 1 - after(content, content) +/// 2 - after(function(index)) +/// +/// +/// HTML string, DOM element, or jQuery object to insert after each element in the set of matched elements. +/// +/// +/// One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert after each element in the set of matched elements. +/// +/// + + if ( this[0] && this[0].parentNode ) { + return this.domManip(arguments, false, function( elem ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + }); + } else if ( arguments.length ) { + var set = this.pushStack( this, "after", arguments ); + set.push.apply( set, jQuery(arguments[0]).toArray() ); + return set; + } + }; +jQuery.prototype.ajaxComplete = function( f ){ +/// +/// Register a handler to be called when Ajax requests complete. This is an Ajax Event. +/// +/// +/// The function to be invoked. +/// +/// + + return this.bind( o, f ); + }; +jQuery.prototype.ajaxError = function( f ){ +/// +/// Register a handler to be called when Ajax requests complete with an error. This is an Ajax Event. +/// +/// +/// The function to be invoked. +/// +/// + + return this.bind( o, f ); + }; +jQuery.prototype.ajaxSend = function( f ){ +/// +/// Attach a function to be executed before an Ajax request is sent. This is an Ajax Event. +/// +/// +/// The function to be invoked. +/// +/// + + return this.bind( o, f ); + }; +jQuery.prototype.ajaxStart = function( f ){ +/// +/// Register a handler to be called when the first Ajax request begins. This is an Ajax Event. +/// +/// +/// The function to be invoked. +/// +/// + + return this.bind( o, f ); + }; +jQuery.prototype.ajaxStop = function( f ){ +/// +/// Register a handler to be called when all Ajax requests have completed. This is an Ajax Event. +/// +/// +/// The function to be invoked. +/// +/// + + return this.bind( o, f ); + }; +jQuery.prototype.ajaxSuccess = function( f ){ +/// +/// Attach a function to be executed whenever an Ajax request completes successfully. This is an Ajax Event. +/// +/// +/// The function to be invoked. +/// +/// + + return this.bind( o, f ); + }; +jQuery.prototype.andSelf = function() { +/// +/// Add the previous set of elements on the stack to the current set. +/// +/// + + return this.add( this.prevObject ); + }; +jQuery.prototype.animate = function( prop, speed, easing, callback ) { +/// +/// Perform a custom animation of a set of CSS properties. +/// 1 - animate(properties, duration, easing, complete) +/// 2 - animate(properties, options) +/// +/// +/// A map of CSS properties that the animation will move toward. +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + var optall = jQuery.speed(speed, easing, callback); + + if ( jQuery.isEmptyObject( prop ) ) { + return this.each( optall.complete, [ false ] ); + } + + // Do not change referenced properties as per-property easing will be lost + prop = jQuery.extend( {}, prop ); + + return this[ optall.queue === false ? "each" : "queue" ](function() { + // XXX 'this' does not always have a nodeName when running the + // test suite + + if ( optall.queue === false ) { + jQuery._mark( this ); + } + + var opt = jQuery.extend( {}, optall ), + isElement = this.nodeType === 1, + hidden = isElement && jQuery(this).is(":hidden"), + name, val, p, + display, e, + parts, start, end, unit; + + // will store per property easing and be used to determine when an animation is complete + opt.animatedProperties = {}; + + for ( p in prop ) { + + // property name normalization + name = jQuery.camelCase( p ); + if ( p !== name ) { + prop[ name ] = prop[ p ]; + delete prop[ p ]; + } + + val = prop[ name ]; + + // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default) + if ( jQuery.isArray( val ) ) { + opt.animatedProperties[ name ] = val[ 1 ]; + val = prop[ name ] = val[ 0 ]; + } else { + opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing'; + } + + if ( val === "hide" && hidden || val === "show" && !hidden ) { + return opt.complete.call( this ); + } + + if ( isElement && ( name === "height" || name === "width" ) ) { + // Make sure that nothing sneaks out + // Record all 3 overflow attributes because IE does not + // change the overflow attribute when overflowX and + // overflowY are set to the same value + opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ]; + + // Set display property to inline-block for height/width + // animations on inline elements that are having width/height + // animated + if ( jQuery.css( this, "display" ) === "inline" && + jQuery.css( this, "float" ) === "none" ) { + if ( !jQuery.support.inlineBlockNeedsLayout ) { + this.style.display = "inline-block"; + + } else { + display = defaultDisplay( this.nodeName ); + + // inline-level elements accept inline-block; + // block-level elements need to be inline with layout + if ( display === "inline" ) { + this.style.display = "inline-block"; + + } else { + this.style.display = "inline"; + this.style.zoom = 1; + } + } + } + } + } + + if ( opt.overflow != null ) { + this.style.overflow = "hidden"; + } + + for ( p in prop ) { + e = new jQuery.fx( this, opt, p ); + val = prop[ p ]; + + if ( rfxtypes.test(val) ) { + e[ val === "toggle" ? hidden ? "show" : "hide" : val ](); + + } else { + parts = rfxnum.exec( val ); + start = e.cur(); + + if ( parts ) { + end = parseFloat( parts[2] ); + unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" ); + + // We need to compute starting value + if ( unit !== "px" ) { + jQuery.style( this, p, (end || 1) + unit); + start = ((end || 1) / e.cur()) * start; + jQuery.style( this, p, start + unit); + } + + // If a +=/-= token was provided, we're doing a relative animation + if ( parts[1] ) { + end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start; + } + + e.custom( start, end, unit ); + + } else { + e.custom( start, val, "" ); + } + } + } + + // For JS strict compliance + return true; + }); + }; +jQuery.prototype.append = function() { +/// +/// Insert content, specified by the parameter, to the end of each element in the set of matched elements. +/// 1 - append(content, content) +/// 2 - append(function(index, html)) +/// +/// +/// DOM element, HTML string, or jQuery object to insert at the end of each element in the set of matched elements. +/// +/// +/// One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert at the end of each element in the set of matched elements. +/// +/// + + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 ) { + this.appendChild( elem ); + } + }); + }; +jQuery.prototype.appendTo = function( selector ) { +/// +/// Insert every element in the set of matched elements to the end of the target. +/// +/// +/// A selector, element, HTML string, or jQuery object; the matched set of elements will be inserted at the end of the element(s) specified by this parameter. +/// +/// + + var ret = [], + insert = jQuery( selector ), + parent = this.length === 1 && this[0].parentNode; + + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + return this; + + } else { + for ( var i = 0, l = insert.length; i < l; i++ ) { + var elems = (i > 0 ? this.clone(true) : this).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +jQuery.prototype.attr = function( name, value ) { +/// +/// 1: Get the value of an attribute for the first element in the set of matched elements. +/// 1.1 - attr(attributeName) +/// 2: Set one or more attributes for the set of matched elements. +/// 2.1 - attr(attributeName, value) +/// 2.2 - attr(map) +/// 2.3 - attr(attributeName, function(index, attr)) +/// +/// +/// The name of the attribute to set. +/// +/// +/// A value to set for the attribute. +/// +/// + + return jQuery.access( this, name, value, true, jQuery.attr ); + }; +jQuery.prototype.before = function() { +/// +/// Insert content, specified by the parameter, before each element in the set of matched elements. +/// 1 - before(content, content) +/// 2 - before(function) +/// +/// +/// HTML string, DOM element, or jQuery object to insert before each element in the set of matched elements. +/// +/// +/// One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert before each element in the set of matched elements. +/// +/// + + if ( this[0] && this[0].parentNode ) { + return this.domManip(arguments, false, function( elem ) { + this.parentNode.insertBefore( elem, this ); + }); + } else if ( arguments.length ) { + var set = jQuery(arguments[0]); + set.push.apply( set, this.toArray() ); + return this.pushStack( set, "before", arguments ); + } + }; +jQuery.prototype.bind = function( type, data, fn ) { +/// +/// Attach a handler to an event for the elements. +/// 1 - bind(eventType, eventData, handler(eventObject)) +/// 2 - bind(eventType, eventData, false) +/// 3 - bind(events) +/// +/// +/// A string containing one or more JavaScript event types, such as "click" or "submit," or custom event names. +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + 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.prototype.blur = function( data, fn ) { +/// +/// Bind an event handler to the "blur" JavaScript event, or trigger that event on an element. +/// 1 - blur(handler(eventObject)) +/// 2 - blur(eventData, handler(eventObject)) +/// 3 - blur() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.change = function( data, fn ) { +/// +/// Bind an event handler to the "change" JavaScript event, or trigger that event on an element. +/// 1 - change(handler(eventObject)) +/// 2 - change(eventData, handler(eventObject)) +/// 3 - change() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.children = function( until, selector ) { +/// +/// Get the children of each element in the set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.clearQueue = function( type ) { +/// +/// Remove from the queue all items that have not yet been run. +/// +/// +/// A string containing the name of the queue. Defaults to fx, the standard effects queue. +/// +/// + + return this.queue( type || "fx", [] ); + }; +jQuery.prototype.click = function( data, fn ) { +/// +/// Bind an event handler to the "click" JavaScript event, or trigger that event on an element. +/// 1 - click(handler(eventObject)) +/// 2 - click(eventData, handler(eventObject)) +/// 3 - click() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.clone = function( dataAndEvents, deepDataAndEvents ) { +/// +/// Create a deep copy of the set of matched elements. +/// 1 - clone(withDataAndEvents) +/// 2 - clone(withDataAndEvents, deepWithDataAndEvents) +/// +/// +/// A Boolean indicating whether event handlers and data should be copied along with the elements. The default value is false. *For 1.5.0 the default value is incorrectly true. This will be changed back to false in 1.5.1 and up. +/// +/// +/// A Boolean indicating whether event handlers and data for all children of the cloned element should be copied. By default its value matches the first argument's value (which defaults to false). +/// +/// + + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function () { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }; +jQuery.prototype.closest = function( selectors, context ) { +/// +/// 1: Get the first ancestor element that matches the selector, beginning at the current element and progressing up through the DOM tree. +/// 1.1 - closest(selector) +/// 1.2 - closest(selector, context) +/// 1.3 - closest(jQuery object) +/// 1.4 - closest(element) +/// 2: Gets an array of all the elements and selectors matched against the current element up through the DOM tree. +/// 2.1 - closest(selectors, context) +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// +/// A DOM element within which a matching element may be found. If no context is passed in then the context of the jQuery set will be used instead. +/// +/// + + 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 ); + }; +jQuery.prototype.constructor = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }; +jQuery.prototype.contents = function( until, selector ) { +/// +/// Get the children of each element in the set of matched elements, including text and comment nodes. +/// +/// + + 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.prototype.css = function( name, value ) { +/// +/// 1: Get the value of a style property for the first element in the set of matched elements. +/// 1.1 - css(propertyName) +/// 2: Set one or more CSS properties for the set of matched elements. +/// 2.1 - css(propertyName, value) +/// 2.2 - css(propertyName, function(index, value)) +/// 2.3 - css(map) +/// +/// +/// A CSS property name. +/// +/// +/// A value to set for the property. +/// +/// + + // Setting 'undefined' is a no-op + if ( arguments.length === 2 && value === undefined ) { + return this; + } + + return jQuery.access( this, name, value, true, function( elem, name, value ) { + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }); +}; +jQuery.prototype.data = function( key, value ) { +/// +/// 1: Store arbitrary data associated with the matched elements. +/// 1.1 - data(key, value) +/// 1.2 - data(obj) +/// 2: Returns value at named data store for the first element in the jQuery collection, as set by data(name, value). +/// 2.1 - data(key) +/// 2.2 - data() +/// +/// +/// A string naming the piece of data to set. +/// +/// +/// The new data value; it can be any Javascript type including Array or Object. +/// +/// + + 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 ); + }); + } + }; +jQuery.prototype.dblclick = function( data, fn ) { +/// +/// Bind an event handler to the "dblclick" JavaScript event, or trigger that event on an element. +/// 1 - dblclick(handler(eventObject)) +/// 2 - dblclick(eventData, handler(eventObject)) +/// 3 - dblclick() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.delay = function( time, type ) { +/// +/// Set a timer to delay execution of subsequent items in the queue. +/// +/// +/// An integer indicating the number of milliseconds to delay execution of the next item in the queue. +/// +/// +/// A string containing the name of the queue. Defaults to fx, the standard effects queue. +/// +/// + + 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 ); + }); + }; +jQuery.prototype.delegate = function( selector, types, data, fn ) { +/// +/// Attach a handler to one or more events for all elements that match the selector, now or in the future, based on a specific set of root elements. +/// 1 - delegate(selector, eventType, handler) +/// 2 - delegate(selector, eventType, eventData, handler) +/// 3 - delegate(selector, events) +/// +/// +/// A selector to filter the elements that trigger the event. +/// +/// +/// A string containing one or more space-separated JavaScript event types, such as "click" or "keydown," or custom event names. +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute at the time the event is triggered. +/// +/// + + return this.live( types, data, fn, selector ); + }; +jQuery.prototype.dequeue = function( type ) { +/// +/// Execute the next function on the queue for the matched elements. +/// +/// +/// A string containing the name of the queue. Defaults to fx, the standard effects queue. +/// +/// + + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }; +jQuery.prototype.detach = function( selector ) { +/// +/// Remove the set of matched elements from the DOM. +/// +/// +/// A selector expression that filters the set of matched elements to be removed. +/// +/// + + return this.remove( selector, true ); + }; +jQuery.prototype.die = function( types, data, fn, origSelector /* Internal Use Only */ ) { +/// +/// 1: Remove all event handlers previously attached using .live() from the elements. +/// 1.1 - die() +/// 2: Remove an event handler previously attached using .live() from the elements. +/// 2.1 - die(eventType, handler) +/// 2.2 - die(eventTypes) +/// +/// +/// A string containing a JavaScript event type, such as click or keydown. +/// +/// +/// The function that is no longer to be executed. +/// +/// + + 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; + }; +jQuery.prototype.domManip = function( args, table, callback ) { + + var results, first, fragment, parent, + value = args[0], + scripts = []; + + // We can't cloneNode fragments that contain checked, in WebKit + if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) { + return this.each(function() { + jQuery(this).domManip( args, table, callback, true ); + }); + } + + if ( jQuery.isFunction(value) ) { + return this.each(function(i) { + var self = jQuery(this); + args[0] = value.call(this, i, table ? self.html() : undefined); + self.domManip( args, table, callback ); + }); + } + + if ( this[0] ) { + parent = value && value.parentNode; + + // If we're in a fragment, just use that instead of building a new one + if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) { + results = { fragment: parent }; + + } else { + results = jQuery.buildFragment( args, this, scripts ); + } + + fragment = results.fragment; + + if ( fragment.childNodes.length === 1 ) { + first = fragment = fragment.firstChild; + } else { + first = fragment.firstChild; + } + + if ( first ) { + table = table && jQuery.nodeName( first, "tr" ); + + for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) { + callback.call( + table ? + root(this[i], first) : + this[i], + // Make sure that we do not leak memory by inadvertently discarding + // the original fragment (which might have attached data) instead of + // using it; in addition, use the original fragment object for the last + // item instead of first because it can end up being emptied incorrectly + // in certain situations (Bug #8070). + // Fragments from the fragment cache must always be cloned and never used + // in place. + results.cacheable || (l > 1 && i < lastIndex) ? + jQuery.clone( fragment, true, true ) : + fragment + ); + } + } + + if ( scripts.length ) { + jQuery.each( scripts, evalScript ); + } + } + + return this; + }; +jQuery.prototype.each = function( callback, args ) { +/// +/// Iterate over a jQuery object, executing a function for each matched element. +/// +/// +/// A function to execute for each matched element. +/// +/// + + return jQuery.each( this, callback, args ); + }; +jQuery.prototype.empty = function() { +/// +/// Remove all child nodes of the set of matched elements from the DOM. +/// +/// + + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName("*") ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + } + + return this; + }; +jQuery.prototype.end = function() { +/// +/// End the most recent filtering operation in the current chain and return the set of matched elements to its previous state. +/// +/// + + return this.prevObject || this.constructor(null); + }; +jQuery.prototype.eq = function( i ) { +/// +/// Reduce the set of matched elements to the one at the specified index. +/// 1 - eq(index) +/// 2 - eq(-index) +/// +/// +/// An integer indicating the 0-based position of the element. +/// +/// + + return i === -1 ? + this.slice( i ) : + this.slice( i, +i + 1 ); + }; +jQuery.prototype.error = function( data, fn ) { +/// +/// Bind an event handler to the "error" JavaScript event. +/// 1 - error(handler(eventObject)) +/// 2 - error(eventData, handler(eventObject)) +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.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.prototype.fadeIn = function( speed, easing, callback ) { +/// +/// Display the matched elements by fading them to opaque. +/// 1 - fadeIn(duration, callback) +/// 2 - fadeIn(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.animate( props, speed, easing, callback ); + }; +jQuery.prototype.fadeOut = function( speed, easing, callback ) { +/// +/// Hide the matched elements by fading them to transparent. +/// 1 - fadeOut(duration, callback) +/// 2 - fadeOut(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.animate( props, speed, easing, callback ); + }; +jQuery.prototype.fadeTo = function( speed, to, easing, callback ) { +/// +/// Adjust the opacity of the matched elements. +/// 1 - fadeTo(duration, opacity, callback) +/// 2 - fadeTo(duration, opacity, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A number between 0 and 1 denoting the target opacity. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.filter(":hidden").css("opacity", 0).show().end() + .animate({opacity: to}, speed, easing, callback); + }; +jQuery.prototype.fadeToggle = function( speed, easing, callback ) { +/// +/// Display or hide the matched elements by animating their opacity. +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.animate( props, speed, easing, callback ); + }; +jQuery.prototype.filter = function( selector ) { +/// +/// Reduce the set of matched elements to those that match the selector or pass the function's test. +/// 1 - filter(selector) +/// 2 - filter(function(index)) +/// 3 - filter(element) +/// 4 - filter(jQuery object) +/// +/// +/// A string containing a selector expression to match the current set of elements against. +/// +/// + + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }; +jQuery.prototype.find = function( selector ) { +/// +/// Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element. +/// 1 - find(selector) +/// 2 - find(jQuery object) +/// 3 - find(element) +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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; + }; +jQuery.prototype.first = function() { +/// +/// Reduce the set of matched elements to the first in the set. +/// +/// + + return this.eq( 0 ); + }; +jQuery.prototype.focus = function( data, fn ) { +/// +/// Bind an event handler to the "focus" JavaScript event, or trigger that event on an element. +/// 1 - focus(handler(eventObject)) +/// 2 - focus(eventData, handler(eventObject)) +/// 3 - focus() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.focusin = function( data, fn ) { +/// +/// Bind an event handler to the "focusin" JavaScript event. +/// 1 - focusin(handler(eventObject)) +/// 2 - focusin(eventData, handler(eventObject)) +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.focusout = function( data, fn ) { +/// +/// Bind an event handler to the "focusout" JavaScript event. +/// 1 - focusout(handler(eventObject)) +/// 2 - focusout(eventData, handler(eventObject)) +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.get = function( num ) { +/// +/// Retrieve the DOM elements matched by the jQuery object. +/// +/// +/// A zero-based integer indicating which element to retrieve. +/// +/// + + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }; +jQuery.prototype.has = function( target ) { +/// +/// Reduce the set of matched elements to those that have a descendant that matches the selector or DOM element. +/// 1 - has(selector) +/// 2 - has(contained) +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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; + } + } + }); + }; +jQuery.prototype.hasClass = function( selector ) { +/// +/// Determine whether any of the matched elements are assigned the given class. +/// +/// +/// The class name to search for. +/// +/// + + var className = " " + selector + " "; + for ( var i = 0, l = this.length; i < l; i++ ) { + if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }; +jQuery.prototype.height = function( size ) { +/// +/// 1: Get the current computed height for the first element in the set of matched elements. +/// 1.1 - height() +/// 2: Set the CSS height of every matched element. +/// 2.1 - height(value) +/// 2.2 - height(function(index, height)) +/// +/// +/// An integer representing the number of pixels, or an integer with an optional unit of measure appended (as a string). +/// +/// + + // Get window width or height + var elem = this[0]; + if ( !elem ) { + return size == null ? null : this; + } + + if ( jQuery.isFunction( size ) ) { + return this.each(function( i ) { + var self = jQuery( this ); + self[ type ]( size.call( this, i, self[ type ]() ) ); + }); + } + + if ( jQuery.isWindow( elem ) ) { + // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode + // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat + var docElemProp = elem.document.documentElement[ "client" + name ]; + return elem.document.compatMode === "CSS1Compat" && docElemProp || + elem.document.body[ "client" + name ] || docElemProp; + + // Get document width or height + } else if ( elem.nodeType === 9 ) { + // Either scroll[Width/Height] or offset[Width/Height], whichever is greater + return Math.max( + elem.documentElement["client" + name], + elem.body["scroll" + name], elem.documentElement["scroll" + name], + elem.body["offset" + name], elem.documentElement["offset" + name] + ); + + // Get or set width or height on the element + } else if ( size === undefined ) { + var orig = jQuery.css( elem, type ), + ret = parseFloat( orig ); + + return jQuery.isNaN( ret ) ? orig : ret; + + // Set the width or height on the element (default to pixels if value is unitless) + } else { + return this.css( type, typeof size === "string" ? size : size + "px" ); + } + }; +jQuery.prototype.hide = function( speed, easing, callback ) { +/// +/// Hide the matched elements. +/// 1 - hide() +/// 2 - hide(duration, callback) +/// 3 - hide(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + if ( speed || speed === 0 ) { + return this.animate( genFx("hide", 3), speed, easing, callback); + + } else { + for ( var i = 0, j = this.length; i < j; i++ ) { + if ( this[i].style ) { + var display = jQuery.css( this[i], "display" ); + + if ( display !== "none" && !jQuery._data( this[i], "olddisplay" ) ) { + jQuery._data( this[i], "olddisplay", display ); + } + } + } + + // Set the display of the elements in a second loop + // to avoid the constant reflow + for ( i = 0; i < j; i++ ) { + if ( this[i].style ) { + this[i].style.display = "none"; + } + } + + return this; + } + }; +jQuery.prototype.hover = function( fnOver, fnOut ) { +/// +/// 1: Bind two handlers to the matched elements, to be executed when the mouse pointer enters and leaves the elements. +/// 1.1 - hover(handlerIn(eventObject), handlerOut(eventObject)) +/// 2: Bind a single handler to the matched elements, to be executed when the mouse pointer enters or leaves the elements. +/// 2.1 - hover(handlerInOut(eventObject)) +/// +/// +/// A function to execute when the mouse pointer enters the element. +/// +/// +/// A function to execute when the mouse pointer leaves the element. +/// +/// + + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + }; +jQuery.prototype.html = function( value ) { +/// +/// 1: Get the HTML contents of the first element in the set of matched elements. +/// 1.1 - html() +/// 2: Set the HTML contents of each element in the set of matched elements. +/// 2.1 - html(htmlString) +/// 2.2 - html(function(index, oldhtml)) +/// +/// +/// A string of HTML to set as the content of each matched element. +/// +/// + + if ( value === undefined ) { + return this[0] && this[0].nodeType === 1 ? + this[0].innerHTML.replace(rinlinejQuery, "") : + null; + + // See if we can take a shortcut and just use innerHTML + } else if ( typeof value === "string" && !rnocache.test( value ) && + (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) && + !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) { + + value = value.replace(rxhtmlTag, "<$1>"); + + try { + for ( var i = 0, l = this.length; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + if ( this[i].nodeType === 1 ) { + jQuery.cleanData( this[i].getElementsByTagName("*") ); + this[i].innerHTML = value; + } + } + + // If using innerHTML throws an exception, use the fallback method + } catch(e) { + this.empty().append( value ); + } + + } else if ( jQuery.isFunction( value ) ) { + this.each(function(i){ + var self = jQuery( this ); + + self.html( value.call(this, i, self.html()) ); + }); + + } else { + this.empty().append( value ); + } + + return this; + }; +jQuery.prototype.index = function( elem ) { +/// +/// Search for a given element from among the matched elements. +/// 1 - index() +/// 2 - index(selector) +/// 3 - index(element) +/// +/// +/// A selector representing a jQuery collection in which to look for an element. +/// +/// + + if ( !elem || typeof elem === "string" ) { + return jQuery.inArray( this[0], + // If it receives a string, the selector is used + // If it receives nothing, the siblings are used + elem ? jQuery( elem ) : this.parent().children() ); + } + // 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 ); + }; +jQuery.prototype.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 ); + }; +jQuery.prototype.innerHeight = function() { +/// +/// Get the current computed height for the first element in the set of matched elements, including padding but not border. +/// +/// + + var elem = this[0]; + return elem && elem.style ? + parseFloat( jQuery.css( elem, type, "padding" ) ) : + null; + }; +jQuery.prototype.innerWidth = function() { +/// +/// Get the current computed width for the first element in the set of matched elements, including padding but not border. +/// +/// + + var elem = this[0]; + return elem && elem.style ? + parseFloat( jQuery.css( elem, type, "padding" ) ) : + null; + }; +jQuery.prototype.insertAfter = function( selector ) { +/// +/// Insert every element in the set of matched elements after the target. +/// +/// +/// A selector, element, HTML string, or jQuery object; the matched set of elements will be inserted after the element(s) specified by this parameter. +/// +/// + + var ret = [], + insert = jQuery( selector ), + parent = this.length === 1 && this[0].parentNode; + + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + return this; + + } else { + for ( var i = 0, l = insert.length; i < l; i++ ) { + var elems = (i > 0 ? this.clone(true) : this).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +jQuery.prototype.insertBefore = function( selector ) { +/// +/// Insert every element in the set of matched elements before the target. +/// +/// +/// A selector, element, HTML string, or jQuery object; the matched set of elements will be inserted before the element(s) specified by this parameter. +/// +/// + + var ret = [], + insert = jQuery( selector ), + parent = this.length === 1 && this[0].parentNode; + + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + return this; + + } else { + for ( var i = 0, l = insert.length; i < l; i++ ) { + var elems = (i > 0 ? this.clone(true) : this).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +jQuery.prototype.is = function( selector ) { +/// +/// Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments. +/// 1 - is(selector) +/// 2 - is(function(index)) +/// 3 - is(jQuery object) +/// 4 - is(element) +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + return !!selector && ( typeof selector === "string" ? + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }; +jQuery.prototype.keydown = function( data, fn ) { +/// +/// Bind an event handler to the "keydown" JavaScript event, or trigger that event on an element. +/// 1 - keydown(handler(eventObject)) +/// 2 - keydown(eventData, handler(eventObject)) +/// 3 - keydown() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.keypress = function( data, fn ) { +/// +/// Bind an event handler to the "keypress" JavaScript event, or trigger that event on an element. +/// 1 - keypress(handler(eventObject)) +/// 2 - keypress(eventData, handler(eventObject)) +/// 3 - keypress() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.keyup = function( data, fn ) { +/// +/// Bind an event handler to the "keyup" JavaScript event, or trigger that event on an element. +/// 1 - keyup(handler(eventObject)) +/// 2 - keyup(eventData, handler(eventObject)) +/// 3 - keyup() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.last = function() { +/// +/// Reduce the set of matched elements to the final one in the set. +/// +/// + + return this.eq( -1 ); + }; +jQuery.prototype.length = 0; +jQuery.prototype.live = function( types, data, fn, origSelector /* Internal Use Only */ ) { +/// +/// Attach a handler to the event for all elements which match the current selector, now and in the future. +/// 1 - live(eventType, handler) +/// 2 - live(eventType, eventData, handler) +/// 3 - live(events) +/// +/// +/// A string containing a JavaScript event type, such as "click" or "keydown." As of jQuery 1.4 the string can contain multiple, space-separated event types or custom event names, as well. +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute at the time the event is triggered. +/// +/// + + 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; + }; +jQuery.prototype.load = function( url, params, callback ) { +/// +/// 1: Bind an event handler to the "load" JavaScript event. +/// 1.1 - load(handler(eventObject)) +/// 1.2 - load(eventData, handler(eventObject)) +/// 2: Load data from the server and place the returned HTML into the matched element. +/// 2.1 - load(url, data, complete(responseText, textStatus, XMLHttpRequest)) +/// +/// +/// A string containing the URL to which the request is sent. +/// +/// +/// A map or string that is sent to the server with the request. +/// +/// +/// A callback function that is executed when the request completes. +/// +/// + + if ( typeof url !== "string" && _load ) { + return _load.apply( this, arguments ); + + // Don't do a request if no elements are being requested + } else if ( !this.length ) { + return this; + } + + var off = url.indexOf( " " ); + if ( off >= 0 ) { + var selector = url.slice( off, url.length ); + url = url.slice( 0, off ); + } + + // Default to a GET request + var type = "GET"; + + // If the second parameter was provided + if ( params ) { + // If it's a function + if ( jQuery.isFunction( params ) ) { + // We assume that it's the callback + callback = params; + params = undefined; + + // Otherwise, build a param string + } else if ( typeof params === "object" ) { + params = jQuery.param( params, jQuery.ajaxSettings.traditional ); + type = "POST"; + } + } + + var self = this; + + // Request the remote document + jQuery.ajax({ + url: url, + type: type, + dataType: "html", + data: params, + // Complete callback (responseText is used internally) + complete: function( jqXHR, status, responseText ) { + // Store the response as specified by the jqXHR object + responseText = jqXHR.responseText; + // If successful, inject the HTML into all the matched elements + if ( jqXHR.isResolved() ) { + // #4825: Get the actual response in case + // a dataFilter is present in ajaxSettings + jqXHR.done(function( r ) { + responseText = r; + }); + // See if a selector was specified + self.html( selector ? + // Create a dummy div to hold the results + jQuery("
    ") + // inject the contents of the document in, removing the scripts + // to avoid any 'Permission Denied' errors in IE + .append(responseText.replace(rscript, "")) + + // Locate the specified elements + .find(selector) : + + // If not, just inject the full result + responseText ); + } + + if ( callback ) { + self.each( callback, [ responseText, status, jqXHR ] ); + } + } + }); + + return this; + }; +jQuery.prototype.map = function( callback ) { +/// +/// Pass each element in the current matched set through a function, producing a new jQuery object containing the return values. +/// +/// +/// A function object that will be invoked for each element in the current set. +/// +/// + + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }; +jQuery.prototype.mousedown = function( data, fn ) { +/// +/// Bind an event handler to the "mousedown" JavaScript event, or trigger that event on an element. +/// 1 - mousedown(handler(eventObject)) +/// 2 - mousedown(eventData, handler(eventObject)) +/// 3 - mousedown() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.mouseenter = function( data, fn ) { +/// +/// Bind an event handler to be fired when the mouse enters an element, or trigger that handler on an element. +/// 1 - mouseenter(handler(eventObject)) +/// 2 - mouseenter(eventData, handler(eventObject)) +/// 3 - mouseenter() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.mouseleave = function( data, fn ) { +/// +/// Bind an event handler to be fired when the mouse leaves an element, or trigger that handler on an element. +/// 1 - mouseleave(handler(eventObject)) +/// 2 - mouseleave(eventData, handler(eventObject)) +/// 3 - mouseleave() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.mousemove = function( data, fn ) { +/// +/// Bind an event handler to the "mousemove" JavaScript event, or trigger that event on an element. +/// 1 - mousemove(handler(eventObject)) +/// 2 - mousemove(eventData, handler(eventObject)) +/// 3 - mousemove() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.mouseout = function( data, fn ) { +/// +/// Bind an event handler to the "mouseout" JavaScript event, or trigger that event on an element. +/// 1 - mouseout(handler(eventObject)) +/// 2 - mouseout(eventData, handler(eventObject)) +/// 3 - mouseout() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.mouseover = function( data, fn ) { +/// +/// Bind an event handler to the "mouseover" JavaScript event, or trigger that event on an element. +/// 1 - mouseover(handler(eventObject)) +/// 2 - mouseover(eventData, handler(eventObject)) +/// 3 - mouseover() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.mouseup = function( data, fn ) { +/// +/// Bind an event handler to the "mouseup" JavaScript event, or trigger that event on an element. +/// 1 - mouseup(handler(eventObject)) +/// 2 - mouseup(eventData, handler(eventObject)) +/// 3 - mouseup() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.next = function( until, selector ) { +/// +/// Get the immediately following sibling of each element in the set of matched elements. If a selector is provided, it retrieves the next sibling only if it matches that selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.nextAll = function( until, selector ) { +/// +/// Get all following siblings of each element in the set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.nextUntil = function( until, selector ) { +/// +/// Get all following siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object passed. +/// 1 - nextUntil(selector, filter) +/// 2 - nextUntil(element, filter) +/// +/// +/// A string containing a selector expression to indicate where to stop matching following sibling elements. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.not = function( selector ) { +/// +/// Remove elements from the set of matched elements. +/// 1 - not(selector) +/// 2 - not(elements) +/// 3 - not(function(index)) +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + return this.pushStack( winnow(this, selector, false), "not", selector); + }; +jQuery.prototype.offset = function( options ) { +/// +/// 1: Get the current coordinates of the first element in the set of matched elements, relative to the document. +/// 1.1 - offset() +/// 2: Set the current coordinates of every element in the set of matched elements, relative to the document. +/// 2.1 - offset(coordinates) +/// 2.2 - offset(function(index, coords)) +/// +/// +/// An object containing the properties top and left, which are integers indicating the new top and left coordinates for the elements. +/// +/// + + var elem = this[0], box; + + if ( options ) { + return this.each(function( i ) { + jQuery.offset.setOffset( this, options, i ); + }); + } + + if ( !elem || !elem.ownerDocument ) { + return null; + } + + if ( elem === elem.ownerDocument.body ) { + return jQuery.offset.bodyOffset( elem ); + } + + try { + box = elem.getBoundingClientRect(); + } catch(e) {} + + var doc = elem.ownerDocument, + docElem = doc.documentElement; + + // Make sure we're not dealing with a disconnected DOM node + if ( !box || !jQuery.contains( docElem, elem ) ) { + return box ? { top: box.top, left: box.left } : { top: 0, left: 0 }; + } + + var body = doc.body, + win = getWindow(doc), + clientTop = docElem.clientTop || body.clientTop || 0, + clientLeft = docElem.clientLeft || body.clientLeft || 0, + scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop, + scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft, + top = box.top + scrollTop - clientTop, + left = box.left + scrollLeft - clientLeft; + + return { top: top, left: left }; + }; +jQuery.prototype.offsetParent = function() { +/// +/// Get the closest ancestor element that is positioned. +/// +/// + + return this.map(function() { + var offsetParent = this.offsetParent || document.body; + while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) { + offsetParent = offsetParent.offsetParent; + } + return offsetParent; + }); + }; +jQuery.prototype.one = function( type, data, fn ) { +/// +/// Attach a handler to an event for the elements. The handler is executed at most once per element. +/// +/// +/// A string containing one or more JavaScript event types, such as "click" or "submit," or custom event names. +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute at the time the event is triggered. +/// +/// + + 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.prototype.outerHeight = function( margin ) { +/// +/// Get the current computed height for the first element in the set of matched elements, including padding, border, and optionally margin. +/// +/// +/// A Boolean indicating whether to include the element's margin in the calculation. +/// +/// + + var elem = this[0]; + return elem && elem.style ? + parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) : + null; + }; +jQuery.prototype.outerWidth = function( margin ) { +/// +/// Get the current computed width for the first element in the set of matched elements, including padding and border. +/// +/// +/// A Boolean indicating whether to include the element's margin in the calculation. +/// +/// + + var elem = this[0]; + return elem && elem.style ? + parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) : + null; + }; +jQuery.prototype.parent = function( until, selector ) { +/// +/// Get the parent of each element in the current set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.parents = function( until, selector ) { +/// +/// Get the ancestors of each element in the current set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.parentsUntil = function( until, selector ) { +/// +/// Get the ancestors of each element in the current set of matched elements, up to but not including the element matched by the selector, DOM node, or jQuery object. +/// 1 - parentsUntil(selector, filter) +/// 2 - parentsUntil(element, filter) +/// +/// +/// A string containing a selector expression to indicate where to stop matching ancestor elements. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.position = function() { +/// +/// Get the current coordinates of the first element in the set of matched elements, relative to the offset parent. +/// +/// + + if ( !this[0] ) { + return null; + } + + var elem = this[0], + + // Get *real* offsetParent + offsetParent = this.offsetParent(), + + // Get correct offsets + offset = this.offset(), + parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset(); + + // Subtract element margins + // note: when an element has margin: auto the offsetLeft and marginLeft + // are the same in Safari causing offset.left to incorrectly be 0 + offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0; + offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0; + + // Add offsetParent borders + parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0; + parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0; + + // Subtract the two offsets + return { + top: offset.top - parentOffset.top, + left: offset.left - parentOffset.left + }; + }; +jQuery.prototype.prepend = function() { +/// +/// Insert content, specified by the parameter, to the beginning of each element in the set of matched elements. +/// 1 - prepend(content, content) +/// 2 - prepend(function(index, html)) +/// +/// +/// DOM element, array of elements, HTML string, or jQuery object to insert at the beginning of each element in the set of matched elements. +/// +/// +/// One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert at the beginning of each element in the set of matched elements. +/// +/// + + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 ) { + this.insertBefore( elem, this.firstChild ); + } + }); + }; +jQuery.prototype.prependTo = function( selector ) { +/// +/// Insert every element in the set of matched elements to the beginning of the target. +/// +/// +/// A selector, element, HTML string, or jQuery object; the matched set of elements will be inserted at the beginning of the element(s) specified by this parameter. +/// +/// + + var ret = [], + insert = jQuery( selector ), + parent = this.length === 1 && this[0].parentNode; + + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + return this; + + } else { + for ( var i = 0, l = insert.length; i < l; i++ ) { + var elems = (i > 0 ? this.clone(true) : this).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +jQuery.prototype.prev = function( until, selector ) { +/// +/// Get the immediately preceding sibling of each element in the set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.prevAll = function( until, selector ) { +/// +/// Get all preceding siblings of each element in the set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.prevUntil = function( until, selector ) { +/// +/// Get all preceding siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object. +/// 1 - prevUntil(selector, filter) +/// 2 - prevUntil(element, filter) +/// +/// +/// A string containing a selector expression to indicate where to stop matching preceding sibling elements. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.promise = function( type, object ) { +/// +/// Return a Promise object to observe when all actions of a certain type bound to the collection, queued or not, have finished. +/// +/// +/// The type of queue that needs to be observed. +/// +/// +/// Object onto which the promise methods have to be attached +/// +/// + + 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(); + }; +jQuery.prototype.prop = function( name, value ) { +/// +/// 1: Get the value of a property for the first element in the set of matched elements. +/// 1.1 - prop(propertyName) +/// 2: Set one or more properties for the set of matched elements. +/// 2.1 - prop(propertyName, value) +/// 2.2 - prop(map) +/// 2.3 - prop(propertyName, function(index, oldPropertyValue)) +/// +/// +/// The name of the property to set. +/// +/// +/// A value to set for the property. +/// +/// + + return jQuery.access( this, name, value, true, jQuery.prop ); + }; +jQuery.prototype.pushStack = function( elems, name, selector ) { +/// +/// Add a collection of DOM elements onto the jQuery stack. +/// 1 - pushStack(elements) +/// 2 - pushStack(elements, name, arguments) +/// +/// +/// An array of elements to push onto the stack and make into a new jQuery object. +/// +/// +/// The name of a jQuery method that generated the array of elements. +/// +/// +/// The arguments that were passed in to the jQuery method (for serialization). +/// +/// + + // 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; + }; +jQuery.prototype.queue = function( type, data ) { +/// +/// 1: Show the queue of functions to be executed on the matched elements. +/// 1.1 - queue(queueName) +/// 2: Manipulate the queue of functions to be executed on the matched elements. +/// 2.1 - queue(queueName, newQueue) +/// 2.2 - queue(queueName, callback( next )) +/// +/// +/// A string containing the name of the queue. Defaults to fx, the standard effects queue. +/// +/// +/// An array of functions to replace the current queue contents. +/// +/// + + 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 ); + } + }); + }; +jQuery.prototype.ready = function( fn ) { +/// +/// Specify a function to execute when the DOM is fully loaded. +/// +/// +/// A function to execute after the DOM is ready. +/// +/// + + // Attach the listeners + jQuery.bindReady(); + + // Add the callback + readyList.done( fn ); + + return this; + }; +jQuery.prototype.remove = function( selector, keepData ) { +/// +/// Remove the set of matched elements from the DOM. +/// +/// +/// A selector expression that filters the set of matched elements to be removed. +/// +/// + + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + if ( !selector || jQuery.filter( selector, [ elem ] ).length ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName("*") ); + jQuery.cleanData( [ elem ] ); + } + + if ( elem.parentNode ) { + elem.parentNode.removeChild( elem ); + } + } + } + + return this; + }; +jQuery.prototype.removeAttr = function( name ) { +/// +/// Remove an attribute from each element in the set of matched elements. +/// +/// +/// An attribute to remove. +/// +/// + + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }; +jQuery.prototype.removeClass = function( value ) { +/// +/// Remove a single class, multiple classes, or all classes from each element in the set of matched elements. +/// 1 - removeClass(className) +/// 2 - removeClass(function(index, class)) +/// +/// +/// One or more space-separated classes to be removed from the class attribute of each matched element. +/// +/// + + 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; + }; +jQuery.prototype.removeData = function( key ) { +/// +/// Remove a previously-stored piece of data. +/// +/// +/// A string naming the piece of data to delete. +/// +/// + + return this.each(function() { + jQuery.removeData( this, key ); + }); + }; +jQuery.prototype.removeProp = function( name ) { +/// +/// Remove a property for the set of matched elements. +/// +/// +/// The name of the property to set. +/// +/// + + 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 ) {} + }); + }; +jQuery.prototype.replaceAll = function( selector ) { +/// +/// Replace each target element with the set of matched elements. +/// +/// +/// A selector expression indicating which element(s) to replace. +/// +/// + + var ret = [], + insert = jQuery( selector ), + parent = this.length === 1 && this[0].parentNode; + + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + return this; + + } else { + for ( var i = 0, l = insert.length; i < l; i++ ) { + var elems = (i > 0 ? this.clone(true) : this).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +jQuery.prototype.replaceWith = function( value ) { +/// +/// Replace each element in the set of matched elements with the provided new content. +/// 1 - replaceWith(newContent) +/// 2 - replaceWith(function) +/// +/// +/// The content to insert. May be an HTML string, DOM element, or jQuery object. +/// +/// + + if ( this[0] && this[0].parentNode ) { + // Make sure that the elements are removed from the DOM before they are inserted + // this can help fix replacing a parent with child elements + if ( jQuery.isFunction( value ) ) { + return this.each(function(i) { + var self = jQuery(this), old = self.html(); + self.replaceWith( value.call( this, i, old ) ); + }); + } + + if ( typeof value !== "string" ) { + value = jQuery( value ).detach(); + } + + return this.each(function() { + var next = this.nextSibling, + parent = this.parentNode; + + jQuery( this ).remove(); + + if ( next ) { + jQuery(next).before( value ); + } else { + jQuery(parent).append( value ); + } + }); + } else { + return this.length ? + this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) : + this; + } + }; +jQuery.prototype.resize = function( data, fn ) { +/// +/// Bind an event handler to the "resize" JavaScript event, or trigger that event on an element. +/// 1 - resize(handler(eventObject)) +/// 2 - resize(eventData, handler(eventObject)) +/// 3 - resize() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.scroll = function( data, fn ) { +/// +/// Bind an event handler to the "scroll" JavaScript event, or trigger that event on an element. +/// 1 - scroll(handler(eventObject)) +/// 2 - scroll(eventData, handler(eventObject)) +/// 3 - scroll() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.scrollLeft = function( val ) { +/// +/// 1: Get the current horizontal position of the scroll bar for the first element in the set of matched elements. +/// 1.1 - scrollLeft() +/// 2: Set the current horizontal position of the scroll bar for each of the set of matched elements. +/// 2.1 - scrollLeft(value) +/// +/// +/// An integer indicating the new position to set the scroll bar to. +/// +/// + + var elem, win; + + if ( val === undefined ) { + elem = this[ 0 ]; + + if ( !elem ) { + return null; + } + + win = getWindow( elem ); + + // Return the scroll offset + return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] : + jQuery.support.boxModel && win.document.documentElement[ method ] || + win.document.body[ method ] : + elem[ method ]; + } + + // Set the scroll offset + return this.each(function() { + win = getWindow( this ); + + if ( win ) { + win.scrollTo( + !i ? val : jQuery( win ).scrollLeft(), + i ? val : jQuery( win ).scrollTop() + ); + + } else { + this[ method ] = val; + } + }); + }; +jQuery.prototype.scrollTop = function( val ) { +/// +/// 1: Get the current vertical position of the scroll bar for the first element in the set of matched elements. +/// 1.1 - scrollTop() +/// 2: Set the current vertical position of the scroll bar for each of the set of matched elements. +/// 2.1 - scrollTop(value) +/// +/// +/// An integer indicating the new position to set the scroll bar to. +/// +/// + + var elem, win; + + if ( val === undefined ) { + elem = this[ 0 ]; + + if ( !elem ) { + return null; + } + + win = getWindow( elem ); + + // Return the scroll offset + return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] : + jQuery.support.boxModel && win.document.documentElement[ method ] || + win.document.body[ method ] : + elem[ method ]; + } + + // Set the scroll offset + return this.each(function() { + win = getWindow( this ); + + if ( win ) { + win.scrollTo( + !i ? val : jQuery( win ).scrollLeft(), + i ? val : jQuery( win ).scrollTop() + ); + + } else { + this[ method ] = val; + } + }); + }; +jQuery.prototype.select = function( data, fn ) { +/// +/// Bind an event handler to the "select" JavaScript event, or trigger that event on an element. +/// 1 - select(handler(eventObject)) +/// 2 - select(eventData, handler(eventObject)) +/// 3 - select() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.serialize = function() { +/// +/// Encode a set of form elements as a string for submission. +/// +/// + + return jQuery.param( this.serializeArray() ); + }; +jQuery.prototype.serializeArray = function() { +/// +/// Encode a set of form elements as an array of names and values. +/// +/// + + return this.map(function(){ + return this.elements ? jQuery.makeArray( this.elements ) : this; + }) + .filter(function(){ + return this.name && !this.disabled && + ( this.checked || rselectTextarea.test( this.nodeName ) || + rinput.test( this.type ) ); + }) + .map(function( i, elem ){ + var val = jQuery( this ).val(); + + return val == null ? + null : + jQuery.isArray( val ) ? + jQuery.map( val, function( val, i ){ + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }) : + { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }).get(); + }; +jQuery.prototype.show = function( speed, easing, callback ) { +/// +/// Display the matched elements. +/// 1 - show() +/// 2 - show(duration, callback) +/// 3 - show(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + var elem, display; + + if ( speed || speed === 0 ) { + return this.animate( genFx("show", 3), speed, easing, callback); + + } else { + for ( var i = 0, j = this.length; i < j; i++ ) { + elem = this[i]; + + if ( elem.style ) { + display = elem.style.display; + + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !jQuery._data(elem, "olddisplay") && display === "none" ) { + display = elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( display === "" && jQuery.css( elem, "display" ) === "none" ) { + jQuery._data(elem, "olddisplay", defaultDisplay(elem.nodeName)); + } + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( i = 0; i < j; i++ ) { + elem = this[i]; + + if ( elem.style ) { + display = elem.style.display; + + if ( display === "" || display === "none" ) { + elem.style.display = jQuery._data(elem, "olddisplay") || ""; + } + } + } + + return this; + } + }; +jQuery.prototype.siblings = function( until, selector ) { +/// +/// Get the siblings of each element in the set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.size = function() { +/// +/// Return the number of elements in the jQuery object. +/// +/// + + return this.length; + }; +jQuery.prototype.slice = function() { +/// +/// Reduce the set of matched elements to a subset specified by a range of indices. +/// +/// +/// An integer indicating the 0-based position at which the elements begin to be selected. If negative, it indicates an offset from the end of the set. +/// +/// +/// An integer indicating the 0-based position at which the elements stop being selected. If negative, it indicates an offset from the end of the set. If omitted, the range continues until the end of the set. +/// +/// + + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }; +jQuery.prototype.slideDown = function( speed, easing, callback ) { +/// +/// Display the matched elements with a sliding motion. +/// 1 - slideDown(duration, callback) +/// 2 - slideDown(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.animate( props, speed, easing, callback ); + }; +jQuery.prototype.slideToggle = function( speed, easing, callback ) { +/// +/// Display or hide the matched elements with a sliding motion. +/// 1 - slideToggle(duration, callback) +/// 2 - slideToggle(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.animate( props, speed, easing, callback ); + }; +jQuery.prototype.slideUp = function( speed, easing, callback ) { +/// +/// Hide the matched elements with a sliding motion. +/// 1 - slideUp(duration, callback) +/// 2 - slideUp(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.animate( props, speed, easing, callback ); + }; +jQuery.prototype.stop = function( clearQueue, gotoEnd ) { +/// +/// Stop the currently-running animation on the matched elements. +/// +/// +/// A Boolean indicating whether to remove queued animation as well. Defaults to false. +/// +/// +/// A Boolean indicating whether to complete the current animation immediately. Defaults to false. +/// +/// + + if ( clearQueue ) { + this.queue([]); + } + + this.each(function() { + var timers = jQuery.timers, + i = timers.length; + // clear marker counters if we know they won't be + if ( !gotoEnd ) { + jQuery._unmark( true, this ); + } + while ( i-- ) { + if ( timers[i].elem === this ) { + if (gotoEnd) { + // force the next step to be the last + timers[i](true); + } + + timers.splice(i, 1); + } + } + }); + + // start the next in the queue if the last step wasn't forced + if ( !gotoEnd ) { + this.dequeue(); + } + + return this; + }; +jQuery.prototype.submit = function( data, fn ) { +/// +/// Bind an event handler to the "submit" JavaScript event, or trigger that event on an element. +/// 1 - submit(handler(eventObject)) +/// 2 - submit(eventData, handler(eventObject)) +/// 3 - submit() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.text = function( text ) { +/// +/// 1: Get the combined text contents of each element in the set of matched elements, including their descendants. +/// 1.1 - text() +/// 2: Set the content of each element in the set of matched elements to the specified text. +/// 2.1 - text(textString) +/// 2.2 - text(function(index, text)) +/// +/// +/// A string of text to set as the content of each matched element. +/// +/// + + if ( jQuery.isFunction(text) ) { + return this.each(function(i) { + var self = jQuery( this ); + + self.text( text.call(this, i, self.text()) ); + }); + } + + if ( typeof text !== "object" && text !== undefined ) { + return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); + } + + return jQuery.text( this ); + }; +jQuery.prototype.toArray = function() { +/// +/// Retrieve all the DOM elements contained in the jQuery set, as an array. +/// +/// + + return slice.call( this, 0 ); + }; +jQuery.prototype.toggle = function( fn, fn2, callback ) { +/// +/// 1: Bind two or more handlers to the matched elements, to be executed on alternate clicks. +/// 1.1 - toggle(handler(eventObject), handler(eventObject), handler(eventObject)) +/// 2: Display or hide the matched elements. +/// 2.1 - toggle(duration, callback) +/// 2.2 - toggle(duration, easing, callback) +/// 2.3 - toggle(showOrHide) +/// +/// +/// A function to execute every even time the element is clicked. +/// +/// +/// A function to execute every odd time the element is clicked. +/// +/// +/// Additional handlers to cycle through after clicks. +/// +/// + + var bool = typeof fn === "boolean"; + + if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) { + this._toggle.apply( this, arguments ); + + } else if ( fn == null || bool ) { + this.each(function() { + var state = bool ? fn : jQuery(this).is(":hidden"); + jQuery(this)[ state ? "show" : "hide" ](); + }); + + } else { + this.animate(genFx("toggle", 3), fn, fn2, callback); + } + + return this; + }; +jQuery.prototype.toggleClass = function( value, stateVal ) { +/// +/// Add or remove one or more classes from each element in the set of matched elements, depending on either the class's presence or the value of the switch argument. +/// 1 - toggleClass(className) +/// 2 - toggleClass(className, switch) +/// 3 - toggleClass(function(index, class, switch), switch) +/// +/// +/// One or more class names (separated by spaces) to be toggled for each element in the matched set. +/// +/// +/// A Boolean (not just truthy/falsy) value to determine whether the class should be added or removed. +/// +/// + + 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__" ) || ""; + } + }); + }; +jQuery.prototype.trigger = function( type, data ) { +/// +/// Execute all handlers and behaviors attached to the matched elements for the given event type. +/// 1 - trigger(eventType, extraParameters) +/// 2 - trigger(event) +/// +/// +/// A string containing a JavaScript event type, such as click or submit. +/// +/// +/// Additional parameters to pass along to the event handler. +/// +/// + + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }; +jQuery.prototype.triggerHandler = function( type, data ) { +/// +/// Execute all handlers attached to an element for an event. +/// +/// +/// A string containing a JavaScript event type, such as click or submit. +/// +/// +/// An array of additional parameters to pass along to the event handler. +/// +/// + + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }; +jQuery.prototype.unbind = function( type, fn ) { +/// +/// Remove a previously-attached event handler from the elements. +/// 1 - unbind(eventType, handler(eventObject)) +/// 2 - unbind(eventType, false) +/// 3 - unbind(event) +/// +/// +/// A string containing a JavaScript event type, such as click or submit. +/// +/// +/// The function that is to be no longer executed. +/// +/// + + // 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; + }; +jQuery.prototype.undelegate = function( selector, types, fn ) { +/// +/// Remove a handler from the event for all elements which match the current selector, now or in the future, based upon a specific set of root elements. +/// 1 - undelegate() +/// 2 - undelegate(selector, eventType) +/// 3 - undelegate(selector, eventType, handler) +/// 4 - undelegate(selector, events) +/// 5 - undelegate(namespace) +/// +/// +/// A selector which will be used to filter the event results. +/// +/// +/// A string containing a JavaScript event type, such as "click" or "keydown" +/// +/// +/// A function to execute at the time the event is triggered. +/// +/// + + if ( arguments.length === 0 ) { + return this.unbind( "live" ); + + } else { + return this.die( types, null, fn, selector ); + } + }; +jQuery.prototype.unload = function( data, fn ) { +/// +/// Bind an event handler to the "unload" JavaScript event. +/// 1 - unload(handler(eventObject)) +/// 2 - unload(eventData, handler(eventObject)) +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.unwrap = function() { +/// +/// Remove the parents of the set of matched elements from the DOM, leaving the matched elements in their place. +/// +/// + + return this.parent().each(function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + }).end(); + }; +jQuery.prototype.val = function( value ) { +/// +/// 1: Get the current value of the first element in the set of matched elements. +/// 1.1 - val() +/// 2: Set the value of each element in the set of matched elements. +/// 2.1 - val(value) +/// 2.2 - val(function(index, value)) +/// +/// +/// A string of text or an array of strings corresponding to the value of each matched element to set as selected/checked. +/// +/// + + 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.prototype.width = function( size ) { +/// +/// 1: Get the current computed width for the first element in the set of matched elements. +/// 1.1 - width() +/// 2: Set the CSS width of each element in the set of matched elements. +/// 2.1 - width(value) +/// 2.2 - width(function(index, width)) +/// +/// +/// An integer representing the number of pixels, or an integer along with an optional unit of measure appended (as a string). +/// +/// + + // Get window width or height + var elem = this[0]; + if ( !elem ) { + return size == null ? null : this; + } + + if ( jQuery.isFunction( size ) ) { + return this.each(function( i ) { + var self = jQuery( this ); + self[ type ]( size.call( this, i, self[ type ]() ) ); + }); + } + + if ( jQuery.isWindow( elem ) ) { + // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode + // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat + var docElemProp = elem.document.documentElement[ "client" + name ]; + return elem.document.compatMode === "CSS1Compat" && docElemProp || + elem.document.body[ "client" + name ] || docElemProp; + + // Get document width or height + } else if ( elem.nodeType === 9 ) { + // Either scroll[Width/Height] or offset[Width/Height], whichever is greater + return Math.max( + elem.documentElement["client" + name], + elem.body["scroll" + name], elem.documentElement["scroll" + name], + elem.body["offset" + name], elem.documentElement["offset" + name] + ); + + // Get or set width or height on the element + } else if ( size === undefined ) { + var orig = jQuery.css( elem, type ), + ret = parseFloat( orig ); + + return jQuery.isNaN( ret ) ? orig : ret; + + // Set the width or height on the element (default to pixels if value is unitless) + } else { + return this.css( type, typeof size === "string" ? size : size + "px" ); + } + }; +jQuery.prototype.wrap = function( html ) { +/// +/// Wrap an HTML structure around each element in the set of matched elements. +/// 1 - wrap(wrappingElement) +/// 2 - wrap(function(index)) +/// +/// +/// An HTML snippet, selector expression, jQuery object, or DOM element specifying the structure to wrap around the matched elements. +/// +/// + + return this.each(function() { + jQuery( this ).wrapAll( html ); + }); + }; +jQuery.prototype.wrapAll = function( html ) { +/// +/// Wrap an HTML structure around all elements in the set of matched elements. +/// +/// +/// An HTML snippet, selector expression, jQuery object, or DOM element specifying the structure to wrap around the matched elements. +/// +/// + + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapAll( html.call(this, i) ); + }); + } + + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); + + if ( this[0].parentNode ) { + wrap.insertBefore( this[0] ); + } + + wrap.map(function() { + var elem = this; + + while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { + elem = elem.firstChild; + } + + return elem; + }).append( this ); + } + + return this; + }; +jQuery.prototype.wrapInner = function( html ) { +/// +/// Wrap an HTML structure around the content of each element in the set of matched elements. +/// 1 - wrapInner(wrappingElement) +/// 2 - wrapInner(wrappingFunction) +/// +/// +/// An HTML snippet, selector expression, jQuery object, or DOM element specifying the structure to wrap around the content of the matched elements. +/// +/// + + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapInner( html.call(this, i) ); + }); + } + + return this.each(function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + }); + }; +jQuery.fn = jQuery.prototype; +jQuery.fn.init.prototype = jQuery.fn; +window.jQuery = window.$ = jQuery; +})(window); \ No newline at end of file diff --git a/SignalR.Samples/Scripts/jquery-1.6.2.js b/SignalR.Samples/Scripts/jquery-1.6.2.js new file mode 100644 index 000000000..f3201aacb --- /dev/null +++ b/SignalR.Samples/Scripts/jquery-1.6.2.js @@ -0,0 +1,8981 @@ +/*! + * jQuery JavaScript Library v1.6.2 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Thu Jun 30 14:16:56 2011 -0400 + */ +(function( window, undefined ) { + +// Use the correct document accordingly with window argument (sandbox) +var document = window.document, + navigator = window.navigator, + location = window.location; +var jQuery = (function() { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // (both of which we optimize for) + 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])/ig, + + // 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.2", + + // 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; + } + + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + 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 + // (xml & tmp used internally) + parseXML: function( data , xml , tmp ) { + + 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 ); + } + + tmp = xml.documentElement; + + if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) { + 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 ); + } + }, + + // Converts a dashed string to camelCased string; + // Used by both the css and data modules + camelCase: function( string ) { + return string.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 ( 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 ]( 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 + }; + if ( body ) { + jQuery.extend( testElementStyle, { + position: "absolute", + left: -1000, + top: -1000 + }); + } + 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 internalKey = jQuery.expando, getByName = typeof name === "string", thisCache, + + // 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 ][ 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; + } + + return getByName ? + // Check for both converted-to-camel and non-converted data property names + thisCache[ jQuery.camelCase( name ) ] || thisCache[ name ] : + thisCache; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var 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 ) { + var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ]; + + if ( thisCache ) { + 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 + if ( jQuery.support.deleteExpando || cache != window ) { + 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, + rinvalidChar = /\:|^on/, + formHook, 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].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 formHook for forms and if the name contains certain characters + } else if ( formHook && name !== "className" && + (jQuery.nodeName( elem, "form" ) || rinvalidChar.test( name )) ) { + + hooks = formHook; + } + } + } + + 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; + + if ( jQuery.support.getSetAttribute ) { + // Use removeAttribute in browsers that support it + elem.removeAttribute( name ); + } else { + jQuery.attr( elem, name, "" ); + elem.removeAttributeNode( elem.getAttributeNode( 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; + } + } + }, + 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; + } + }, + // Use the value property for back compat + // Use the formHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( formHook && jQuery.nodeName( elem, "button" ) ) { + return formHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( formHook && jQuery.nodeName( elem, "button" ) ) { + return formHook.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 )) !== undefined ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: {} +}); + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + return jQuery.prop( elem, name ) ? + 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 ) { + + // propFix is more comprehensive and contains all fixes + jQuery.attrFix = jQuery.propFix; + + // Use this for any attribute on a form in IE6/7 + formHook = jQuery.attrHooks.name = jQuery.attrHooks.title = 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 ) { + // Check form objects in IE (multiple bugs related) + // Only use nodeValue if the attribute node exists on the form + var ret = elem.getAttributeNode( name ); + if ( ret ) { + ret.nodeValue = value; + return 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; + } + } + } + }); +} + +// 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 = 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 = 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 = 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 ) { + if ( !elem || typeof elem === "string" ) { + return jQuery.inArray( this[0], + // If it receives a string, the selector is used + // If it receives nothing, the siblings are used + elem ? jQuery( elem ) : this.parent().children() ); + } + // 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 = /", "" ], + legend: [ 1, "
    ", "
    " ], + thead: [ 1, "", "
    " ], + 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 + + + + +
      +
    + + diff --git a/SignalR.Samples/Web.Debug.config b/SignalR.Samples/Web.Debug.config new file mode 100644 index 000000000..2c6dd51a7 --- /dev/null +++ b/SignalR.Samples/Web.Debug.config @@ -0,0 +1,30 @@ + + + + + + + + + + \ No newline at end of file diff --git a/SignalR.Samples/Web.Release.config b/SignalR.Samples/Web.Release.config new file mode 100644 index 000000000..4122d79bf --- /dev/null +++ b/SignalR.Samples/Web.Release.config @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/SignalR.Samples/Web.config b/SignalR.Samples/Web.config new file mode 100644 index 000000000..6061dd257 --- /dev/null +++ b/SignalR.Samples/Web.config @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SignalR.Samples/packages.config b/SignalR.Samples/packages.config new file mode 100644 index 000000000..b07036769 --- /dev/null +++ b/SignalR.Samples/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/SignalR.sln b/SignalR.sln new file mode 100644 index 000000000..3fcd9b298 --- /dev/null +++ b/SignalR.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.Samples", "SignalR.Samples\SignalR.Samples.csproj", "{1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR", "SignalR\SignalR.csproj", "{1B9A82C4-BCA1-4834-A33E-226F17BE070B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.SQLServer", "SignalR.SQLServer\SignalR.SQLServer.csproj", "{32D16B36-970E-4CF2-B954-78CB1833CBC1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Release|Any CPU.Build.0 = Release|Any CPU + {1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Release|Any CPU.Build.0 = Release|Any CPU + {32D16B36-970E-4CF2-B954-78CB1833CBC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32D16B36-970E-4CF2-B954-78CB1833CBC1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32D16B36-970E-4CF2-B954-78CB1833CBC1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32D16B36-970E-4CF2-B954-78CB1833CBC1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/SignalR/CommandType.cs b/SignalR/CommandType.cs new file mode 100644 index 000000000..7284c00c5 --- /dev/null +++ b/SignalR/CommandType.cs @@ -0,0 +1,6 @@ +namespace SignalR { + internal enum CommandType { + AddToGroup, + RemoveFromGroup + } +} diff --git a/SignalR/Connection.cs b/SignalR/Connection.cs new file mode 100644 index 000000000..9da74cb8f --- /dev/null +++ b/SignalR/Connection.cs @@ -0,0 +1,203 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SignalR.Infrastructure; + +namespace SignalR { + public class Connection : IConnection { + private readonly Signaler _signaler; + private readonly IMessageStore _store; + private readonly string _baseSignal; + private readonly string _clientId; + private readonly HashSet _signals; + private readonly HashSet _groups; + + public Connection(IMessageStore store, + Signaler signaler, + string baseSignal, + string clientId, + IEnumerable signals) + : this(store, + signaler, + baseSignal, + clientId, + signals, + Enumerable.Empty()) { + } + + public Connection(IMessageStore store, + Signaler signaler, + string baseSignal, + string clientId, + IEnumerable signals, + IEnumerable groups) { + _store = store; + _signaler = signaler; + _baseSignal = baseSignal; + _clientId = clientId; + _signals = new HashSet(signals); + _groups = new HashSet(groups); + } + + public TimeSpan ReceiveTimeout { + get { + return _signaler.DefaultTimeout; + } + set { + _signaler.DefaultTimeout = value; + } + } + + private IEnumerable Signals { + get { + return _signals.Concat(_groups); + } + } + + public virtual Task Broadcast(object value) { + return Broadcast(_baseSignal, value); + } + + public virtual Task Broadcast(string message, object value) { + return SendMessage(message, value); + } + + public Task Send(object value) { + return SendMessage(_clientId, value); + } + + public Task ReceiveAsync() { + // Get the last message id then wait for new messages to arrive + return _store.GetLastId() + .ContinueWith(storeTask => WaitForSignal(storeTask.Result)) + .Unwrap(); + } + + public Task ReceiveAsync(long messageId) { + // Get all messages for this message id, or wait until new messages if there are none + return GetResponse(messageId).ContinueWith(task => ProcessReceive(task, messageId)) + .Unwrap(); + } + + public static IConnection GetConnection() where T : PersistentConnection { + return GetConnection(typeof(T).FullName); + } + + public static IConnection GetConnection(string connectionType) { + return new Connection(DependencyResolver.Resolve(), + Signaler.Instance, + connectionType, + null, + new[] { connectionType }); + } + + private Task ProcessReceive(Task responseTask, long? messageId = null) { + // No messages to return so we need to subscribe until we have something + if (responseTask.Result == null) { + return WaitForSignal(messageId); + } + + // Return the task as is + return responseTask; + } + + private Task WaitForSignal(long? messageId = null) { + // Wait for a signal to get triggered and return with a response + return _signaler.Subscribe(Signals) + .ContinueWith(task => ProcessSignal(task, messageId)) + .Unwrap(); + } + + private Task ProcessSignal(Task signalTask, long? messageId = null) { + if (signalTask.Result.TimedOut) { + // If we timed out waiting for a signal we have a message id then return null + PersistentResponse response = null; + + // Otherwise ee need to return 0 so that the next request we'll get all messages + // on the next try + if (messageId == null) { + response = new PersistentResponse { + MessageId = 0 + }; + } + + // Return a task wrapping the result + return TaskAsyncHelper.FromResult(response); + } + + // Get the response for this message id + return GetResponse(messageId ?? 0); + } + + private Task GetResponse(long messageId) { + // Get all messages for the current set of signals + return GetMessages(messageId, Signals).ContinueWith(messageTask => { + if (!messageTask.Result.Any()) { + return null; + } + + var response = new PersistentResponse(); + + var commands = messageTask.Result.Where(m => m.SignalKey.EndsWith(PersistentConnection.SignalrCommand)); + + ProcessCommands(commands); + + messageId = messageTask.Result.Last().Id; + + // Get the message values and the max message id we received + var messageValues = messageTask.Result.Except(commands) + .Select(m => m.Value) + .ToList(); + + response.MessageId = messageId; + response.Messages = messageValues; + + // Set the groups on the outgoing transport data + response.TransportData["Groups"] = _groups; + + return response; + }); + } + + private void ProcessCommands(IEnumerable messages) { + foreach (var message in messages) { + var command = message.Value as SignalCommand; + if (command == null) { + continue; + } + + switch (command.Type) { + case CommandType.AddToGroup: + _groups.Add((string)command.Value); + break; + case CommandType.RemoveFromGroup: + _groups.Remove((string)command.Value); + break; + } + } + } + + private Task SendMessage(string message, object value) { + return _store.Save(message, value) + .ContinueWith(_ => _signaler.Signal(message)) + .Unwrap(); + } + + private Task> GetMessages(long id, IEnumerable signals) { + var pendingMessagesTasks = (from signal in signals + select _store.GetAllSince(signal, id)).ToArray(); + + // If there are no pending messages, we need to shortcut since ContinueWhenAll + // blows up for empty arrays. + if (!pendingMessagesTasks.Any()) { + return TaskAsyncHelper.FromResult(Enumerable.Empty()); + } + + // Wait until all of the tasks are done before we return + return Task.Factory.ContinueWhenAll( + pendingMessagesTasks, + tasks => (IEnumerable)tasks.SelectMany(t => t.Result).ToList()); + } + } +} \ No newline at end of file diff --git a/SignalR/Hubs/ActionInfo.cs b/SignalR/Hubs/ActionInfo.cs new file mode 100644 index 000000000..f7f533881 --- /dev/null +++ b/SignalR/Hubs/ActionInfo.cs @@ -0,0 +1,8 @@ +using System.Reflection; + +namespace SignalR.Hubs { + public class ActionInfo { + public object[] Arguments { get; set; } + public MethodInfo Method { get; set; } + } +} diff --git a/SignalR/Hubs/ClientAgent.cs b/SignalR/Hubs/ClientAgent.cs new file mode 100644 index 000000000..b620d29e6 --- /dev/null +++ b/SignalR/Hubs/ClientAgent.cs @@ -0,0 +1,67 @@ +using System.Dynamic; +using System.Threading.Tasks; + +namespace SignalR.Hubs { + public class ClientAgent : DynamicObject, IClientAgent, IGroupManager { + private readonly IConnection _connection; + private readonly string _hubName; + + public ClientAgent(IConnection connection, string hubName) { + _connection = connection; + _hubName = hubName; + } + + public dynamic this[string key] { + get { + return new SignalAgent(_connection, key, _hubName); + } + } + + public Task Invoke(string method, params object[] args) { + return Invoke(_connection, method, _hubName, method, args); + } + + public override bool TryGetMember(GetMemberBinder binder, out object result) { + result = this[binder.Name]; + return true; + } + + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { + result = Invoke(binder.Name, args); + return true; + } + + public static Task Invoke(IConnection connection, string signal, string hubName, string method, object[] args) { + signal = hubName + "." + signal; + + var invocation = new { + Hub = hubName, + Method = method, + Args = args + }; + + return connection.Broadcast(signal, invocation); + } + + public void AddToGroup(string clientId, string groupName) { + groupName = _hubName + "." + groupName; + SendCommand(clientId, CommandType.AddToGroup, groupName); + } + + public void RemoveFromGroup(string clientId, string groupName) { + groupName = _hubName + "." + groupName; + SendCommand(clientId, CommandType.RemoveFromGroup, groupName); + } + + private void SendCommand(string clientId, CommandType commandType, object commandValue) { + string signal = _hubName + "." + clientId + "." + PersistentConnection.SignalrCommand; + + var groupCommand = new SignalCommand { + Type = commandType, + Value = commandValue + }; + + _connection.Broadcast(signal, groupCommand); + } + } +} diff --git a/SignalR/Hubs/DefaultActionResolver.cs b/SignalR/Hubs/DefaultActionResolver.cs new file mode 100644 index 000000000..3fb3e9f85 --- /dev/null +++ b/SignalR/Hubs/DefaultActionResolver.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace SignalR.Hubs { + public class DefaultActionResolver : IActionResolver { + public ActionInfo ResolveAction(Type hubType, string actionName, object[] parameters) { + // Get all methods + var candidates = hubType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase) + .Where(m => m.Name.Equals(actionName, StringComparison.OrdinalIgnoreCase)) + .Where(m => ParametersAreCompatible(m.GetParameters(), parameters)) + .ToList(); + + switch (candidates.Count) { + case 1: + var method = candidates.Single(); + var args = GetParameters(method.GetParameters(), parameters); + return new ActionInfo { + Method = method, + Arguments = args + }; + default: + break; + } + + return null; + } + + private bool ParametersAreCompatible(ParameterInfo[] parameterInfos, object[] parameters) { + return (parameterInfos.Length == 0 && parameters == null) || + (parameterInfos.Length == parameters.Length); + } + + private object[] GetParameters(ParameterInfo[] parameterInfos, object[] parameters) { + return parameterInfos.OrderBy(p => p.Position) + .Select(p => Bind(parameters[p.Position], p.ParameterType)) + .ToArray(); + } + + private object Bind(IDictionary dictionaryValue, Type type) { + object obj = Activator.CreateInstance(type); + foreach (var property in type.GetProperties()) { + object value; + if (dictionaryValue.TryGetValue(property.Name, out value)) { + property.SetValue(obj, Bind(value, property.PropertyType), null); + } + } + return obj; + } + + private object Bind(object value, Type type) { + var dictionaryValue = value as IDictionary; + if (dictionaryValue != null) { + return Bind(new Dictionary(dictionaryValue, StringComparer.OrdinalIgnoreCase), type); + } + return Convert.ChangeType(value, type); + } + } +} \ No newline at end of file diff --git a/SignalR/Hubs/DefaultHubActivator.cs b/SignalR/Hubs/DefaultHubActivator.cs new file mode 100644 index 000000000..be11c13dd --- /dev/null +++ b/SignalR/Hubs/DefaultHubActivator.cs @@ -0,0 +1,11 @@ +using System; +using SignalR.Infrastructure; + +namespace SignalR.Hubs { + public class DefaultHubActivator : IHubActivator { + public Hub Create(Type hubType) { + object hub = DependencyResolver.Resolve(hubType) ?? Activator.CreateInstance(hubType); + return hub as Hub; + } + } +} \ No newline at end of file diff --git a/SignalR/Hubs/DefaultHubFactory.cs b/SignalR/Hubs/DefaultHubFactory.cs new file mode 100644 index 000000000..497800cce --- /dev/null +++ b/SignalR/Hubs/DefaultHubFactory.cs @@ -0,0 +1,37 @@ +using System; +using System.Globalization; +using System.Web.Compilation; +using SignalR.Infrastructure; + +namespace SignalR.Hubs { + public class DefaultHubFactory : IHubFactory { + private IHubActivator _hubActivator; + + public DefaultHubFactory() + : this(DependencyResolver.Resolve()) { + + } + + public DefaultHubFactory(IHubActivator hubActivator) { + if (hubActivator == null) { + throw new ArgumentNullException("hubActivator"); + } + _hubActivator = hubActivator; + } + + public Hub CreateHub(string hubName) { + // Get the type name from the client + Type type = BuildManager.GetType(hubName, throwOnError: false); + + if (type == null) { + throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "Unable to find '{0}'.", hubName)); + } + + if (!type.IsSubclassOf(typeof(Hub))) { + throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "'{0}' is not a Hub.", type.FullName)); + } + + return _hubActivator.Create(type); + } + } +} \ No newline at end of file diff --git a/SignalR/Hubs/DefaultHubLocator.cs b/SignalR/Hubs/DefaultHubLocator.cs new file mode 100644 index 000000000..934161867 --- /dev/null +++ b/SignalR/Hubs/DefaultHubLocator.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Web.Compilation; + +namespace SignalR.Hubs { + public class DefaultHubLocator : IHubLocator { + public IEnumerable GetHubs() { + return from Assembly a in BuildManager.GetReferencedAssemblies() + where !a.GlobalAssemblyCache && !a.IsDynamic + from type in GetTypesSafe(a) + where type.IsSubclassOf(typeof(Hub)) + select type; + } + + private IEnumerable GetTypesSafe(Assembly a) { + try { + return a.GetTypes(); + } + catch { + return Enumerable.Empty(); + } + } + } +} \ No newline at end of file diff --git a/SignalR/Hubs/DefaultJavaScriptProxyGenerator.cs b/SignalR/Hubs/DefaultJavaScriptProxyGenerator.cs new file mode 100644 index 000000000..cede3f0e4 --- /dev/null +++ b/SignalR/Hubs/DefaultJavaScriptProxyGenerator.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Web; +using Microsoft.Ajax.Utilities; + +namespace SignalR.Hubs { + public class DefaultJavaScriptProxyGenerator : IJavaScriptProxyGenerator { + private static readonly Lazy _template = new Lazy(GetTemplate); + private static readonly ConcurrentDictionary _scriptCache = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + + private const string ScriptResource = "SignalR.Scripts.hubs.js"; + + private readonly IHubLocator _hubLocator; + + public DefaultJavaScriptProxyGenerator(IHubLocator hubLocator) { + _hubLocator = hubLocator; + } + + public string GenerateProxy(HttpContextBase context, string serviceUrl) { + string script; + if (_scriptCache.TryGetValue(serviceUrl, out script)) { + return script; + } + + var template = _template.Value; + + script = template.Replace("{serviceUrl}", serviceUrl); + + var hubs = new StringBuilder(); + var first = true; + foreach (var type in _hubLocator.GetHubs()) { + if (!first) { + hubs.AppendLine(","); + hubs.Append(" "); + } + GenerateType(serviceUrl, hubs, type); + first = false; + } + + script = script.Replace("/*hubs*/", hubs.ToString()); + + if (!context.IsDebuggingEnabled) { + var minifier = new Minifier(); + script = minifier.MinifyJavaScript(script); + } + + _scriptCache.TryAdd(serviceUrl, script); + + return script; + } + + private void GenerateType(string serviceUrl, StringBuilder sb, Type type) { + // Get public instance methods declared on this type only + var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); + var members = methods.Select(m => m.Name).ToList(); + members.Add("namespace"); + members.Add("serverMembers"); + members.Add("callbacks"); + + sb.AppendFormat("{0}: {{", Json.CamelCase(type.Name)).AppendLine(); + sb.AppendFormat(" _: {{").AppendLine(); + sb.AppendFormat(" hubName: '{0}',", type.FullName ?? "null").AppendLine(); + sb.AppendFormat(" serverMembers: [{0}],", Commas(members, m => "'" + Json.CamelCase(m) + "'")).AppendLine(); + sb.AppendLine( " connection: function () { return window.signalR.hub; }"); + sb.AppendFormat(" }},").AppendLine(); + sb.AppendFormat(" state: {{}}"); + if (methods.Any()) { + sb.Append(","); + } + bool first = true; + foreach (var method in methods) { + if (!first) { + sb.Append(",").AppendLine(); + } + GenerateMethod(serviceUrl, sb, type, method); + first = false; + } + sb.AppendLine(); + sb.Append(" }"); + } + + private void GenerateMethod(string serviceUrl, StringBuilder sb, Type type, MethodInfo method) { + var parameters = method.GetParameters(); + var parameterNames = parameters.Select(p => p.Name).ToList(); + parameterNames.Add("callback"); + sb.AppendLine(); + sb.AppendFormat(" {0}: function ({1}) {{", Json.CamelCase(method.Name), Commas(parameterNames)).AppendLine(); + sb.AppendFormat(" return serverCall(this, \"{0}\", $.makeArray(arguments));", method.Name).AppendLine(); + sb.Append(" }"); + } + + private static string Commas(IEnumerable values) { + return Commas(values, v => v); + } + + private static string Commas(IEnumerable values, Func selector) { + return String.Join(", ", values.Select(selector)); + } + + private static string GetTemplate() { + using (Stream resourceStream = typeof(DefaultJavaScriptProxyGenerator).Assembly.GetManifestResourceStream(ScriptResource)) { + using (var reader = new StreamReader(resourceStream)) { + return reader.ReadToEnd(); + } + } + } + } +} diff --git a/SignalR/Hubs/Hub.cs b/SignalR/Hubs/Hub.cs new file mode 100644 index 000000000..fe5c39d64 --- /dev/null +++ b/SignalR/Hubs/Hub.cs @@ -0,0 +1,24 @@ + +namespace SignalR.Hubs { + public class Hub { + public IClientAgent Agent { get; set; } + + public dynamic Clients { + get { + return Agent; + } + } + + public dynamic Caller { get; set; } + public HubContext Context { get; set; } + public IGroupManager GroupManager { get; set; } + + public void AddToGroup(string groupName) { + GroupManager.AddToGroup(Context.ClientId, groupName); + } + + public void RemoveFromGroup(string groupName) { + GroupManager.RemoveFromGroup(Context.ClientId, groupName); + } + } +} \ No newline at end of file diff --git a/SignalR/Hubs/HubContext.cs b/SignalR/Hubs/HubContext.cs new file mode 100644 index 000000000..0b3c8542c --- /dev/null +++ b/SignalR/Hubs/HubContext.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web; + +namespace SignalR.Hubs { + public class HubContext { + /// + /// Gets the client id of the calling client. + /// + public string ClientId { get; private set; } + + /// + /// Gets the cookies for the request + /// + public HttpCookieCollection Cookies { get; private set; } + + public HubContext(string clientId, HttpCookieCollection cookies) { + ClientId = clientId; + Cookies = cookies; + } + } +} diff --git a/SignalR/Hubs/HubDispatcher.cs b/SignalR/Hubs/HubDispatcher.cs new file mode 100644 index 000000000..d55aedebc --- /dev/null +++ b/SignalR/Hubs/HubDispatcher.cs @@ -0,0 +1,212 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Threading.Tasks; +using System.Web; +using System.Web.Script.Serialization; +using SignalR.Infrastructure; + +namespace SignalR.Hubs { + public class HubDispatcher : PersistentConnection { + // We don't use the configured IJson serializer here because we assume things about + // the parsed output. Doesn't matter coming in anyway as member resolution is + // case insensitive. + private readonly JavaScriptSerializer _serializer = new JavaScriptSerializer { + MaxJsonLength = 30 * 1024 * 1024 + }; + + private readonly Signaler _signaler; + private readonly IMessageStore _store; + private readonly IHubFactory _hubFactory; + private readonly IActionResolver _actionResolver; + private readonly IJavaScriptProxyGenerator _proxyGenerator; + private readonly string _url; + private HttpCookieCollection _cookies; + + public HubDispatcher(string url) + : this(DependencyResolver.Resolve(), + DependencyResolver.Resolve(), + Signaler.Instance, + DependencyResolver.Resolve(), + DependencyResolver.Resolve(), + DependencyResolver.Resolve(), + url) { + } + + public HubDispatcher(IHubFactory hubFactory, + IMessageStore store, + Signaler signaler, + IActionResolver actionResolver, + IJavaScriptProxyGenerator proxyGenerator, + IJsonStringifier jsonStringifier, + string url) + : base(signaler, store, jsonStringifier) { + _hubFactory = hubFactory; + _store = store; + _signaler = signaler; + _actionResolver = actionResolver; + _proxyGenerator = proxyGenerator; + _url = VirtualPathUtility.ToAbsolute(url); + } + + protected override Task OnReceivedAsync(string clientId, string data) { + var hubRequest = _serializer.Deserialize(data); + + // Create the hub + Hub hub = _hubFactory.CreateHub(hubRequest.Hub); + + // Deserialize the parameter name value pairs so we can match it up with the method's parameters + var parameters = hubRequest.Data; + + // Resolve the action + ActionInfo actionInfo = _actionResolver.ResolveAction(hub.GetType(), hubRequest.Action, parameters); + + if (actionInfo == null) { + throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "'{0}' could not be resolved.", hubRequest.Action)); + } + + string hubName = hub.GetType().FullName; + + var state = new TrackingDictionary(hubRequest.State); + hub.Context = new HubContext(clientId, _cookies); + hub.Caller = new SignalAgent(Connection, clientId, hubName, state); + var agent = new ClientAgent(Connection, hubName); + hub.Agent = agent; + hub.GroupManager = agent; + + try { + // Execute the method + object result = actionInfo.Method.Invoke(hub, actionInfo.Arguments); + Type returnType = actionInfo.Method.ReturnType; + if (typeof(Task).IsAssignableFrom(returnType)) { + var task = (Task)result; + if (returnType.IsGenericType) { + // Get the in Task + Type resultType = returnType.GetGenericArguments().Single(); + + // Get the correct ContinueWith overload + var continueWith = (from m in task.GetType().GetMethods() + let methodParameters = m.GetParameters() + where m.Name.Equals("ContinueWith", StringComparison.OrdinalIgnoreCase) && + methodParameters.Length == 1 + let parameter = methodParameters[0] + where parameter.ParameterType.IsGenericType && + typeof(Action<>) == parameter.ParameterType.GetGenericTypeDefinition() + select m).FirstOrDefault(); + + var taskParameter = Expression.Parameter(returnType); + var processResultMethod = typeof(HubDispatcher).GetMethod("ProcessResult", BindingFlags.NonPublic | BindingFlags.Instance); + var taskResult = Expression.Property(taskParameter, "Result"); + + var body = Expression.Call(Expression.Constant(this), + processResultMethod, + Expression.Constant(state), + Expression.Convert(taskResult, typeof(object)), + Expression.Constant(hubRequest), + Expression.Constant(null, typeof(Exception))); + + var lambda = Expression.Lambda(body, taskParameter); + + var call = Expression.Call(Expression.Constant(task), continueWith, lambda); + return Expression.Lambda>(call).Compile()(); + } + } + else { + ProcessResult(state, result, hubRequest, null); + } + } + catch (TargetInvocationException e) { + ProcessResult(state, null, hubRequest, e.GetBaseException()); + } + + return base.OnReceivedAsync(clientId, data); + } + + public override Task ProcessRequestAsync(HttpContext context) { + // Generate the proxy + if (context.Request.Path.EndsWith("/hubs", StringComparison.OrdinalIgnoreCase)) { + context.Response.ContentType = "application/x-javascript"; + context.Response.Write(_proxyGenerator.GenerateProxy(new HttpContextWrapper(context), _url)); + return TaskAsyncHelper.Empty; + } + + _cookies = context.Request.Cookies; + return base.ProcessRequestAsync(context); + } + + private void ProcessResult(TrackingDictionary state, object result, HubRequest request, Exception error) { + var hubResult = new HubResult { + State = state.GetChanges(), + Result = result, + Id = request.Id, + Error = error != null ? error.Message : null + }; + + Send(hubResult); + } + + protected override IConnection CreateConnection(string clientId, IEnumerable groups, HttpContextBase context) { + string data = context.Request["data"]; + + if (String.IsNullOrEmpty(data)) { + return base.CreateConnection(clientId, groups, context); + } + + IEnumerable clientHubInfo = null; + + try { + clientHubInfo = _serializer.Deserialize>(data); + } + catch { + + } + + if (clientHubInfo == null || !clientHubInfo.Any()) { + return base.CreateConnection(clientId, groups, context); + } + + IEnumerable hubSignals = clientHubInfo.SelectMany(info => GetSignals(info, clientId)); + + return new Connection(_store, _signaler, null, clientId, hubSignals, groups); + } + + private IEnumerable GetSignals(ClientHubInfo hubInfo, string clientId) { + var clientSignals = new[] { + hubInfo.CreateQualifiedName(clientId), + hubInfo.CreateQualifiedName(clientId) + "." + PersistentConnection.SignalrCommand + }; + + // Create the signals for hubs + return hubInfo.Methods.Select(hubInfo.CreateQualifiedName) + .Concat(clientSignals); + + } + + private class ClientHubInfo { + public string Name { get; set; } + public string[] Methods { get; set; } + + public string CreateQualifiedName(string unqualifiedName) { + return Name + "." + unqualifiedName; + } + } + + private class HubResult { + public IDictionary State { get; set; } + public object Result { get; set; } + public string Id { get; set; } + public string Error { get; set; } + } + + private class HubRequest { + public string Hub { get; set; } + public string Action { get; set; } + public object[] Data { get; set; } + public IDictionary State { get; set; } + public string Id { get; set; } + } + } +} \ No newline at end of file diff --git a/SignalR/Hubs/HubModule.cs b/SignalR/Hubs/HubModule.cs new file mode 100644 index 000000000..9d891003c --- /dev/null +++ b/SignalR/Hubs/HubModule.cs @@ -0,0 +1,22 @@ +using System; +using System.Web; +using System.IO; + +namespace SignalR.Hubs { + public class HubModule : IHttpModule { + private const string Url = "~/signalr"; + + public void Init(HttpApplication app) { + app.PostResolveRequestCache += (sender, e) => { + if (app.Request.AppRelativeCurrentExecutionFilePath.StartsWith(Url, StringComparison.OrdinalIgnoreCase) && + !Path.GetExtension(app.Request.AppRelativeCurrentExecutionFilePath).Equals(".js", StringComparison.OrdinalIgnoreCase)) { + var handler = new HubDispatcher(Url); + + app.Context.RemapHandler(handler); + } + }; + } + + public void Dispose() { } + } +} \ No newline at end of file diff --git a/SignalR/Hubs/IActionResolver.cs b/SignalR/Hubs/IActionResolver.cs new file mode 100644 index 000000000..56e1d469c --- /dev/null +++ b/SignalR/Hubs/IActionResolver.cs @@ -0,0 +1,8 @@ +using System; +using System.Collections.Generic; + +namespace SignalR.Hubs { + public interface IActionResolver { + ActionInfo ResolveAction(Type hubType, string actionName, object[] parameters); + } +} diff --git a/SignalR/Hubs/IClientAgent.cs b/SignalR/Hubs/IClientAgent.cs new file mode 100644 index 000000000..4ca234fb0 --- /dev/null +++ b/SignalR/Hubs/IClientAgent.cs @@ -0,0 +1,8 @@ +using System; +using System.Threading.Tasks; + +namespace SignalR.Hubs { + public interface IClientAgent { + Task Invoke(string method, params object[] args); + } +} diff --git a/SignalR/Hubs/IHubActivator.cs b/SignalR/Hubs/IHubActivator.cs new file mode 100644 index 000000000..793efc53b --- /dev/null +++ b/SignalR/Hubs/IHubActivator.cs @@ -0,0 +1,7 @@ +using System; + +namespace SignalR.Hubs { + public interface IHubActivator { + Hub Create(Type hubType); + } +} \ No newline at end of file diff --git a/SignalR/Hubs/IHubFactory.cs b/SignalR/Hubs/IHubFactory.cs new file mode 100644 index 000000000..ce3616945 --- /dev/null +++ b/SignalR/Hubs/IHubFactory.cs @@ -0,0 +1,6 @@ + +namespace SignalR.Hubs { + public interface IHubFactory { + Hub CreateHub(string hubName); + } +} \ No newline at end of file diff --git a/SignalR/Hubs/IHubLocator.cs b/SignalR/Hubs/IHubLocator.cs new file mode 100644 index 000000000..6fc1cf4d6 --- /dev/null +++ b/SignalR/Hubs/IHubLocator.cs @@ -0,0 +1,8 @@ +using System; +using System.Collections.Generic; + +namespace SignalR.Hubs { + public interface IHubLocator { + IEnumerable GetHubs(); + } +} \ No newline at end of file diff --git a/SignalR/Hubs/IJavaScriptProxyGenerator.cs b/SignalR/Hubs/IJavaScriptProxyGenerator.cs new file mode 100644 index 000000000..5016c2ab5 --- /dev/null +++ b/SignalR/Hubs/IJavaScriptProxyGenerator.cs @@ -0,0 +1,7 @@ +using System.Web; + +namespace SignalR.Hubs { + public interface IJavaScriptProxyGenerator { + string GenerateProxy(HttpContextBase context, string serviceUrl); + } +} diff --git a/SignalR/Hubs/SignalAgent.cs b/SignalR/Hubs/SignalAgent.cs new file mode 100644 index 000000000..b4bacbc01 --- /dev/null +++ b/SignalR/Hubs/SignalAgent.cs @@ -0,0 +1,68 @@ +using System.Dynamic; +using System.Threading.Tasks; + +namespace SignalR.Hubs { + public class SignalAgent : DynamicObject, IClientAgent { + private readonly IConnection _connection; + private readonly string _signal; + private readonly string _hubName; + private readonly TrackingDictionary _state; + + public SignalAgent(IConnection connection, string signal, string hubName) + : this(connection, signal, hubName, null) { + } + + public SignalAgent(IConnection connection, string signal, string hubName, TrackingDictionary state) { + _connection = connection; + _signal = signal; + _hubName = hubName; + _state = state; + } + + public Task Invoke(string method, params object[] args) { + return ClientAgent.Invoke(_connection, _signal, _hubName, method, args); + } + + public override bool TrySetMember(SetMemberBinder binder, object value) { + if (_state == null) { + return false; + } + + _state[binder.Name] = value; + return true; + } + + public override bool TryGetMember(GetMemberBinder binder, out object result) { + if (_state == null) { + result = null; + return false; + } + + result = _state[binder.Name]; + return true; + } + + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { + if (_state != null) { + result = InvokeWithState(binder.Name, args); + } + else { + result = Invoke(binder.Name, args); + } + return true; + } + + private Task InvokeWithState(string method, object[] args) { + string signal = _hubName + "." + _signal; + + var invocation = new { + Hub = _hubName, + Method = method, + Args = args, + State = _state.GetChanges() + }; + + return _connection.Broadcast(signal, invocation); + } + } +} diff --git a/SignalR/Hubs/TrackingDictionary.cs b/SignalR/Hubs/TrackingDictionary.cs new file mode 100644 index 000000000..aa8ece176 --- /dev/null +++ b/SignalR/Hubs/TrackingDictionary.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; + +namespace SignalR.Hubs { + public class TrackingDictionary { + private readonly IDictionary _values; + // Keep track of everyting that changed since creation + private readonly IDictionary _oldValues = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public TrackingDictionary() { + _values = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + public TrackingDictionary(IDictionary values) { + _values = values; + } + + public object this[string key] { + get { + object result; + _values.TryGetValue(key, out result); + return result; + } + set { + if (!_oldValues.ContainsKey(key)) { + object oldValue; + _values.TryGetValue(key, out oldValue); + _oldValues[key] = oldValue; + } + + _values[key] = value; + } + } + + public IDictionary GetChanges() { + var changes = (from key in _oldValues.Keys + let oldValue = _oldValues[key] + let newValue = _values[key] + where !Object.Equals(oldValue, newValue) + select new { + Key = key, + Value = newValue + }).ToDictionary(p => p.Key, p => p.Value); + + return changes; + } + } +} diff --git a/SignalR/IConnection.cs b/SignalR/IConnection.cs new file mode 100644 index 000000000..845fcb11c --- /dev/null +++ b/SignalR/IConnection.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading.Tasks; + +namespace SignalR { + public interface IConnection { + TimeSpan ReceiveTimeout { get; set; } + + Task Send(object value); + Task Broadcast(string message, object value); + Task Broadcast(object value); + + Task ReceiveAsync(); + Task ReceiveAsync(long messageId); + } +} diff --git a/SignalR/IGroupManager.cs b/SignalR/IGroupManager.cs new file mode 100644 index 000000000..2dd30d2f0 --- /dev/null +++ b/SignalR/IGroupManager.cs @@ -0,0 +1,6 @@ +namespace SignalR { + public interface IGroupManager { + void AddToGroup(string clientId, string groupName); + void RemoveFromGroup(string clientId, string groupName); + } +} diff --git a/SignalR/IJsonStringifier.cs b/SignalR/IJsonStringifier.cs new file mode 100644 index 000000000..ffe6b265e --- /dev/null +++ b/SignalR/IJsonStringifier.cs @@ -0,0 +1,6 @@ + +namespace SignalR { + public interface IJsonStringifier { + string Stringify(object obj); + } +} \ No newline at end of file diff --git a/SignalR/IMessageStore.cs b/SignalR/IMessageStore.cs new file mode 100644 index 000000000..a45cfa2aa --- /dev/null +++ b/SignalR/IMessageStore.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace SignalR { + public interface IMessageStore { + Task GetLastId(); + Task Save(string key, object value); + Task> GetAll(string key); + Task> GetAllSince(string key, long id); + } +} diff --git a/SignalR/ISignalBus.cs b/SignalR/ISignalBus.cs new file mode 100644 index 000000000..937ed0388 --- /dev/null +++ b/SignalR/ISignalBus.cs @@ -0,0 +1,10 @@ +using System; +using System.Threading.Tasks; + +namespace SignalR { + public interface ISignalBus { + void AddHandler(string eventKey, EventHandler handler); + void RemoveHandler(string eventKey, EventHandler handler); + Task Signal(string eventKey); + } +} \ No newline at end of file diff --git a/SignalR/InProcessMessageStore.cs b/SignalR/InProcessMessageStore.cs new file mode 100644 index 000000000..9fe60ecc6 --- /dev/null +++ b/SignalR/InProcessMessageStore.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using SignalR.Infrastructure; + +namespace SignalR { + public class InProcessMessageStore : IMessageStore { + private readonly ConcurrentDictionary> _items = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); + // Interval to wait before cleaning up expired items + private static readonly TimeSpan _cleanupInterval = TimeSpan.FromSeconds(10); + + private static long _messageId = 0; + + private readonly Timer _timer; + + public InProcessMessageStore() { + _timer = new Timer(RemoveExpiredEntries, null, _cleanupInterval, _cleanupInterval); + } + + public Task Save(string key, object value) { + SafeSet list; + if (!_items.TryGetValue(key, out list)) { + list = new SafeSet(); + _items.TryAdd(key, list); + } + list.Add(new Message(key, Interlocked.Increment(ref _messageId), value)); + return TaskAsyncHelper.Empty; + } + + public Task> GetAllSince(string key, long id) { + var items = GetAllCore(key).Where(item => item.Id > id) + .OrderBy(item => item.Id); + return TaskAsyncHelper.FromResult>(items); + + } + + public Task GetLastId() { + if (_messageId > 0) { + return TaskAsyncHelper.FromResult(_messageId); + } + + return TaskAsyncHelper.FromResult(null); + } + + public Task> GetAll(string key) { + return TaskAsyncHelper.FromResult(GetAllCore(key)); + } + + private IEnumerable GetAllCore(string key) { + SafeSet list; + if (_items.TryGetValue(key, out list)) { + // Return a copy of the list + return list.GetSnapshot(); + } + return Enumerable.Empty(); + } + + private void RemoveExpiredEntries(object state) { + // Take a snapshot of the entries + var entries = _items.ToList(); + + // Remove all the expired ones + foreach (var entry in entries) { + foreach (var item in entry.Value.GetSnapshot()) { + if (item.Expired) { + entry.Value.Remove(item); + } + } + } + } + } +} \ No newline at end of file diff --git a/SignalR/InProcessSignalBus.cs b/SignalR/InProcessSignalBus.cs new file mode 100644 index 000000000..c48085528 --- /dev/null +++ b/SignalR/InProcessSignalBus.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; +using SignalR.Infrastructure; + +namespace SignalR { + /// + /// An in-memory signaler that signals directly on an incoming signal + /// + public class InProcessSignalBus : ISignalBus { + private readonly ConcurrentDictionary>> _handlers = new ConcurrentDictionary>>(StringComparer.OrdinalIgnoreCase); + + private void OnSignaled(string eventKey) { + SafeSet> handlers; + if (_handlers.TryGetValue(eventKey, out handlers)) { + Parallel.ForEach(handlers.GetSnapshot(), handler => handler(this, new SignaledEventArgs(eventKey))); + } + } + + public Task Signal(string eventKey) { + return Task.Factory.StartNew(() => OnSignaled(eventKey)); + } + + public void AddHandler(string eventKey, EventHandler handler) { + _handlers.AddOrUpdate(eventKey, new SafeSet>(new[] { handler }), (key, list) => { + list.Add(handler); + return list; + }); + } + + public void RemoveHandler(string eventKey, EventHandler handler) { + SafeSet> handlers; + if (_handlers.TryGetValue(eventKey, out handlers)) { + handlers.Remove(handler); + } + } + } +} \ No newline at end of file diff --git a/SignalR/Infrastructure/DependencyResolver.cs b/SignalR/Infrastructure/DependencyResolver.cs new file mode 100644 index 000000000..35ef0e32e --- /dev/null +++ b/SignalR/Infrastructure/DependencyResolver.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Script.Serialization; +using SignalR.Hubs; +using SignalR.SignalBuses; + +namespace SignalR.Infrastructure { + public static class DependencyResolver { + private static readonly IDependencyResolver _defaultResolver = new DefaultDependencyResolver(); + private static IDependencyResolver _resolver; + + private static IDependencyResolver Current { + get { return _resolver ?? _defaultResolver; } + } + + public static void SetResolver(IDependencyResolver resolver) { + if (resolver == null) { + throw new ArgumentNullException("resolver"); + } + + _resolver = new FallbackDependencyResolver(resolver, _defaultResolver); + } + + public static T Resolve() { + return (T)Current.GetService(typeof(T)); + } + + public static object Resolve(Type type) { + return Current.GetService(type); + } + + public static void Register(Type type, Func activator) { + Current.Register(type, activator); + } + + public static void Register(Type serviceType, IEnumerable> activators) { + Current.Register(serviceType, activators); + } + + private class FallbackDependencyResolver : IDependencyResolver { + private readonly IDependencyResolver _resolver; + private readonly IDependencyResolver _fallbackResolver; + + public FallbackDependencyResolver(IDependencyResolver resolver, IDependencyResolver fallbackResolver) { + _resolver = resolver; + _fallbackResolver = fallbackResolver; + } + + public object GetService(Type serviceType) { + return _resolver.GetService(serviceType) ?? _fallbackResolver.GetService(serviceType); + } + + public IEnumerable GetServices(Type serviceType) { + return _resolver.GetServices(serviceType).Concat(_fallbackResolver.GetServices(serviceType)); + } + + public void Register(Type serviceType, Func activator) { + _resolver.Register(serviceType, activator); + } + + public void Register(Type serviceType, IEnumerable> activators) { + _resolver.Register(serviceType, activators); + } + } + + private class DefaultDependencyResolver : IDependencyResolver { + private readonly Dictionary>> _resolvers = new Dictionary>>(); + + internal DefaultDependencyResolver() { + var store = new InProcessMessageStore(); + + Register(typeof(IMessageStore), () => store); + + var serialzier = new JavaScriptSerializerAdapter(new JavaScriptSerializer { + MaxJsonLength = 30 * 1024 * 1024 + }); + + Register(typeof(IJsonStringifier), () => serialzier); + + Register(typeof(IPeerUrlSource), () => new ConfigPeerUrlSource()); + Register(typeof(IActionResolver), () => new DefaultActionResolver()); + Register(typeof(IHubActivator), () => new DefaultHubActivator()); + Register(typeof(IHubFactory), () => new DefaultHubFactory()); + + var hubLocator = new DefaultHubLocator(); + Register(typeof(IHubLocator), () => hubLocator); + + var signalBus = new InProcessSignalBus(); + Register(typeof(ISignalBus), () => signalBus); + + var proxyGenerator = new DefaultJavaScriptProxyGenerator(hubLocator); + Register(typeof(IJavaScriptProxyGenerator), () => proxyGenerator); + + // TODO: Register ITransport implementations and resolve via the dependency resolver + } + + public object GetService(Type serviceType) { + IList> activators; + if (_resolvers.TryGetValue(serviceType, out activators)) { + if (activators.Count == 0) { + return null; + } + if (activators.Count > 1) { + throw new InvalidOperationException(String.Format("Multiple activators for type {0} are registered. Please call GetServices instead.", serviceType.FullName)); + } + return activators[0](); + } + return null; + } + + public IEnumerable GetServices(Type serviceType) { + IList> activators; + if (_resolvers.TryGetValue(serviceType, out activators)) { + if (activators.Count == 0) { + return null; + } + return activators.Select(r => r()).ToList(); + } + return null; + } + + public void Register(Type serviceType, Func activator) { + IList> activators; + if (!_resolvers.TryGetValue(serviceType, out activators)) { + activators = new List>(); + _resolvers.Add(serviceType, activators); + } + else { + activators.Clear(); + } + activators.Add(activator); + } + + public void Register(Type serviceType, IEnumerable> activators) { + IList> list; + if (!_resolvers.TryGetValue(serviceType, out list)) { + list = new List>(); + _resolvers.Add(serviceType, list); + } + else { + list.Clear(); + } + foreach (var a in activators) { + list.Add(a); + } + } + } + } +} \ No newline at end of file diff --git a/SignalR/Infrastructure/IDependencyResolver.cs b/SignalR/Infrastructure/IDependencyResolver.cs new file mode 100644 index 000000000..fe7461e48 --- /dev/null +++ b/SignalR/Infrastructure/IDependencyResolver.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; + +namespace SignalR.Infrastructure { + public interface IDependencyResolver { + object GetService(Type serviceType); + IEnumerable GetServices(Type serviceType); + void Register(Type serviceType, Func activator); + void Register(Type serviceType, IEnumerable> activators); + } +} \ No newline at end of file diff --git a/SignalR/Infrastructure/SafeSet.cs b/SignalR/Infrastructure/SafeSet.cs new file mode 100644 index 000000000..6fbc8088d --- /dev/null +++ b/SignalR/Infrastructure/SafeSet.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.Linq; + +namespace SignalR.Infrastructure { + internal class SafeSet { + private readonly HashSet _items = new HashSet(); + + public SafeSet() { + } + + public SafeSet(IEqualityComparer comparer) { + _items = new HashSet(comparer); + } + + public SafeSet(IEnumerable items) { + _items = new HashSet(items); + } + + public IEnumerable GetSnapshot() { + lock (_items) { + return _items.ToArray(); + } + } + + public void Add(T item) { + lock (_items) { + _items.Add(item); + } + } + + public void Remove(T item) { + lock (_items) { + _items.Remove(item); + } + } + } +} diff --git a/SignalR/JavaScriptSerializerAdapter.cs b/SignalR/JavaScriptSerializerAdapter.cs new file mode 100644 index 000000000..237d3ec94 --- /dev/null +++ b/SignalR/JavaScriptSerializerAdapter.cs @@ -0,0 +1,16 @@ +using System.Web.Script.Serialization; +using System; + +namespace SignalR { + public class JavaScriptSerializerAdapter : IJsonStringifier { + private JavaScriptSerializer _serializer; + + public JavaScriptSerializerAdapter(JavaScriptSerializer serializer) { + _serializer = serializer; + } + + public string Stringify(object obj) { + return _serializer.Serialize(obj); + } + } +} \ No newline at end of file diff --git a/SignalR/Json.cs b/SignalR/Json.cs new file mode 100644 index 000000000..2d06dfb9c --- /dev/null +++ b/SignalR/Json.cs @@ -0,0 +1,18 @@ +using System; +using System.Linq; +using System.Web.Script.Serialization; + +namespace SignalR { + internal static class Json { + internal static string CamelCase(string value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + return String.Join(".", value.Split('.').Select(n => Char.ToLower(n[0]) + n.Substring(1))); + } + + internal static string MimeType { + get { return "application/json"; } + } + } +} \ No newline at end of file diff --git a/SignalR/Message.cs b/SignalR/Message.cs new file mode 100644 index 000000000..86d266374 --- /dev/null +++ b/SignalR/Message.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading; + +namespace SignalR { + public class Message { + public string SignalKey { get; set; } + public object Value { get; private set; } + public long Id { get; private set; } + public DateTime Created { get; private set; } + + public bool Expired { + get { + return DateTime.Now.Subtract(Created).TotalSeconds >= 30; + } + } + + private Message() { } + + public Message(string signalKey, long id, object value) + : this(signalKey, id, value, DateTime.Now) { + + } + + public Message(string signalKey, long id, object value, DateTime created) { + SignalKey = signalKey; + Value = value; + Id = id; + Created = created; + } + } +} \ No newline at end of file diff --git a/SignalR/PersistentConnection.cs b/SignalR/PersistentConnection.cs new file mode 100644 index 000000000..5bf6530a1 --- /dev/null +++ b/SignalR/PersistentConnection.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Web; +using SignalR.Infrastructure; +using SignalR.Transports; +using SignalR.Web; + +namespace SignalR { + public abstract class PersistentConnection : HttpTaskAsyncHandler, IGroupManager { + internal const string SignalrCommand = "__SIGNALRCOMMAND__"; + + private readonly Signaler _signaler; + private readonly IMessageStore _store; + private readonly IJsonStringifier _jsonStringifier; + + protected ITransport _transport; + + protected PersistentConnection() + : this(Signaler.Instance, + DependencyResolver.Resolve(), + DependencyResolver.Resolve()) { + } + + protected PersistentConnection(Signaler signaler, + IMessageStore store, + IJsonStringifier jsonStringifier) { + _signaler = signaler; + _store = store; + _jsonStringifier = jsonStringifier; + } + + public override bool IsReusable { + get { + return false; + } + } + + public IConnection Connection { + get; + private set; + } + + private string DefaultSignal { + get { + return GetType().FullName; + } + } + + public override Task ProcessRequestAsync(HttpContext context) { + Task task = null; + + if (IsNegotiationRequest(context.Request)) { + context.Response.ContentType = Json.MimeType; + context.Response.Write(_jsonStringifier.Stringify(new { + Url = VirtualPathUtility.ToAbsolute(context.Request.AppRelativeCurrentExecutionFilePath.Replace("/negotiate", "")), + ClientId = Guid.NewGuid().ToString("d") + })); + } + else { + var contextBase = new HttpContextWrapper(context); + _transport = GetTransport(contextBase); + + string clientId = contextBase.Request["clientId"]; + IEnumerable groups = GetGroups(contextBase); + + Connection = CreateConnection(clientId, groups, contextBase); + + // Wire up the events we needs + _transport.Connected += () => { + OnConnected(contextBase, clientId); + }; + + _transport.Received += (data) => { + task = OnReceivedAsync(clientId, data); + }; + + _transport.Error += (e) => { + OnError(e); + }; + + _transport.Disconnected += () => { + OnDisconnect(clientId); + }; + + var processRequestTask = _transport.ProcessRequest(Connection); + + if (processRequestTask != null) { + return processRequestTask; + } + } + + return task ?? TaskAsyncHelper.Empty; + } + + protected virtual IConnection CreateConnection(string clientId, IEnumerable groups, HttpContextBase context) { + string groupValue = context.Request["groups"] ?? String.Empty; + + // The list of default signals this connection cares about: + // 1. The default signal (the type name) + // 2. The client id (so we can message this particular connection) + // 3. client id + SIGNALRCOMMAND -> for built in commands that we need to process + var signals = new string[] { + DefaultSignal, + clientId, + clientId + "." + SignalrCommand + }; + + return new Connection(_store, _signaler, DefaultSignal, clientId, signals, groups); + } + + protected virtual void OnConnected(HttpContextBase context, string clientId) { } + + protected virtual Task OnReceivedAsync(string clientId, string data) { + OnReceived(clientId, data); + return TaskAsyncHelper.Empty; + } + + protected virtual void OnReceived(string clientId, string data) { } + + protected virtual void OnDisconnect(string clientId) { } + + protected virtual void OnError(Exception e) { } + + public void Send(object value) { + _transport.Send(value); + } + + public void Send(string clientId, object value) { + Connection.Broadcast(clientId, value); + } + + public void SendToGroup(string groupName, object value) { + Connection.Broadcast(CreateQualifiedName(groupName), value); + } + + public void AddToGroup(string clientId, string groupName) { + groupName = CreateQualifiedName(groupName); + SendCommand(clientId, CommandType.AddToGroup, groupName); + } + + public void RemoveFromGroup(string clientId, string groupName) { + groupName = CreateQualifiedName(groupName); + SendCommand(clientId, CommandType.RemoveFromGroup, groupName); + } + + private void SendCommand(string clientId, CommandType type, object value) { + string signal = clientId + "." + SignalrCommand; + + var groupCommand = new SignalCommand { + Type = type, + Value = value + }; + + Connection.Broadcast(signal, groupCommand); + } + + private string CreateQualifiedName(string groupName) { + return DefaultSignal + "." + groupName; + } + + private IEnumerable GetGroups(HttpContextBase context) { + string groupValue = context.Request["groups"]; + + if (String.IsNullOrEmpty(groupValue)) { + return Enumerable.Empty(); + } + + return groupValue.Split(','); + } + + private bool IsNegotiationRequest(HttpRequest httpRequest) { + return httpRequest.Path.EndsWith("/negotiate", StringComparison.OrdinalIgnoreCase); + } + + private ITransport GetTransport(HttpContextBase context) { + return TransportManager.GetTransport(context) ?? + new LongPollingTransport(context, _jsonStringifier); + } + } +} \ No newline at end of file diff --git a/SignalR/PersistentResponse.cs b/SignalR/PersistentResponse.cs new file mode 100644 index 000000000..c37eb7fbb --- /dev/null +++ b/SignalR/PersistentResponse.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace SignalR { + public class PersistentResponse { + private readonly IDictionary _transportData = new Dictionary(); + + public long MessageId { get; set; } + public IEnumerable Messages { get; set; } + + public IDictionary TransportData { + get { return _transportData; } + } + } +} \ No newline at end of file diff --git a/SignalR/PreApplicationStart.cs b/SignalR/PreApplicationStart.cs new file mode 100644 index 000000000..f369a4d8e --- /dev/null +++ b/SignalR/PreApplicationStart.cs @@ -0,0 +1,21 @@ +using System.Web; +using Microsoft.Web.Infrastructure.DynamicModuleHelper; +using SignalR; +using SignalR.Hubs; +using SignalR.SignalBuses; +using SignalR.Transports; +using SignalR.Infrastructure; + +[assembly: PreApplicationStartMethod(typeof(PreApplicationStart), "Start")] + +namespace SignalR { + public static class PreApplicationStart { + public static void Start() { + DynamicModuleUtility.RegisterModule(typeof(HubModule)); + DynamicModuleUtility.RegisterModule(typeof(SignalReceiverModule)); + + TransportManager.Register("longPolling", context => new LongPollingTransport(context, DependencyResolver.Resolve())); + TransportManager.Register("forever", context => new ForeverTransport(context, DependencyResolver.Resolve())); + } + } +} \ No newline at end of file diff --git a/SignalR/Properties/AssemblyInfo.cs b/SignalR/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..d19cee0a2 --- /dev/null +++ b/SignalR/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SignalR.Core")] +[assembly: AssemblyDescription("A client and server side library for ASP.NET that provides messaging and an abstraction over a persistant connection.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("SignalR.Core")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("960f3974-c967-4b92-b196-5747f9b49552")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/SignalR/Properties/SignalR.nuspec b/SignalR/Properties/SignalR.nuspec new file mode 100644 index 000000000..aa791b7b9 --- /dev/null +++ b/SignalR/Properties/SignalR.nuspec @@ -0,0 +1,14 @@ + + + + $id$ + $version$ + $author$ + $author$ + false + $description$ + + + + + \ No newline at end of file diff --git a/SignalR/Scripts/SignalR.min.js b/SignalR/Scripts/SignalR.min.js new file mode 100644 index 000000000..b3b807c32 --- /dev/null +++ b/SignalR/Scripts/SignalR.min.js @@ -0,0 +1 @@ +(function(n,t){"use strict";if(typeof t.signalR=="function")return;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";var i;i=function(n){return new i.fn.init(n)},i.fn=i.prototype={init:function(n){this.url=n},start:function(r,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(r)==="function"?u=r:n.type(r)==="object"&&(n.extend(e,r),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,r){r=r||0;if(r>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[r],e=n.type(u)==="object"?u:i.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,r+1)})},t.setTimeout(function(){n.post(f.url+"/negotiate",{},function(t){f.appRelativeUrl=t.Url,f.clientId=t.ClientId,n(f).trigger("onStarting");var u=[],r=[];n.each(i.transports,function(n){r.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,r)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,r)<0?u=r:u.push(e.transport),o(u)})},0),f)},starting:function(t){var i=this;return n(i).bind("onStarting",function(){t.call(i)}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),n}},i.fn.init.prototype=i.fn,i.transports={webSockets:{send:function(n,t){n.socket.send(t)},start:function(i,r,u){var f,e;if(n.type(t.WebSocket)!=="object"){u();return}i.socket||(f=document.location.host+i.appRelativeUrl,n(i).trigger("onSending"),f+=i.data?"?data="+i.data+"&transport=webSockets&clientId="+i.clientId:"?transport=webSockets&clientId="+i.clientId,i.socket=new t.WebSocket("ws://"+f),e=!1,i.socket.onopen=function(){e=!0,r&&r()},i.socket.onclose=function(){e||u&&u(),i.socket=null},i.socket.onmessage=function(r){var u=t.JSON.parse(r.data);u&&(u.Messages?n.each(u.Messages,function(){n(i).trigger("onReceived",[this])}):n(i).trigger("onReceived",[u]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},longPolling:{start:function(i,r){i.pollXhr&&i.stop(),i.messageId=null,t.setTimeout(function(){(function u(i){n(i).trigger("onSending");var r=i.messageId,e=r===null,f=i.url+(e?"/connect":"");i.pollXhr=n.ajax(f,{type:"POST",data:{clientId:i.clientId,messageId:r,data:i.data,transport:"longPolling",groups:(i.groups||[]).toString()},dataType:"json",success:function(r){var f=0;r&&(r.Messages&&n.each(r.Messages,function(){n(i).trigger("onReceived",[this])}),i.messageId=r.MessageId,n.type(r.TransportData.LongPollDelay)==="number"&&(f=r.TransportData.LongPollDelay),i.groups=r.TransportData.Groups),f>0?t.setTimeout(function(){u(i)},f):u(i)},error:function(r,f){if(f==="abort")return;n(i).trigger("onError",[r]),t.setTimeout(function(){u(i)},2e3)}})})(i),r()},250)},send:function(t,i){n.ajax(t.url+"/send",{type:"POST",dataType:"json",data:{data:i,transport:"longPolling",clientId:t.clientId},success:function(i){i&&n(t).trigger("onReceived",[i])},error:function(i,r){if(r==="abort")return;n(t).trigger("onError",[i])}})},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},t.signalR=i})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR/Scripts/hubs.js b/SignalR/Scripts/hubs.js new file mode 100644 index 000000000..5351ab63b --- /dev/null +++ b/SignalR/Scripts/hubs.js @@ -0,0 +1,154 @@ +/// +(function ($, window) { + /// + "use strict"; + + if (typeof (window.signalR) !== "function") { + throw "SignalR: SignalR is not loaded. Please ensure SignalR.js is referenced before ~/signalr/hubs."; + } + + var hubs = {}, + signalR = window.signalR, + callbackId = 0, + callbacks = {}; + + function executeCallback(hubName, fn, args, state) { + var hub = hubs[hubName], + method; + if (hub) { + $.extend(hub.obj.state, state); + + method = hub[fn]; + if (method) { + method.apply(hub.obj, args); + } + } + } + + function updateClientMembers(instance) { + var newHubs = {}, + obj, + hubName = "", + newHub, + memberValue, + key, + memberKey; + + for (key in instance) { + if (instance.hasOwnProperty(key)) { + + obj = instance[key]; + + if ($.type(obj) !== "object" || + key === "prototype" || + key === "constructor" || + key === "fn" || + key === "hub" || + key === "transports") { + continue; + } + + newHub = null; + hubName = obj._.hubName; + + for (memberKey in obj) { + if (obj.hasOwnProperty(memberKey)) { + memberValue = obj[memberKey]; + + if (memberKey === "_" || + $.type(memberValue) !== "function" || + $.inArray(memberKey, obj._.serverMembers) >= 0) { + continue; + } + + if (!newHub) { + newHub = { obj: obj }; + + newHubs[hubName] = newHub; + } + + newHub[memberKey] = memberValue; + } + } + } + } + + hubs = {}; + $.extend(hubs, newHubs); + } + + function getArgValue(a) { + return $.isFunction(a) ? null : + ($.type(a) === "undefined" + ? null : a); + } + + function serverCall(hub, methodName, args) { + /// + var callback = args[args.length - 1], // last argument + methodArgs = $.type(callback) === "function" + ? args.slice(0, -1) // all but last + : args, + argValues = $.map(methodArgs, getArgValue), + data = { hub: hub._.hubName, action: methodName, data: argValues, state: hub.state, id: callbackId }, + d = $.Deferred(), + cb = function (result) { + $.extend(hub.state, result.State); + if (result.Error) { + d.rejectWith(hub, [result.Error]); + } else { + if ($.type(callback) === "function") { + callback.call(hub, result.Result); + } + d.resolveWith(hub, [result.Result]); + } + }; + + callbacks[callbackId.toString()] = { scope: hub, callback: cb }; + callbackId += 1; + hub._.connection().send(window.JSON.stringify(data)); + return d; + } + + // Create hub signalR instance + $.extend(signalR, { + /*hubs*/ + }); + + signalR.hub = signalR("{serviceUrl}") + .starting(function () { + updateClientMembers(signalR); + }) + .sending(function () { + var localHubs = []; + + $.each(hubs, function (key) { + var methods = []; + + $.each(this, function (key) { + if (key === "obj") { + return true; + } + + methods.push(key); + }); + + localHubs.push({ name: key, methods: methods }); + }); + + this.data = window.JSON.stringify(localHubs); + }) + .received(function (result) { + if (result) { + if (!result.Id) { + executeCallback(result.Hub, result.Method, result.Args, result.State); + } else { + var callback = callbacks[result.Id.toString()]; + if (callback) { + callback.callback.call(callback.scope, result); + } + } + } + }); + +}(window.jQuery, window)); \ No newline at end of file diff --git a/SignalR/Scripts/jquery-1.6.2-vsdoc.js b/SignalR/Scripts/jquery-1.6.2-vsdoc.js new file mode 100644 index 000000000..7b4fad84a --- /dev/null +++ b/SignalR/Scripts/jquery-1.6.2-vsdoc.js @@ -0,0 +1,6987 @@ +/* +* This file has been generated to support Visual Studio IntelliSense. +* You should not use this file at runtime inside the browser--it is only +* intended to be used only for design-time IntelliSense. Please use the +* standard jQuery library for all production use. +* +* Comment version: 1.6.2 +*/ + +/*! +* jQuery JavaScript Library v1.6.2 +* http://jquery.com/ +* +* Distributed in whole under the terms of the MIT +* +* Copyright 2010, John Resig +* +* 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. +* +* Includes Sizzle.js +* http://sizzlejs.com/ +* Copyright 2010, The Dojo Foundation +* Released under the MIT and BSD Licenses. +*/ + +(function ( window, undefined ) { +var jQuery = function( selector, context ) { +/// +/// 1: Accepts a string containing a CSS selector which is then used to match a set of elements. +/// 1.1 - $(selector, context) +/// 1.2 - $(element) +/// 1.3 - $(elementArray) +/// 1.4 - $(jQuery object) +/// 1.5 - $() +/// 2: Creates DOM elements on the fly from the provided string of raw HTML. +/// 2.1 - $(html, ownerDocument) +/// 2.2 - $(html, props) +/// 3: Binds a function to be executed when the DOM has finished loading. +/// 3.1 - $(callback) +/// +/// +/// A string containing a selector expression +/// +/// +/// A DOM Element, Document, or jQuery to use as context +/// +/// + + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }; +jQuery.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 ]( 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; + }; +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; +}; +jQuery._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; + }; +jQuery._data = function( elem, name, data ) { + + return jQuery.data( elem, name, data, true ); + }; +jQuery._mark = function( elem, type ) { + + if ( elem ) { + type = (type || "fx") + "mark"; + jQuery.data( elem, type, (jQuery.data(elem,type,undefined,true) || 0) + 1, true ); + } + }; +jQuery._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" ); + } + } + }; +jQuery.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.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; + }; +jQuery.active = 0; +jQuery.ajax = function( url, options ) { +/// +/// Perform an asynchronous HTTP (Ajax) request. +/// 1 - jQuery.ajax(url, settings) +/// 2 - jQuery.ajax(settings) +/// +/// +/// A string containing the URL to which the request is sent. +/// +/// +/// A set of key/value pairs that configure the Ajax request. All settings are optional. A default can be set for any option with $.ajaxSetup(). See jQuery.ajax( settings ) below for a complete list of all settings. +/// + + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + // Callbacks context + callbackContext = s.context || s, + // Context for global events + // It's the callbackContext if one was provided in the options + // and if it's a DOM node or a jQuery collection + globalEventContext = callbackContext !== s && + ( callbackContext.nodeType || callbackContext instanceof jQuery ) ? + jQuery( callbackContext ) : jQuery.event, + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery._Deferred(), + // Status-dependent callbacks + statusCode = s.statusCode || {}, + // ifModified key + ifModifiedKey, + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + // Response headers + responseHeadersString, + responseHeaders, + // transport + transport, + // timeout handle + timeoutTimer, + // Cross-domain detection vars + parts, + // The jqXHR state + state = 0, + // To know if global events are to be dispatched + fireGlobals, + // Loop variable + i, + // Fake xhr + jqXHR = { + + readyState: 0, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( !state ) { + var lname = name.toLowerCase(); + name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Raw string + getAllResponseHeaders: function() { + return state === 2 ? responseHeadersString : null; + }, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( state === 2 ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[1].toLowerCase() ] = match[ 2 ]; + } + } + match = responseHeaders[ key.toLowerCase() ]; + } + return match === undefined ? null : match; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( !state ) { + s.mimeType = type; + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + statusText = statusText || "abort"; + if ( transport ) { + transport.abort( statusText ); + } + done( 0, statusText ); + return this; + } + }; + + // Callback for when everything is done + // It is defined here because jslint complains if it is declared + // at the end of the function (which would be more logical and readable) + function done( status, statusText, responses, headers ) { + + // Called once + if ( state === 2 ) { + return; + } + + // State is "done" now + state = 2; + + // Clear timeout if it exists + if ( timeoutTimer ) { + clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status ? 4 : 0; + + var isSuccess, + success, + error, + response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined, + lastModified, + etag; + + // If successful, handle type chaining + if ( status >= 200 && status < 300 || status === 304 ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + + if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) { + jQuery.lastModified[ ifModifiedKey ] = lastModified; + } + if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) { + jQuery.etag[ ifModifiedKey ] = etag; + } + } + + // If not modified + if ( status === 304 ) { + + statusText = "notmodified"; + isSuccess = true; + + // If we have data + } else { + + try { + success = ajaxConvert( s, response ); + statusText = "success"; + isSuccess = true; + } catch(e) { + // We have a parsererror + statusText = "parsererror"; + error = e; + } + } + } else { + // We extract error from statusText + // then normalize statusText and status for non-aborts + error = statusText; + if( !statusText || status ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = statusText; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ), + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.resolveWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s] ); + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + // Attach deferreds + deferred.promise( jqXHR ); + jqXHR.success = jqXHR.done; + jqXHR.error = jqXHR.fail; + jqXHR.complete = completeDeferred.done; + + // Status-dependent callbacks + jqXHR.statusCode = function( map ) { + if ( map ) { + var tmp; + if ( state < 2 ) { + for( tmp in map ) { + statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ]; + } + } else { + tmp = map[ jqXHR.status ]; + jqXHR.then( tmp, tmp ); + } + } + return this; + }; + + // Remove hash character (#7531: and string promotion) + // Add protocol if not provided (#5866: IE7 issue with protocol-less urls) + // We also use the url parameter if available + s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); + + // Extract dataTypes list + s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax ); + + // Determine if a cross-domain request is in order + if ( s.crossDomain == null ) { + parts = rurl.exec( s.url.toLowerCase() ); + s.crossDomain = !!( parts && + ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] || + ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) != + ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) ) + ); + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefiler, stop there + if ( state === 2 ) { + return false; + } + + // We can fire global events as of now if asked to + fireGlobals = s.global; + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // If data is available, append data to url + if ( s.data ) { + s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data; + } + + // Get ifModifiedKey before adding the anti-cache parameter + ifModifiedKey = s.url; + + // Add anti-cache in url if needed + if ( s.cache === false ) { + + var ts = jQuery.now(), + // try replacing _= if it is there + ret = s.url.replace( rts, "$1_=" + ts ); + + // if nothing was replaced, add timestamp to the end + s.url = ret + ( (ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + ifModifiedKey = ifModifiedKey || s.url; + if ( jQuery.lastModified[ ifModifiedKey ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] ); + } + if ( jQuery.etag[ ifModifiedKey ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] ); + } + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ? + s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", */*; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { + // Abort if not done already + jqXHR.abort(); + return false; + + } + + // Install callbacks on deferreds + for ( i in { success: 1, error: 1, complete: 1 } ) { + jqXHR[ i ]( s[ i ] ); + } + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = setTimeout( function(){ + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + state = 1; + transport.send( requestHeaders, done ); + } catch (e) { + // Propagate exception as error if not done + if ( status < 2 ) { + done( -1, e ); + // Simply rethrow otherwise + } else { + jQuery.error( e ); + } + } + } + + return jqXHR; + }; +jQuery.ajaxPrefilter = function( dataTypeExpression, func ) { +/// +/// Handle custom Ajax options or modify existing options before each request is sent and before they are processed by $.ajax(). +/// +/// +/// An optional string containing one or more space-separated dataTypes +/// +/// +/// A handler to set default values for future Ajax requests. +/// +/// + + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + if ( jQuery.isFunction( func ) ) { + var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ), + i = 0, + length = dataTypes.length, + dataType, + list, + placeBefore; + + // For each dataType in the dataTypeExpression + for(; i < length; i++ ) { + dataType = dataTypes[ i ]; + // We control if we're asked to add before + // any existing element + placeBefore = /^\+/.test( dataType ); + if ( placeBefore ) { + dataType = dataType.substr( 1 ) || "*"; + } + list = structure[ dataType ] = structure[ dataType ] || []; + // then we add to the structure accordingly + list[ placeBefore ? "unshift" : "push" ]( func ); + } + } + }; +jQuery.ajaxSettings = { "url": 'http://localhost:25813/?ver=1.6.2&newLineMethod=para', +"isLocal": false, +"global": true, +"type": 'GET', +"contentType": 'application/x-www-form-urlencoded', +"processData": true, +"async": true, +"accepts": {}, +"contents": {}, +"responseFields": {}, +"converters": {}, +"jsonp": 'callback' }; +jQuery.ajaxSetup = function ( target, settings ) { +/// +/// Set default values for future Ajax requests. +/// +/// +/// A set of key/value pairs that configure the default Ajax request. All options are optional. +/// + + if ( !settings ) { + // Only one parameter, we extend ajaxSettings + settings = target; + target = jQuery.extend( true, jQuery.ajaxSettings, settings ); + } else { + // target was provided, we extend into it + jQuery.extend( true, target, jQuery.ajaxSettings, settings ); + } + // Flatten fields we don't want deep extended + for( var field in { context: 1, url: 1 } ) { + if ( field in settings ) { + target[ field ] = settings[ field ]; + } else if( field in jQuery.ajaxSettings ) { + target[ field ] = jQuery.ajaxSettings[ field ]; + } + } + return target; + }; +jQuery.ajaxTransport = function( dataTypeExpression, func ) { + + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + if ( jQuery.isFunction( func ) ) { + var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ), + i = 0, + length = dataTypes.length, + dataType, + list, + placeBefore; + + // For each dataType in the dataTypeExpression + for(; i < length; i++ ) { + dataType = dataTypes[ i ]; + // We control if we're asked to add before + // any existing element + placeBefore = /^\+/.test( dataType ); + if ( placeBefore ) { + dataType = dataType.substr( 1 ) || "*"; + } + list = structure[ dataType ] = structure[ dataType ] || []; + // then we add to the structure accordingly + list[ placeBefore ? "unshift" : "push" ]( func ); + } + } + }; +jQuery.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 formHook for forms and if the name contains certain characters + } else if ( formHook && name !== "className" && + (jQuery.nodeName( elem, "form" ) || rinvalidChar.test( name )) ) { + + hooks = formHook; + } + } + } + + 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; + } + }; +jQuery.attrFix = { "tabindex": 'tabIndex' }; +jQuery.attrFn = { "val": true, +"css": true, +"html": true, +"text": true, +"data": true, +"width": true, +"height": true, +"offset": true, +"blur": true, +"focus": true, +"focusin": true, +"focusout": true, +"load": true, +"resize": true, +"scroll": true, +"unload": true, +"click": true, +"dblclick": true, +"mousedown": true, +"mouseup": true, +"mousemove": true, +"mouseover": true, +"mouseout": true, +"mouseenter": true, +"mouseleave": true, +"change": true, +"select": true, +"submit": true, +"keydown": true, +"keypress": true, +"keyup": true, +"error": true }; +jQuery.attrHooks = { "type": {}, +"tabIndex": {}, +"value": {} }; +jQuery.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(); + } + } + }; +jQuery.boxModel = true; +jQuery.browser = { "msie": true, +"version": '9.0' }; +jQuery.buildFragment = function( args, nodes, scripts ) { + + var fragment, cacheable, cacheresults, doc; + + // nodes may contain either an explicit document object, + // a jQuery collection or context object. + // If nodes[0] contains a valid object to assign to doc + if ( nodes && nodes[0] ) { + doc = nodes[0].ownerDocument || nodes[0]; + } + + // Ensure that an attr object doesn't incorrectly stand in as a document object + // Chrome and Firefox seem to allow this to occur and will throw exception + // Fixes #8950 + if ( !doc.createDocumentFragment ) { + doc = document; + } + + // Only cache "small" (1/2 KB) HTML strings that are associated with the main document + // Cloning options loses the selected state, so don't cache them + // IE 6 doesn't like it when you put or elements in a fragment + // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache + if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document && + args[0].charAt(0) === "<" && !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) { + + cacheable = true; + + cacheresults = jQuery.fragments[ args[0] ]; + if ( cacheresults && cacheresults !== 1 ) { + fragment = cacheresults; + } + } + + if ( !fragment ) { + fragment = doc.createDocumentFragment(); + jQuery.clean( args, doc, fragment, scripts ); + } + + if ( cacheable ) { + jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1; + } + + return { fragment: fragment, cacheable: cacheable }; +}; +jQuery.cache = {}; +jQuery.camelCase = function( string ) { + + return string.replace( rdashAlpha, fcamelCase ); + }; +jQuery.clean = function( elems, context, fragment, scripts ) { + + var checkScriptType; + + context = context || document; + + // !context.createElement fails in IE with an error but returns typeof 'object' + if ( typeof context.createElement === "undefined" ) { + context = context.ownerDocument || context[0] && context[0].ownerDocument || document; + } + + var ret = [], j; + + for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { + if ( typeof elem === "number" ) { + elem += ""; + } + + if ( !elem ) { + continue; + } + + // Convert html string into DOM nodes + if ( typeof elem === "string" ) { + if ( !rhtml.test( elem ) ) { + elem = context.createTextNode( elem ); + } else { + // Fix "XHTML"-style tags in all browsers + elem = elem.replace(rxhtmlTag, "<$1>"); + + // Trim whitespace, otherwise indexOf won't work as expected + var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(), + wrap = wrapMap[ tag ] || wrapMap._default, + depth = wrap[0], + div = context.createElement("div"); + + // Go to html and back, then peel off extra wrappers + div.innerHTML = wrap[1] + elem + wrap[2]; + + // Move to the right depth + while ( depth-- ) { + div = div.lastChild; + } + + // Remove IE's autoinserted from table fragments + if ( !jQuery.support.tbody ) { + + // String was a , *may* have spurious + var hasBody = rtbody.test(elem), + tbody = tag === "table" && !hasBody ? + div.firstChild && div.firstChild.childNodes : + + // String was a bare or + wrap[1] === "
    " && !hasBody ? + div.childNodes : + []; + + for ( j = tbody.length - 1; j >= 0 ; --j ) { + if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) { + tbody[ j ].parentNode.removeChild( tbody[ j ] ); + } + } + } + + // IE completely kills leading whitespace when innerHTML is used + if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild ); + } + + elem = div.childNodes; + } + } + + // Resets defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + var len; + if ( !jQuery.support.appendChecked ) { + if ( elem[0] && typeof (len = elem.length) === "number" ) { + for ( j = 0; j < len; j++ ) { + findInputs( elem[j] ); + } + } else { + findInputs( elem ); + } + } + + if ( elem.nodeType ) { + ret.push( elem ); + } else { + ret = jQuery.merge( ret, elem ); + } + } + + if ( fragment ) { + checkScriptType = function( elem ) { + return !elem.type || rscriptType.test( elem.type ); + }; + for ( i = 0; ret[i]; i++ ) { + if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) { + scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] ); + + } else { + if ( ret[i].nodeType === 1 ) { + var jsTags = jQuery.grep( ret[i].getElementsByTagName( "script" ), checkScriptType ); + + ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) ); + } + fragment.appendChild( ret[i] ); + } + } + } + + return ret; + }; +jQuery.cleanData = function( elems ) { + + var data, id, cache = jQuery.cache, internalKey = jQuery.expando, special = jQuery.event.special, + deleteExpando = jQuery.support.deleteExpando; + + for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { + if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) { + continue; + } + + id = elem[ jQuery.expando ]; + + if ( id ) { + data = cache[ id ] && cache[ id ][ internalKey ]; + + if ( data && data.events ) { + for ( var type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + + // Null the DOM reference to avoid IE6/7/8 leak (#7054) + if ( data.handle ) { + data.handle.elem = null; + } + } + + if ( deleteExpando ) { + delete elem[ jQuery.expando ]; + + } else if ( elem.removeAttribute ) { + elem.removeAttribute( jQuery.expando ); + } + + delete cache[ id ]; + } + } + }; +jQuery.clone = function( elem, dataAndEvents, deepDataAndEvents ) { + + var clone = elem.cloneNode(true), + srcElements, + destElements, + i; + + if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + // IE copies events bound via attachEvent when using cloneNode. + // Calling detachEvent on the clone will also remove the events + // from the original. In order to get around this, we use some + // proprietary methods to clear the events. Thanks to MooTools + // guys for this hotness. + + cloneFixAttributes( elem, clone ); + + // Using Sizzle here is crazy slow, so we use getElementsByTagName + // instead + srcElements = getAll( elem ); + destElements = getAll( clone ); + + // Weird iteration because IE will replace the length property + // with an element if you are cloning the body and one of the + // elements on the page has a name or id of "length" + for ( i = 0; srcElements[i]; ++i ) { + cloneFixAttributes( srcElements[i], destElements[i] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + cloneCopyEvent( elem, clone ); + + if ( deepDataAndEvents ) { + srcElements = getAll( elem ); + destElements = getAll( clone ); + + for ( i = 0; srcElements[i]; ++i ) { + cloneCopyEvent( srcElements[i], destElements[i] ); + } + } + } + + srcElements = destElements = null; + + // Return the cloned set + return clone; + }; +jQuery.contains = function( a, b ) { +/// +/// Check to see if a DOM node is within another DOM node. +/// +/// +/// The DOM element that may contain the other element. +/// +/// +/// The DOM node that may be contained by the other element. +/// +/// + + return a !== b && (a.contains ? a.contains(b) : true); + }; +jQuery.css = function( elem, name, extra ) { + + var ret, hooks; + + // Make sure that we're working with the right name + name = jQuery.camelCase( name ); + hooks = jQuery.cssHooks[ name ]; + name = jQuery.cssProps[ name ] || name; + + // cssFloat needs a special treatment + if ( name === "cssFloat" ) { + name = "float"; + } + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) { + return ret; + + // Otherwise, if a way to get the computed value exists, use that + } else if ( curCSS ) { + return curCSS( elem, name ); + } + }; +jQuery.cssHooks = { "opacity": {}, +"height": {}, +"width": {} }; +jQuery.cssNumber = { "fillOpacity": true, +"fontWeight": true, +"lineHeight": true, +"opacity": true, +"orphans": true, +"widows": true, +"zIndex": true, +"zoom": true }; +jQuery.cssProps = { "float": 'cssFloat' }; +jQuery.curCSS = function( elem, name, extra ) { + + var ret, hooks; + + // Make sure that we're working with the right name + name = jQuery.camelCase( name ); + hooks = jQuery.cssHooks[ name ]; + name = jQuery.cssProps[ name ] || name; + + // cssFloat needs a special treatment + if ( name === "cssFloat" ) { + name = "float"; + } + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) { + return ret; + + // Otherwise, if a way to get the computed value exists, use that + } else if ( curCSS ) { + return curCSS( elem, name ); + } + }; +jQuery.data = function( elem, name, data, pvt /* Internal Use Only */ ) { +/// +/// 1: Store arbitrary data associated with the specified element. Returns the value that was set. +/// 1.1 - jQuery.data(element, key, value) +/// 2: Returns value at named data store for the element, as set by jQuery.data(element, name, value), or the full data store for the element. +/// 2.1 - jQuery.data(element, key) +/// 2.2 - jQuery.data(element) +/// +/// +/// The DOM element to associate with the data. +/// +/// +/// A string naming the piece of data to set. +/// +/// +/// The new data value. +/// +/// + + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache, + + // 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 ][ 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; + } + + return getByName ? + // Check for both converted-to-camel and non-converted data property names + thisCache[ jQuery.camelCase( name ) ] || thisCache[ name ] : + thisCache; + }; +jQuery.dequeue = function( elem, type ) { +/// +/// Execute the next function on the queue for the matched element. +/// +/// +/// A DOM element from which to remove and execute a queued function. +/// +/// +/// A string containing the name of the queue. Defaults to fx, the standard effects queue. +/// +/// + + 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.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; + }; +jQuery.each = function( object, callback, args ) { +/// +/// A generic iterator function, which can be used to seamlessly iterate over both objects and arrays. Arrays and array-like objects with a length property (such as a function's arguments object) are iterated by numeric index, from 0 to length-1. Other objects are iterated via their named properties. +/// +/// +/// The object or array to iterate over. +/// +/// +/// The function that will be executed on every object. +/// +/// + + 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; + }; +jQuery.easing = {}; +jQuery.error = function( msg ) { +/// +/// Takes a string and throws an exception containing it. +/// +/// +/// The message to send out. +/// + + throw msg; + }; +jQuery.etag = {}; +jQuery.event = { "global": {}, +"customEvent": {}, +"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'], +"guid": 100000000, +"special": {}, +"triggered": }; +jQuery.expr = { "order": ['ID','CLASS','NAME','TAG'], +"match": {}, +"leftMatch": {}, +"attrMap": {}, +"attrHandle": {}, +"relative": {}, +"find": {}, +"preFilter": {}, +"filters": {}, +"setFilters": {}, +"filter": {}, +":": {} }; +jQuery.extend = function() { +/// +/// Merge the contents of two or more objects together into the first object. +/// 1 - jQuery.extend(target, object1, objectN) +/// 2 - jQuery.extend(deep, target, object1, objectN) +/// +/// +/// If true, the merge becomes recursive (aka. deep copy). +/// +/// +/// The object to extend. It will receive the new properties. +/// +/// +/// An object containing additional properties to merge in. +/// +/// +/// Additional objects containing properties to merge in. +/// +/// + + 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.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); + }; +jQuery.find = 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); + }; +jQuery.fn = { "selector": '', +"jquery": '1.6.2', +"length": 0 }; +jQuery.fragments = {}; +jQuery.fx = function( elem, options, prop ) { + + this.options = options; + this.elem = elem; + this.prop = prop; + + options.orig = options.orig || {}; + }; +jQuery.get = function( url, data, callback, type ) { +/// +/// Load data from the server using a HTTP GET request. +/// +/// +/// A string containing the URL to which the request is sent. +/// +/// +/// A map or string that is sent to the server with the request. +/// +/// +/// A callback function that is executed if the request succeeds. +/// +/// +/// The type of data expected from the server. Default: Intelligent Guess (xml, json, script, or html). +/// + + // shift arguments if data argument was omitted + if ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + return jQuery.ajax({ + type: method, + url: url, + data: data, + success: callback, + dataType: type + }); + }; +jQuery.getJSON = function( url, data, callback ) { +/// +/// Load JSON-encoded data from the server using a GET HTTP request. +/// +/// +/// A string containing the URL to which the request is sent. +/// +/// +/// A map or string that is sent to the server with the request. +/// +/// +/// A callback function that is executed if the request succeeds. +/// + + return jQuery.get( url, data, callback, "json" ); + }; +jQuery.getScript = function( url, callback ) { +/// +/// Load a JavaScript file from the server using a GET HTTP request, then execute it. +/// +/// +/// A string containing the URL to which the request is sent. +/// +/// +/// A callback function that is executed if the request succeeds. +/// +/// + + return jQuery.get( url, undefined, callback, "script" ); + }; +jQuery.globalEval = function( data ) { +/// +/// Execute some JavaScript code globally. +/// +/// +/// The JavaScript code to execute. +/// + + 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 ); + } + }; +jQuery.grep = function( elems, callback, inv ) { +/// +/// Finds the elements of an array which satisfy a filter function. The original array is not affected. +/// +/// +/// The array to search through. +/// +/// +/// The function to process each item against. The first argument to the function is the item, and the second argument is the index. The function should return a Boolean value. this will be the global window object. +/// +/// +/// If "invert" is false, or not provided, then the function returns an array consisting of all elements for which "callback" returns true. If "invert" is true, then the function returns an array consisting of all elements for which "callback" returns false. +/// +/// + + 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; + }; +jQuery.guid = 1; +jQuery.hasData = function( elem ) { +/// +/// Determine whether an element has any jQuery data associated with it. +/// +/// +/// A DOM element to be checked for data. +/// +/// + + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + + return !!elem && !isEmptyDataObject( elem ); + }; +jQuery.holdReady = function( hold ) { +/// +/// Holds or releases the execution of jQuery's ready event. +/// +/// +/// Indicates whether the ready hold is being requested or released +/// +/// + + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }; +jQuery.inArray = function( elem, array ) { +/// +/// Search for a specified value within an array and return its index (or -1 if not found). +/// +/// +/// The value to search for. +/// +/// +/// An array through which to search. +/// +/// + + + 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; + }; +jQuery.isEmptyObject = function( obj ) { +/// +/// Check to see if an object is empty (contains no properties). +/// +/// +/// The object that will be checked to see if it's empty. +/// +/// + + for ( var name in obj ) { + return false; + } + return true; + }; +jQuery.isFunction = function( obj ) { +/// +/// Determine if the argument passed is a Javascript function object. +/// +/// +/// Object to test whether or not it is a function. +/// +/// + + return jQuery.type(obj) === "function"; + }; +jQuery.isNaN = function( obj ) { + + return obj == null || !rdigit.test( obj ) || isNaN( obj ); + }; +jQuery.isPlainObject = function( obj ) { +/// +/// Check to see if an object is a plain object (created using "{}" or "new Object"). +/// +/// +/// The object that will be checked to see if it's a plain object. +/// +/// + + // 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; + } + + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + 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 ); + }; +jQuery.isReady = true; +jQuery.isWindow = function( obj ) { +/// +/// Determine whether the argument is a window. +/// +/// +/// Object to test whether or not it is a window. +/// +/// + + return obj && typeof obj === "object" && "setInterval" in obj; + }; +jQuery.isXMLDoc = function( elem ) { +/// +/// Check to see if a DOM node is within an XML document (or is an XML document). +/// +/// +/// The DOM node that will be checked to see if it's in an XML document. +/// +/// + + // 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; +}; +jQuery.lastModified = {}; +jQuery.makeArray = function( array, results ) { +/// +/// Convert an array-like object into a true JavaScript array. +/// +/// +/// Any object to turn into a native Array. +/// +/// + + 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; + }; +jQuery.map = function( elems, callback, arg ) { +/// +/// Translate all items in an array or object to new array of items. +/// 1 - jQuery.map(array, callback(elementOfArray, indexInArray)) +/// 2 - jQuery.map(arrayOrObject, callback( value, indexOrKey )) +/// +/// +/// The Array to translate. +/// +/// +/// The function to process each item against. The first argument to the function is the array item, the second argument is the index in array The function can return any value. Within the function, this refers to the global (window) object. +/// +/// + + 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 ); + }; +jQuery.merge = function( first, second ) { +/// +/// Merge the contents of two arrays together into the first array. +/// +/// +/// The first array to merge, the elements of second added. +/// +/// +/// The second array to merge into the first, unaltered. +/// +/// + + 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; + }; +jQuery.noConflict = function( deep ) { +/// +/// Relinquish jQuery's control of the $ variable. +/// +/// +/// A Boolean indicating whether to remove all jQuery variables from the global scope (including jQuery itself). +/// +/// + + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }; +jQuery.noData = { "embed": true, +"object": 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000', +"applet": true }; +jQuery.nodeName = function( elem, name ) { + + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }; +jQuery.noop = function() { +/// +/// An empty function. +/// +/// +}; +jQuery.now = function() { +/// +/// Return a number representing the current time. +/// +/// + + return (new Date()).getTime(); + }; +jQuery.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; + }; +jQuery.offset = {}; +jQuery.param = function( a, traditional ) { +/// +/// Create a serialized representation of an array or object, suitable for use in a URL query string or Ajax request. +/// 1 - jQuery.param(obj) +/// 2 - jQuery.param(obj, traditional) +/// +/// +/// An array or object to serialize. +/// +/// +/// A Boolean indicating whether to perform a traditional "shallow" serialization. +/// +/// + + var s = [], + add = function( key, value ) { + // If value is a function, invoke it and return its value + value = jQuery.isFunction( value ) ? value() : value; + s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value ); + }; + + // Set traditional to true for jQuery <= 1.3.2 behavior. + if ( traditional === undefined ) { + traditional = jQuery.ajaxSettings.traditional; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + }); + + } else { + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( var prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ).replace( r20, "+" ); + }; +jQuery.parseJSON = function( data ) { +/// +/// Takes a well-formed JSON string and returns the resulting JavaScript object. +/// +/// +/// The JSON string to parse. +/// +/// + + 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 ); + }; +jQuery.parseXML = function( data , xml , tmp ) { +/// +/// Parses a string into an XML document. +/// +/// +/// a well-formed XML string to be parsed +/// +/// + + + 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 ); + } + + tmp = xml.documentElement; + + if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) { + jQuery.error( "Invalid XML: " + data ); + } + + return xml; + }; +jQuery.post = function( url, data, callback, type ) { +/// +/// Load data from the server using a HTTP POST request. +/// +/// +/// A string containing the URL to which the request is sent. +/// +/// +/// A map or string that is sent to the server with the request. +/// +/// +/// A callback function that is executed if the request succeeds. +/// +/// +/// The type of data expected from the server. Default: Intelligent Guess (xml, json, script, or html). +/// + + // shift arguments if data argument was omitted + if ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + return jQuery.ajax({ + type: method, + url: url, + data: data, + success: callback, + dataType: type + }); + }; +jQuery.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 )) !== undefined ) { + return ret; + + } else { + return elem[ name ]; + } + } + }; +jQuery.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' }; +jQuery.propHooks = { "selected": {} }; +jQuery.proxy = function( fn, context ) { +/// +/// Takes a function and returns a new one that will always have a particular context. +/// 1 - jQuery.proxy(function, context) +/// 2 - jQuery.proxy(context, name) +/// +/// +/// The function whose context will be changed. +/// +/// +/// The object to which the context (this) of the function should be set. +/// +/// + + 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; + }; +jQuery.queue = function( elem, type, data ) { +/// +/// 1: Show the queue of functions to be executed on the matched element. +/// 1.1 - jQuery.queue(element, queueName) +/// 2: Manipulate the queue of functions to be executed on the matched element. +/// 2.1 - jQuery.queue(element, queueName, newQueue) +/// 2.2 - jQuery.queue(element, queueName, callback()) +/// +/// +/// A DOM element where the array of queued functions is attached. +/// +/// +/// A string containing the name of the queue. Defaults to fx, the standard effects queue. +/// +/// +/// An array of functions to replace the current queue contents. +/// +/// + + 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 || []; + } + }; +jQuery.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" ); + } + } + }; +jQuery.readyWait = 0; +jQuery.removeAttr = function( elem, name ) { + + var propName; + if ( elem.nodeType === 1 ) { + name = jQuery.attrFix[ name ] || name; + + if ( jQuery.support.getSetAttribute ) { + // Use removeAttribute in browsers that support it + elem.removeAttribute( name ); + } else { + jQuery.attr( elem, name, "" ); + elem.removeAttributeNode( elem.getAttributeNode( name ) ); + } + + // Set corresponding property to false for boolean attributes + if ( rboolean.test( name ) && (propName = jQuery.propFix[ name ] || name) in elem ) { + elem[ propName ] = false; + } + } + }; +jQuery.removeData = function( elem, name, pvt /* Internal Use Only */ ) { +/// +/// Remove a previously-stored piece of data. +/// +/// +/// A DOM element from which to remove data. +/// +/// +/// A string naming the piece of data to remove. +/// +/// + + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var 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 ) { + var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ]; + + if ( thisCache ) { + 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 + if ( jQuery.support.deleteExpando || cache != window ) { + 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; + } + } + }; +jQuery.removeEvent = function( elem, type, handle ) { + + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + }; +jQuery.sibling = function( n, elem ) { + + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + }; +jQuery.speed = function( speed, easing, fn ) { + + var opt = speed && typeof speed === "object" ? jQuery.extend({}, speed) : { + complete: fn || !fn && easing || + jQuery.isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !jQuery.isFunction(easing) && easing + }; + + opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : + opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[opt.duration] : jQuery.fx.speeds._default; + + // Queueing + opt.old = opt.complete; + opt.complete = function( noUnmark ) { + if ( jQuery.isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue !== false ) { + jQuery.dequeue( this ); + } else if ( noUnmark !== false ) { + jQuery._unmark( this ); + } + }; + + return opt; + }; +jQuery.style = function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, origName = jQuery.camelCase( name ), + style = elem.style, hooks = jQuery.cssHooks[ origName ]; + + name = jQuery.cssProps[ origName ] || origName; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Make sure that NaN and null values aren't set. See: #7116 + if ( type === "number" && isNaN( value ) || value == null ) { + return; + } + + // convert relative number strings (+= or -=) to relative numbers. #7345 + if ( type === "string" && rrelNum.test( value ) ) { + value = +value.replace( rrelNumFilter, "" ) + parseFloat( jQuery.css( elem, name ) ); + // Fixes bug #9237 + type = "number"; + } + + // If a number was passed in, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) { + // Wrapped to prevent IE from throwing errors when 'invalid' values are provided + // Fixes bug #5509 + try { + style[ name ] = value; + } catch(e) {} + } + + } else { + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }; +jQuery.sub = function() { +/// +/// Creates a new copy of jQuery whose properties and methods can be modified without affecting the original jQuery object. +/// +/// + + 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; + }; +jQuery.support = { "leadingWhitespace": true, +"tbody": true, +"htmlSerialize": true, +"style": true, +"hrefNormalized": true, +"opacity": true, +"cssFloat": true, +"checkOn": true, +"optSelected": false, +"getSetAttribute": true, +"submitBubbles": true, +"changeBubbles": true, +"focusinBubbles": true, +"deleteExpando": true, +"noCloneEvent": true, +"inlineBlockNeedsLayout": false, +"shrinkWrapBlocks": false, +"reliableMarginRight": true, +"noCloneChecked": false, +"optDisabled": true, +"radioValue": false, +"checkClone": , +"appendChecked": true, +"boxModel": true, +"reliableHiddenOffsets": true, +"ajax": true, +"cors": false }; +jQuery.swap = function( elem, options, callback ) { + + var old = {}; + + // Remember the old values, and insert the new ones + for ( var name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + }; +jQuery.text = 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; +}; +jQuery.trim = function( text ) { +/// +/// Remove the whitespace from the beginning and end of a string. +/// +/// +/// The string to trim. +/// +/// + + return text == null ? + "" : + trim.call( text ); + }; +jQuery.type = function( obj ) { +/// +/// Determine the internal JavaScript [[Class]] of an object. +/// +/// +/// Object to get the internal JavaScript [[Class]] of. +/// +/// + + return obj == null ? + String( obj ) : + class2type[ toString.call(obj) ] || "object"; + }; +jQuery.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" }; + }; +jQuery.unique = function( results ) { +/// +/// Sorts an array of DOM elements, in place, with the duplicates removed. Note that this only works on arrays of DOM elements, not strings or numbers. +/// +/// +/// The Array of DOM elements. +/// +/// + + 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; +}; +jQuery.uuid = 0; +jQuery.valHooks = { "option": {}, +"select": {}, +"radio": {}, +"checkbox": {} }; +jQuery.when = function( firstParam ) { +/// +/// Provides a way to execute callback functions based on one or more objects, usually Deferred objects that represent asynchronous events. +/// +/// +/// One or more Deferred objects, or plain JavaScript objects. +/// +/// + + 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.Event.prototype.isDefaultPrevented = function returnFalse() { +/// +/// Returns whether event.preventDefault() was ever called on this event object. +/// +/// + + return false; +}; +jQuery.Event.prototype.isImmediatePropagationStopped = function returnFalse() { +/// +/// Returns whether event.stopImmediatePropagation() was ever called on this event object. +/// +/// + + return false; +}; +jQuery.Event.prototype.isPropagationStopped = function returnFalse() { +/// +/// Returns whether event.stopPropagation() was ever called on this event object. +/// +/// + + return false; +}; +jQuery.Event.prototype.preventDefault = function() { +/// +/// If this method is called, the default action of the event will not be triggered. +/// +/// + + 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; + } + }; +jQuery.Event.prototype.stopImmediatePropagation = function() { +/// +/// Keeps the rest of the handlers from being executed and prevents the event from bubbling up the DOM tree. +/// + + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }; +jQuery.Event.prototype.stopPropagation = function() { +/// +/// Prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event. +/// + + 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; + }; +jQuery.prototype._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 ); + }; +jQuery.prototype.add = function( selector, context ) { +/// +/// Add elements to the set of matched elements. +/// 1 - add(selector) +/// 2 - add(elements) +/// 3 - add(html) +/// 4 - add(jQuery object) +/// 5 - add(selector, context) +/// +/// +/// A string representing a selector expression to find additional elements to add to the set of matched elements. +/// +/// +/// The point in the document at which the selector should begin matching; similar to the context argument of the $(selector, context) method. +/// +/// + + 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 ) ); + }; +jQuery.prototype.addClass = function( value ) { +/// +/// Adds the specified class(es) to each of the set of matched elements. +/// 1 - addClass(className) +/// 2 - addClass(function(index, currentClass)) +/// +/// +/// One or more class names to be added to the class attribute of each matched element. +/// +/// + + 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; + }; +jQuery.prototype.after = function() { +/// +/// Insert content, specified by the parameter, after each element in the set of matched elements. +/// 1 - after(content, content) +/// 2 - after(function(index)) +/// +/// +/// HTML string, DOM element, or jQuery object to insert after each element in the set of matched elements. +/// +/// +/// One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert after each element in the set of matched elements. +/// +/// + + if ( this[0] && this[0].parentNode ) { + return this.domManip(arguments, false, function( elem ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + }); + } else if ( arguments.length ) { + var set = this.pushStack( this, "after", arguments ); + set.push.apply( set, jQuery(arguments[0]).toArray() ); + return set; + } + }; +jQuery.prototype.ajaxComplete = function( f ){ +/// +/// Register a handler to be called when Ajax requests complete. This is an Ajax Event. +/// +/// +/// The function to be invoked. +/// +/// + + return this.bind( o, f ); + }; +jQuery.prototype.ajaxError = function( f ){ +/// +/// Register a handler to be called when Ajax requests complete with an error. This is an Ajax Event. +/// +/// +/// The function to be invoked. +/// +/// + + return this.bind( o, f ); + }; +jQuery.prototype.ajaxSend = function( f ){ +/// +/// Attach a function to be executed before an Ajax request is sent. This is an Ajax Event. +/// +/// +/// The function to be invoked. +/// +/// + + return this.bind( o, f ); + }; +jQuery.prototype.ajaxStart = function( f ){ +/// +/// Register a handler to be called when the first Ajax request begins. This is an Ajax Event. +/// +/// +/// The function to be invoked. +/// +/// + + return this.bind( o, f ); + }; +jQuery.prototype.ajaxStop = function( f ){ +/// +/// Register a handler to be called when all Ajax requests have completed. This is an Ajax Event. +/// +/// +/// The function to be invoked. +/// +/// + + return this.bind( o, f ); + }; +jQuery.prototype.ajaxSuccess = function( f ){ +/// +/// Attach a function to be executed whenever an Ajax request completes successfully. This is an Ajax Event. +/// +/// +/// The function to be invoked. +/// +/// + + return this.bind( o, f ); + }; +jQuery.prototype.andSelf = function() { +/// +/// Add the previous set of elements on the stack to the current set. +/// +/// + + return this.add( this.prevObject ); + }; +jQuery.prototype.animate = function( prop, speed, easing, callback ) { +/// +/// Perform a custom animation of a set of CSS properties. +/// 1 - animate(properties, duration, easing, complete) +/// 2 - animate(properties, options) +/// +/// +/// A map of CSS properties that the animation will move toward. +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + var optall = jQuery.speed(speed, easing, callback); + + if ( jQuery.isEmptyObject( prop ) ) { + return this.each( optall.complete, [ false ] ); + } + + // Do not change referenced properties as per-property easing will be lost + prop = jQuery.extend( {}, prop ); + + return this[ optall.queue === false ? "each" : "queue" ](function() { + // XXX 'this' does not always have a nodeName when running the + // test suite + + if ( optall.queue === false ) { + jQuery._mark( this ); + } + + var opt = jQuery.extend( {}, optall ), + isElement = this.nodeType === 1, + hidden = isElement && jQuery(this).is(":hidden"), + name, val, p, + display, e, + parts, start, end, unit; + + // will store per property easing and be used to determine when an animation is complete + opt.animatedProperties = {}; + + for ( p in prop ) { + + // property name normalization + name = jQuery.camelCase( p ); + if ( p !== name ) { + prop[ name ] = prop[ p ]; + delete prop[ p ]; + } + + val = prop[ name ]; + + // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default) + if ( jQuery.isArray( val ) ) { + opt.animatedProperties[ name ] = val[ 1 ]; + val = prop[ name ] = val[ 0 ]; + } else { + opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing'; + } + + if ( val === "hide" && hidden || val === "show" && !hidden ) { + return opt.complete.call( this ); + } + + if ( isElement && ( name === "height" || name === "width" ) ) { + // Make sure that nothing sneaks out + // Record all 3 overflow attributes because IE does not + // change the overflow attribute when overflowX and + // overflowY are set to the same value + opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ]; + + // Set display property to inline-block for height/width + // animations on inline elements that are having width/height + // animated + if ( jQuery.css( this, "display" ) === "inline" && + jQuery.css( this, "float" ) === "none" ) { + if ( !jQuery.support.inlineBlockNeedsLayout ) { + this.style.display = "inline-block"; + + } else { + display = defaultDisplay( this.nodeName ); + + // inline-level elements accept inline-block; + // block-level elements need to be inline with layout + if ( display === "inline" ) { + this.style.display = "inline-block"; + + } else { + this.style.display = "inline"; + this.style.zoom = 1; + } + } + } + } + } + + if ( opt.overflow != null ) { + this.style.overflow = "hidden"; + } + + for ( p in prop ) { + e = new jQuery.fx( this, opt, p ); + val = prop[ p ]; + + if ( rfxtypes.test(val) ) { + e[ val === "toggle" ? hidden ? "show" : "hide" : val ](); + + } else { + parts = rfxnum.exec( val ); + start = e.cur(); + + if ( parts ) { + end = parseFloat( parts[2] ); + unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" ); + + // We need to compute starting value + if ( unit !== "px" ) { + jQuery.style( this, p, (end || 1) + unit); + start = ((end || 1) / e.cur()) * start; + jQuery.style( this, p, start + unit); + } + + // If a +=/-= token was provided, we're doing a relative animation + if ( parts[1] ) { + end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start; + } + + e.custom( start, end, unit ); + + } else { + e.custom( start, val, "" ); + } + } + } + + // For JS strict compliance + return true; + }); + }; +jQuery.prototype.append = function() { +/// +/// Insert content, specified by the parameter, to the end of each element in the set of matched elements. +/// 1 - append(content, content) +/// 2 - append(function(index, html)) +/// +/// +/// DOM element, HTML string, or jQuery object to insert at the end of each element in the set of matched elements. +/// +/// +/// One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert at the end of each element in the set of matched elements. +/// +/// + + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 ) { + this.appendChild( elem ); + } + }); + }; +jQuery.prototype.appendTo = function( selector ) { +/// +/// Insert every element in the set of matched elements to the end of the target. +/// +/// +/// A selector, element, HTML string, or jQuery object; the matched set of elements will be inserted at the end of the element(s) specified by this parameter. +/// +/// + + var ret = [], + insert = jQuery( selector ), + parent = this.length === 1 && this[0].parentNode; + + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + return this; + + } else { + for ( var i = 0, l = insert.length; i < l; i++ ) { + var elems = (i > 0 ? this.clone(true) : this).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +jQuery.prototype.attr = function( name, value ) { +/// +/// 1: Get the value of an attribute for the first element in the set of matched elements. +/// 1.1 - attr(attributeName) +/// 2: Set one or more attributes for the set of matched elements. +/// 2.1 - attr(attributeName, value) +/// 2.2 - attr(map) +/// 2.3 - attr(attributeName, function(index, attr)) +/// +/// +/// The name of the attribute to set. +/// +/// +/// A value to set for the attribute. +/// +/// + + return jQuery.access( this, name, value, true, jQuery.attr ); + }; +jQuery.prototype.before = function() { +/// +/// Insert content, specified by the parameter, before each element in the set of matched elements. +/// 1 - before(content, content) +/// 2 - before(function) +/// +/// +/// HTML string, DOM element, or jQuery object to insert before each element in the set of matched elements. +/// +/// +/// One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert before each element in the set of matched elements. +/// +/// + + if ( this[0] && this[0].parentNode ) { + return this.domManip(arguments, false, function( elem ) { + this.parentNode.insertBefore( elem, this ); + }); + } else if ( arguments.length ) { + var set = jQuery(arguments[0]); + set.push.apply( set, this.toArray() ); + return this.pushStack( set, "before", arguments ); + } + }; +jQuery.prototype.bind = function( type, data, fn ) { +/// +/// Attach a handler to an event for the elements. +/// 1 - bind(eventType, eventData, handler(eventObject)) +/// 2 - bind(eventType, eventData, false) +/// 3 - bind(events) +/// +/// +/// A string containing one or more JavaScript event types, such as "click" or "submit," or custom event names. +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + 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.prototype.blur = function( data, fn ) { +/// +/// Bind an event handler to the "blur" JavaScript event, or trigger that event on an element. +/// 1 - blur(handler(eventObject)) +/// 2 - blur(eventData, handler(eventObject)) +/// 3 - blur() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.change = function( data, fn ) { +/// +/// Bind an event handler to the "change" JavaScript event, or trigger that event on an element. +/// 1 - change(handler(eventObject)) +/// 2 - change(eventData, handler(eventObject)) +/// 3 - change() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.children = function( until, selector ) { +/// +/// Get the children of each element in the set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.clearQueue = function( type ) { +/// +/// Remove from the queue all items that have not yet been run. +/// +/// +/// A string containing the name of the queue. Defaults to fx, the standard effects queue. +/// +/// + + return this.queue( type || "fx", [] ); + }; +jQuery.prototype.click = function( data, fn ) { +/// +/// Bind an event handler to the "click" JavaScript event, or trigger that event on an element. +/// 1 - click(handler(eventObject)) +/// 2 - click(eventData, handler(eventObject)) +/// 3 - click() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.clone = function( dataAndEvents, deepDataAndEvents ) { +/// +/// Create a deep copy of the set of matched elements. +/// 1 - clone(withDataAndEvents) +/// 2 - clone(withDataAndEvents, deepWithDataAndEvents) +/// +/// +/// A Boolean indicating whether event handlers and data should be copied along with the elements. The default value is false. *For 1.5.0 the default value is incorrectly true. This will be changed back to false in 1.5.1 and up. +/// +/// +/// A Boolean indicating whether event handlers and data for all children of the cloned element should be copied. By default its value matches the first argument's value (which defaults to false). +/// +/// + + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function () { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }; +jQuery.prototype.closest = function( selectors, context ) { +/// +/// 1: Get the first ancestor element that matches the selector, beginning at the current element and progressing up through the DOM tree. +/// 1.1 - closest(selector) +/// 1.2 - closest(selector, context) +/// 1.3 - closest(jQuery object) +/// 1.4 - closest(element) +/// 2: Gets an array of all the elements and selectors matched against the current element up through the DOM tree. +/// 2.1 - closest(selectors, context) +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// +/// A DOM element within which a matching element may be found. If no context is passed in then the context of the jQuery set will be used instead. +/// +/// + + 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 ); + }; +jQuery.prototype.constructor = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }; +jQuery.prototype.contents = function( until, selector ) { +/// +/// Get the children of each element in the set of matched elements, including text and comment nodes. +/// +/// + + 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.prototype.css = function( name, value ) { +/// +/// 1: Get the value of a style property for the first element in the set of matched elements. +/// 1.1 - css(propertyName) +/// 2: Set one or more CSS properties for the set of matched elements. +/// 2.1 - css(propertyName, value) +/// 2.2 - css(propertyName, function(index, value)) +/// 2.3 - css(map) +/// +/// +/// A CSS property name. +/// +/// +/// A value to set for the property. +/// +/// + + // Setting 'undefined' is a no-op + if ( arguments.length === 2 && value === undefined ) { + return this; + } + + return jQuery.access( this, name, value, true, function( elem, name, value ) { + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }); +}; +jQuery.prototype.data = function( key, value ) { +/// +/// 1: Store arbitrary data associated with the matched elements. +/// 1.1 - data(key, value) +/// 1.2 - data(obj) +/// 2: Returns value at named data store for the first element in the jQuery collection, as set by data(name, value). +/// 2.1 - data(key) +/// 2.2 - data() +/// +/// +/// A string naming the piece of data to set. +/// +/// +/// The new data value; it can be any Javascript type including Array or Object. +/// +/// + + 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 ); + }); + } + }; +jQuery.prototype.dblclick = function( data, fn ) { +/// +/// Bind an event handler to the "dblclick" JavaScript event, or trigger that event on an element. +/// 1 - dblclick(handler(eventObject)) +/// 2 - dblclick(eventData, handler(eventObject)) +/// 3 - dblclick() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.delay = function( time, type ) { +/// +/// Set a timer to delay execution of subsequent items in the queue. +/// +/// +/// An integer indicating the number of milliseconds to delay execution of the next item in the queue. +/// +/// +/// A string containing the name of the queue. Defaults to fx, the standard effects queue. +/// +/// + + 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 ); + }); + }; +jQuery.prototype.delegate = function( selector, types, data, fn ) { +/// +/// Attach a handler to one or more events for all elements that match the selector, now or in the future, based on a specific set of root elements. +/// 1 - delegate(selector, eventType, handler) +/// 2 - delegate(selector, eventType, eventData, handler) +/// 3 - delegate(selector, events) +/// +/// +/// A selector to filter the elements that trigger the event. +/// +/// +/// A string containing one or more space-separated JavaScript event types, such as "click" or "keydown," or custom event names. +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute at the time the event is triggered. +/// +/// + + return this.live( types, data, fn, selector ); + }; +jQuery.prototype.dequeue = function( type ) { +/// +/// Execute the next function on the queue for the matched elements. +/// +/// +/// A string containing the name of the queue. Defaults to fx, the standard effects queue. +/// +/// + + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }; +jQuery.prototype.detach = function( selector ) { +/// +/// Remove the set of matched elements from the DOM. +/// +/// +/// A selector expression that filters the set of matched elements to be removed. +/// +/// + + return this.remove( selector, true ); + }; +jQuery.prototype.die = function( types, data, fn, origSelector /* Internal Use Only */ ) { +/// +/// 1: Remove all event handlers previously attached using .live() from the elements. +/// 1.1 - die() +/// 2: Remove an event handler previously attached using .live() from the elements. +/// 2.1 - die(eventType, handler) +/// 2.2 - die(eventTypes) +/// +/// +/// A string containing a JavaScript event type, such as click or keydown. +/// +/// +/// The function that is no longer to be executed. +/// +/// + + 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; + }; +jQuery.prototype.domManip = function( args, table, callback ) { + + var results, first, fragment, parent, + value = args[0], + scripts = []; + + // We can't cloneNode fragments that contain checked, in WebKit + if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) { + return this.each(function() { + jQuery(this).domManip( args, table, callback, true ); + }); + } + + if ( jQuery.isFunction(value) ) { + return this.each(function(i) { + var self = jQuery(this); + args[0] = value.call(this, i, table ? self.html() : undefined); + self.domManip( args, table, callback ); + }); + } + + if ( this[0] ) { + parent = value && value.parentNode; + + // If we're in a fragment, just use that instead of building a new one + if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) { + results = { fragment: parent }; + + } else { + results = jQuery.buildFragment( args, this, scripts ); + } + + fragment = results.fragment; + + if ( fragment.childNodes.length === 1 ) { + first = fragment = fragment.firstChild; + } else { + first = fragment.firstChild; + } + + if ( first ) { + table = table && jQuery.nodeName( first, "tr" ); + + for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) { + callback.call( + table ? + root(this[i], first) : + this[i], + // Make sure that we do not leak memory by inadvertently discarding + // the original fragment (which might have attached data) instead of + // using it; in addition, use the original fragment object for the last + // item instead of first because it can end up being emptied incorrectly + // in certain situations (Bug #8070). + // Fragments from the fragment cache must always be cloned and never used + // in place. + results.cacheable || (l > 1 && i < lastIndex) ? + jQuery.clone( fragment, true, true ) : + fragment + ); + } + } + + if ( scripts.length ) { + jQuery.each( scripts, evalScript ); + } + } + + return this; + }; +jQuery.prototype.each = function( callback, args ) { +/// +/// Iterate over a jQuery object, executing a function for each matched element. +/// +/// +/// A function to execute for each matched element. +/// +/// + + return jQuery.each( this, callback, args ); + }; +jQuery.prototype.empty = function() { +/// +/// Remove all child nodes of the set of matched elements from the DOM. +/// +/// + + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName("*") ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + } + + return this; + }; +jQuery.prototype.end = function() { +/// +/// End the most recent filtering operation in the current chain and return the set of matched elements to its previous state. +/// +/// + + return this.prevObject || this.constructor(null); + }; +jQuery.prototype.eq = function( i ) { +/// +/// Reduce the set of matched elements to the one at the specified index. +/// 1 - eq(index) +/// 2 - eq(-index) +/// +/// +/// An integer indicating the 0-based position of the element. +/// +/// + + return i === -1 ? + this.slice( i ) : + this.slice( i, +i + 1 ); + }; +jQuery.prototype.error = function( data, fn ) { +/// +/// Bind an event handler to the "error" JavaScript event. +/// 1 - error(handler(eventObject)) +/// 2 - error(eventData, handler(eventObject)) +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.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.prototype.fadeIn = function( speed, easing, callback ) { +/// +/// Display the matched elements by fading them to opaque. +/// 1 - fadeIn(duration, callback) +/// 2 - fadeIn(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.animate( props, speed, easing, callback ); + }; +jQuery.prototype.fadeOut = function( speed, easing, callback ) { +/// +/// Hide the matched elements by fading them to transparent. +/// 1 - fadeOut(duration, callback) +/// 2 - fadeOut(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.animate( props, speed, easing, callback ); + }; +jQuery.prototype.fadeTo = function( speed, to, easing, callback ) { +/// +/// Adjust the opacity of the matched elements. +/// 1 - fadeTo(duration, opacity, callback) +/// 2 - fadeTo(duration, opacity, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A number between 0 and 1 denoting the target opacity. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.filter(":hidden").css("opacity", 0).show().end() + .animate({opacity: to}, speed, easing, callback); + }; +jQuery.prototype.fadeToggle = function( speed, easing, callback ) { +/// +/// Display or hide the matched elements by animating their opacity. +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.animate( props, speed, easing, callback ); + }; +jQuery.prototype.filter = function( selector ) { +/// +/// Reduce the set of matched elements to those that match the selector or pass the function's test. +/// 1 - filter(selector) +/// 2 - filter(function(index)) +/// 3 - filter(element) +/// 4 - filter(jQuery object) +/// +/// +/// A string containing a selector expression to match the current set of elements against. +/// +/// + + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }; +jQuery.prototype.find = function( selector ) { +/// +/// Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element. +/// 1 - find(selector) +/// 2 - find(jQuery object) +/// 3 - find(element) +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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; + }; +jQuery.prototype.first = function() { +/// +/// Reduce the set of matched elements to the first in the set. +/// +/// + + return this.eq( 0 ); + }; +jQuery.prototype.focus = function( data, fn ) { +/// +/// Bind an event handler to the "focus" JavaScript event, or trigger that event on an element. +/// 1 - focus(handler(eventObject)) +/// 2 - focus(eventData, handler(eventObject)) +/// 3 - focus() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.focusin = function( data, fn ) { +/// +/// Bind an event handler to the "focusin" JavaScript event. +/// 1 - focusin(handler(eventObject)) +/// 2 - focusin(eventData, handler(eventObject)) +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.focusout = function( data, fn ) { +/// +/// Bind an event handler to the "focusout" JavaScript event. +/// 1 - focusout(handler(eventObject)) +/// 2 - focusout(eventData, handler(eventObject)) +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.get = function( num ) { +/// +/// Retrieve the DOM elements matched by the jQuery object. +/// +/// +/// A zero-based integer indicating which element to retrieve. +/// +/// + + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }; +jQuery.prototype.has = function( target ) { +/// +/// Reduce the set of matched elements to those that have a descendant that matches the selector or DOM element. +/// 1 - has(selector) +/// 2 - has(contained) +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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; + } + } + }); + }; +jQuery.prototype.hasClass = function( selector ) { +/// +/// Determine whether any of the matched elements are assigned the given class. +/// +/// +/// The class name to search for. +/// +/// + + var className = " " + selector + " "; + for ( var i = 0, l = this.length; i < l; i++ ) { + if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }; +jQuery.prototype.height = function( size ) { +/// +/// 1: Get the current computed height for the first element in the set of matched elements. +/// 1.1 - height() +/// 2: Set the CSS height of every matched element. +/// 2.1 - height(value) +/// 2.2 - height(function(index, height)) +/// +/// +/// An integer representing the number of pixels, or an integer with an optional unit of measure appended (as a string). +/// +/// + + // Get window width or height + var elem = this[0]; + if ( !elem ) { + return size == null ? null : this; + } + + if ( jQuery.isFunction( size ) ) { + return this.each(function( i ) { + var self = jQuery( this ); + self[ type ]( size.call( this, i, self[ type ]() ) ); + }); + } + + if ( jQuery.isWindow( elem ) ) { + // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode + // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat + var docElemProp = elem.document.documentElement[ "client" + name ]; + return elem.document.compatMode === "CSS1Compat" && docElemProp || + elem.document.body[ "client" + name ] || docElemProp; + + // Get document width or height + } else if ( elem.nodeType === 9 ) { + // Either scroll[Width/Height] or offset[Width/Height], whichever is greater + return Math.max( + elem.documentElement["client" + name], + elem.body["scroll" + name], elem.documentElement["scroll" + name], + elem.body["offset" + name], elem.documentElement["offset" + name] + ); + + // Get or set width or height on the element + } else if ( size === undefined ) { + var orig = jQuery.css( elem, type ), + ret = parseFloat( orig ); + + return jQuery.isNaN( ret ) ? orig : ret; + + // Set the width or height on the element (default to pixels if value is unitless) + } else { + return this.css( type, typeof size === "string" ? size : size + "px" ); + } + }; +jQuery.prototype.hide = function( speed, easing, callback ) { +/// +/// Hide the matched elements. +/// 1 - hide() +/// 2 - hide(duration, callback) +/// 3 - hide(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + if ( speed || speed === 0 ) { + return this.animate( genFx("hide", 3), speed, easing, callback); + + } else { + for ( var i = 0, j = this.length; i < j; i++ ) { + if ( this[i].style ) { + var display = jQuery.css( this[i], "display" ); + + if ( display !== "none" && !jQuery._data( this[i], "olddisplay" ) ) { + jQuery._data( this[i], "olddisplay", display ); + } + } + } + + // Set the display of the elements in a second loop + // to avoid the constant reflow + for ( i = 0; i < j; i++ ) { + if ( this[i].style ) { + this[i].style.display = "none"; + } + } + + return this; + } + }; +jQuery.prototype.hover = function( fnOver, fnOut ) { +/// +/// 1: Bind two handlers to the matched elements, to be executed when the mouse pointer enters and leaves the elements. +/// 1.1 - hover(handlerIn(eventObject), handlerOut(eventObject)) +/// 2: Bind a single handler to the matched elements, to be executed when the mouse pointer enters or leaves the elements. +/// 2.1 - hover(handlerInOut(eventObject)) +/// +/// +/// A function to execute when the mouse pointer enters the element. +/// +/// +/// A function to execute when the mouse pointer leaves the element. +/// +/// + + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + }; +jQuery.prototype.html = function( value ) { +/// +/// 1: Get the HTML contents of the first element in the set of matched elements. +/// 1.1 - html() +/// 2: Set the HTML contents of each element in the set of matched elements. +/// 2.1 - html(htmlString) +/// 2.2 - html(function(index, oldhtml)) +/// +/// +/// A string of HTML to set as the content of each matched element. +/// +/// + + if ( value === undefined ) { + return this[0] && this[0].nodeType === 1 ? + this[0].innerHTML.replace(rinlinejQuery, "") : + null; + + // See if we can take a shortcut and just use innerHTML + } else if ( typeof value === "string" && !rnocache.test( value ) && + (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) && + !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) { + + value = value.replace(rxhtmlTag, "<$1>"); + + try { + for ( var i = 0, l = this.length; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + if ( this[i].nodeType === 1 ) { + jQuery.cleanData( this[i].getElementsByTagName("*") ); + this[i].innerHTML = value; + } + } + + // If using innerHTML throws an exception, use the fallback method + } catch(e) { + this.empty().append( value ); + } + + } else if ( jQuery.isFunction( value ) ) { + this.each(function(i){ + var self = jQuery( this ); + + self.html( value.call(this, i, self.html()) ); + }); + + } else { + this.empty().append( value ); + } + + return this; + }; +jQuery.prototype.index = function( elem ) { +/// +/// Search for a given element from among the matched elements. +/// 1 - index() +/// 2 - index(selector) +/// 3 - index(element) +/// +/// +/// A selector representing a jQuery collection in which to look for an element. +/// +/// + + if ( !elem || typeof elem === "string" ) { + return jQuery.inArray( this[0], + // If it receives a string, the selector is used + // If it receives nothing, the siblings are used + elem ? jQuery( elem ) : this.parent().children() ); + } + // 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 ); + }; +jQuery.prototype.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 ); + }; +jQuery.prototype.innerHeight = function() { +/// +/// Get the current computed height for the first element in the set of matched elements, including padding but not border. +/// +/// + + var elem = this[0]; + return elem && elem.style ? + parseFloat( jQuery.css( elem, type, "padding" ) ) : + null; + }; +jQuery.prototype.innerWidth = function() { +/// +/// Get the current computed width for the first element in the set of matched elements, including padding but not border. +/// +/// + + var elem = this[0]; + return elem && elem.style ? + parseFloat( jQuery.css( elem, type, "padding" ) ) : + null; + }; +jQuery.prototype.insertAfter = function( selector ) { +/// +/// Insert every element in the set of matched elements after the target. +/// +/// +/// A selector, element, HTML string, or jQuery object; the matched set of elements will be inserted after the element(s) specified by this parameter. +/// +/// + + var ret = [], + insert = jQuery( selector ), + parent = this.length === 1 && this[0].parentNode; + + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + return this; + + } else { + for ( var i = 0, l = insert.length; i < l; i++ ) { + var elems = (i > 0 ? this.clone(true) : this).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +jQuery.prototype.insertBefore = function( selector ) { +/// +/// Insert every element in the set of matched elements before the target. +/// +/// +/// A selector, element, HTML string, or jQuery object; the matched set of elements will be inserted before the element(s) specified by this parameter. +/// +/// + + var ret = [], + insert = jQuery( selector ), + parent = this.length === 1 && this[0].parentNode; + + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + return this; + + } else { + for ( var i = 0, l = insert.length; i < l; i++ ) { + var elems = (i > 0 ? this.clone(true) : this).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +jQuery.prototype.is = function( selector ) { +/// +/// Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments. +/// 1 - is(selector) +/// 2 - is(function(index)) +/// 3 - is(jQuery object) +/// 4 - is(element) +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + return !!selector && ( typeof selector === "string" ? + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }; +jQuery.prototype.keydown = function( data, fn ) { +/// +/// Bind an event handler to the "keydown" JavaScript event, or trigger that event on an element. +/// 1 - keydown(handler(eventObject)) +/// 2 - keydown(eventData, handler(eventObject)) +/// 3 - keydown() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.keypress = function( data, fn ) { +/// +/// Bind an event handler to the "keypress" JavaScript event, or trigger that event on an element. +/// 1 - keypress(handler(eventObject)) +/// 2 - keypress(eventData, handler(eventObject)) +/// 3 - keypress() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.keyup = function( data, fn ) { +/// +/// Bind an event handler to the "keyup" JavaScript event, or trigger that event on an element. +/// 1 - keyup(handler(eventObject)) +/// 2 - keyup(eventData, handler(eventObject)) +/// 3 - keyup() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.last = function() { +/// +/// Reduce the set of matched elements to the final one in the set. +/// +/// + + return this.eq( -1 ); + }; +jQuery.prototype.length = 0; +jQuery.prototype.live = function( types, data, fn, origSelector /* Internal Use Only */ ) { +/// +/// Attach a handler to the event for all elements which match the current selector, now and in the future. +/// 1 - live(eventType, handler) +/// 2 - live(eventType, eventData, handler) +/// 3 - live(events) +/// +/// +/// A string containing a JavaScript event type, such as "click" or "keydown." As of jQuery 1.4 the string can contain multiple, space-separated event types or custom event names, as well. +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute at the time the event is triggered. +/// +/// + + 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; + }; +jQuery.prototype.load = function( url, params, callback ) { +/// +/// 1: Bind an event handler to the "load" JavaScript event. +/// 1.1 - load(handler(eventObject)) +/// 1.2 - load(eventData, handler(eventObject)) +/// 2: Load data from the server and place the returned HTML into the matched element. +/// 2.1 - load(url, data, complete(responseText, textStatus, XMLHttpRequest)) +/// +/// +/// A string containing the URL to which the request is sent. +/// +/// +/// A map or string that is sent to the server with the request. +/// +/// +/// A callback function that is executed when the request completes. +/// +/// + + if ( typeof url !== "string" && _load ) { + return _load.apply( this, arguments ); + + // Don't do a request if no elements are being requested + } else if ( !this.length ) { + return this; + } + + var off = url.indexOf( " " ); + if ( off >= 0 ) { + var selector = url.slice( off, url.length ); + url = url.slice( 0, off ); + } + + // Default to a GET request + var type = "GET"; + + // If the second parameter was provided + if ( params ) { + // If it's a function + if ( jQuery.isFunction( params ) ) { + // We assume that it's the callback + callback = params; + params = undefined; + + // Otherwise, build a param string + } else if ( typeof params === "object" ) { + params = jQuery.param( params, jQuery.ajaxSettings.traditional ); + type = "POST"; + } + } + + var self = this; + + // Request the remote document + jQuery.ajax({ + url: url, + type: type, + dataType: "html", + data: params, + // Complete callback (responseText is used internally) + complete: function( jqXHR, status, responseText ) { + // Store the response as specified by the jqXHR object + responseText = jqXHR.responseText; + // If successful, inject the HTML into all the matched elements + if ( jqXHR.isResolved() ) { + // #4825: Get the actual response in case + // a dataFilter is present in ajaxSettings + jqXHR.done(function( r ) { + responseText = r; + }); + // See if a selector was specified + self.html( selector ? + // Create a dummy div to hold the results + jQuery("
    ") + // inject the contents of the document in, removing the scripts + // to avoid any 'Permission Denied' errors in IE + .append(responseText.replace(rscript, "")) + + // Locate the specified elements + .find(selector) : + + // If not, just inject the full result + responseText ); + } + + if ( callback ) { + self.each( callback, [ responseText, status, jqXHR ] ); + } + } + }); + + return this; + }; +jQuery.prototype.map = function( callback ) { +/// +/// Pass each element in the current matched set through a function, producing a new jQuery object containing the return values. +/// +/// +/// A function object that will be invoked for each element in the current set. +/// +/// + + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }; +jQuery.prototype.mousedown = function( data, fn ) { +/// +/// Bind an event handler to the "mousedown" JavaScript event, or trigger that event on an element. +/// 1 - mousedown(handler(eventObject)) +/// 2 - mousedown(eventData, handler(eventObject)) +/// 3 - mousedown() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.mouseenter = function( data, fn ) { +/// +/// Bind an event handler to be fired when the mouse enters an element, or trigger that handler on an element. +/// 1 - mouseenter(handler(eventObject)) +/// 2 - mouseenter(eventData, handler(eventObject)) +/// 3 - mouseenter() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.mouseleave = function( data, fn ) { +/// +/// Bind an event handler to be fired when the mouse leaves an element, or trigger that handler on an element. +/// 1 - mouseleave(handler(eventObject)) +/// 2 - mouseleave(eventData, handler(eventObject)) +/// 3 - mouseleave() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.mousemove = function( data, fn ) { +/// +/// Bind an event handler to the "mousemove" JavaScript event, or trigger that event on an element. +/// 1 - mousemove(handler(eventObject)) +/// 2 - mousemove(eventData, handler(eventObject)) +/// 3 - mousemove() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.mouseout = function( data, fn ) { +/// +/// Bind an event handler to the "mouseout" JavaScript event, or trigger that event on an element. +/// 1 - mouseout(handler(eventObject)) +/// 2 - mouseout(eventData, handler(eventObject)) +/// 3 - mouseout() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.mouseover = function( data, fn ) { +/// +/// Bind an event handler to the "mouseover" JavaScript event, or trigger that event on an element. +/// 1 - mouseover(handler(eventObject)) +/// 2 - mouseover(eventData, handler(eventObject)) +/// 3 - mouseover() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.mouseup = function( data, fn ) { +/// +/// Bind an event handler to the "mouseup" JavaScript event, or trigger that event on an element. +/// 1 - mouseup(handler(eventObject)) +/// 2 - mouseup(eventData, handler(eventObject)) +/// 3 - mouseup() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.next = function( until, selector ) { +/// +/// Get the immediately following sibling of each element in the set of matched elements. If a selector is provided, it retrieves the next sibling only if it matches that selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.nextAll = function( until, selector ) { +/// +/// Get all following siblings of each element in the set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.nextUntil = function( until, selector ) { +/// +/// Get all following siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object passed. +/// 1 - nextUntil(selector, filter) +/// 2 - nextUntil(element, filter) +/// +/// +/// A string containing a selector expression to indicate where to stop matching following sibling elements. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.not = function( selector ) { +/// +/// Remove elements from the set of matched elements. +/// 1 - not(selector) +/// 2 - not(elements) +/// 3 - not(function(index)) +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + return this.pushStack( winnow(this, selector, false), "not", selector); + }; +jQuery.prototype.offset = function( options ) { +/// +/// 1: Get the current coordinates of the first element in the set of matched elements, relative to the document. +/// 1.1 - offset() +/// 2: Set the current coordinates of every element in the set of matched elements, relative to the document. +/// 2.1 - offset(coordinates) +/// 2.2 - offset(function(index, coords)) +/// +/// +/// An object containing the properties top and left, which are integers indicating the new top and left coordinates for the elements. +/// +/// + + var elem = this[0], box; + + if ( options ) { + return this.each(function( i ) { + jQuery.offset.setOffset( this, options, i ); + }); + } + + if ( !elem || !elem.ownerDocument ) { + return null; + } + + if ( elem === elem.ownerDocument.body ) { + return jQuery.offset.bodyOffset( elem ); + } + + try { + box = elem.getBoundingClientRect(); + } catch(e) {} + + var doc = elem.ownerDocument, + docElem = doc.documentElement; + + // Make sure we're not dealing with a disconnected DOM node + if ( !box || !jQuery.contains( docElem, elem ) ) { + return box ? { top: box.top, left: box.left } : { top: 0, left: 0 }; + } + + var body = doc.body, + win = getWindow(doc), + clientTop = docElem.clientTop || body.clientTop || 0, + clientLeft = docElem.clientLeft || body.clientLeft || 0, + scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop, + scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft, + top = box.top + scrollTop - clientTop, + left = box.left + scrollLeft - clientLeft; + + return { top: top, left: left }; + }; +jQuery.prototype.offsetParent = function() { +/// +/// Get the closest ancestor element that is positioned. +/// +/// + + return this.map(function() { + var offsetParent = this.offsetParent || document.body; + while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) { + offsetParent = offsetParent.offsetParent; + } + return offsetParent; + }); + }; +jQuery.prototype.one = function( type, data, fn ) { +/// +/// Attach a handler to an event for the elements. The handler is executed at most once per element. +/// +/// +/// A string containing one or more JavaScript event types, such as "click" or "submit," or custom event names. +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute at the time the event is triggered. +/// +/// + + 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.prototype.outerHeight = function( margin ) { +/// +/// Get the current computed height for the first element in the set of matched elements, including padding, border, and optionally margin. +/// +/// +/// A Boolean indicating whether to include the element's margin in the calculation. +/// +/// + + var elem = this[0]; + return elem && elem.style ? + parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) : + null; + }; +jQuery.prototype.outerWidth = function( margin ) { +/// +/// Get the current computed width for the first element in the set of matched elements, including padding and border. +/// +/// +/// A Boolean indicating whether to include the element's margin in the calculation. +/// +/// + + var elem = this[0]; + return elem && elem.style ? + parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) : + null; + }; +jQuery.prototype.parent = function( until, selector ) { +/// +/// Get the parent of each element in the current set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.parents = function( until, selector ) { +/// +/// Get the ancestors of each element in the current set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.parentsUntil = function( until, selector ) { +/// +/// Get the ancestors of each element in the current set of matched elements, up to but not including the element matched by the selector, DOM node, or jQuery object. +/// 1 - parentsUntil(selector, filter) +/// 2 - parentsUntil(element, filter) +/// +/// +/// A string containing a selector expression to indicate where to stop matching ancestor elements. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.position = function() { +/// +/// Get the current coordinates of the first element in the set of matched elements, relative to the offset parent. +/// +/// + + if ( !this[0] ) { + return null; + } + + var elem = this[0], + + // Get *real* offsetParent + offsetParent = this.offsetParent(), + + // Get correct offsets + offset = this.offset(), + parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset(); + + // Subtract element margins + // note: when an element has margin: auto the offsetLeft and marginLeft + // are the same in Safari causing offset.left to incorrectly be 0 + offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0; + offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0; + + // Add offsetParent borders + parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0; + parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0; + + // Subtract the two offsets + return { + top: offset.top - parentOffset.top, + left: offset.left - parentOffset.left + }; + }; +jQuery.prototype.prepend = function() { +/// +/// Insert content, specified by the parameter, to the beginning of each element in the set of matched elements. +/// 1 - prepend(content, content) +/// 2 - prepend(function(index, html)) +/// +/// +/// DOM element, array of elements, HTML string, or jQuery object to insert at the beginning of each element in the set of matched elements. +/// +/// +/// One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert at the beginning of each element in the set of matched elements. +/// +/// + + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 ) { + this.insertBefore( elem, this.firstChild ); + } + }); + }; +jQuery.prototype.prependTo = function( selector ) { +/// +/// Insert every element in the set of matched elements to the beginning of the target. +/// +/// +/// A selector, element, HTML string, or jQuery object; the matched set of elements will be inserted at the beginning of the element(s) specified by this parameter. +/// +/// + + var ret = [], + insert = jQuery( selector ), + parent = this.length === 1 && this[0].parentNode; + + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + return this; + + } else { + for ( var i = 0, l = insert.length; i < l; i++ ) { + var elems = (i > 0 ? this.clone(true) : this).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +jQuery.prototype.prev = function( until, selector ) { +/// +/// Get the immediately preceding sibling of each element in the set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.prevAll = function( until, selector ) { +/// +/// Get all preceding siblings of each element in the set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.prevUntil = function( until, selector ) { +/// +/// Get all preceding siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object. +/// 1 - prevUntil(selector, filter) +/// 2 - prevUntil(element, filter) +/// +/// +/// A string containing a selector expression to indicate where to stop matching preceding sibling elements. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.promise = function( type, object ) { +/// +/// Return a Promise object to observe when all actions of a certain type bound to the collection, queued or not, have finished. +/// +/// +/// The type of queue that needs to be observed. +/// +/// +/// Object onto which the promise methods have to be attached +/// +/// + + 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(); + }; +jQuery.prototype.prop = function( name, value ) { +/// +/// 1: Get the value of a property for the first element in the set of matched elements. +/// 1.1 - prop(propertyName) +/// 2: Set one or more properties for the set of matched elements. +/// 2.1 - prop(propertyName, value) +/// 2.2 - prop(map) +/// 2.3 - prop(propertyName, function(index, oldPropertyValue)) +/// +/// +/// The name of the property to set. +/// +/// +/// A value to set for the property. +/// +/// + + return jQuery.access( this, name, value, true, jQuery.prop ); + }; +jQuery.prototype.pushStack = function( elems, name, selector ) { +/// +/// Add a collection of DOM elements onto the jQuery stack. +/// 1 - pushStack(elements) +/// 2 - pushStack(elements, name, arguments) +/// +/// +/// An array of elements to push onto the stack and make into a new jQuery object. +/// +/// +/// The name of a jQuery method that generated the array of elements. +/// +/// +/// The arguments that were passed in to the jQuery method (for serialization). +/// +/// + + // 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; + }; +jQuery.prototype.queue = function( type, data ) { +/// +/// 1: Show the queue of functions to be executed on the matched elements. +/// 1.1 - queue(queueName) +/// 2: Manipulate the queue of functions to be executed on the matched elements. +/// 2.1 - queue(queueName, newQueue) +/// 2.2 - queue(queueName, callback( next )) +/// +/// +/// A string containing the name of the queue. Defaults to fx, the standard effects queue. +/// +/// +/// An array of functions to replace the current queue contents. +/// +/// + + 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 ); + } + }); + }; +jQuery.prototype.ready = function( fn ) { +/// +/// Specify a function to execute when the DOM is fully loaded. +/// +/// +/// A function to execute after the DOM is ready. +/// +/// + + // Attach the listeners + jQuery.bindReady(); + + // Add the callback + readyList.done( fn ); + + return this; + }; +jQuery.prototype.remove = function( selector, keepData ) { +/// +/// Remove the set of matched elements from the DOM. +/// +/// +/// A selector expression that filters the set of matched elements to be removed. +/// +/// + + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + if ( !selector || jQuery.filter( selector, [ elem ] ).length ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName("*") ); + jQuery.cleanData( [ elem ] ); + } + + if ( elem.parentNode ) { + elem.parentNode.removeChild( elem ); + } + } + } + + return this; + }; +jQuery.prototype.removeAttr = function( name ) { +/// +/// Remove an attribute from each element in the set of matched elements. +/// +/// +/// An attribute to remove. +/// +/// + + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }; +jQuery.prototype.removeClass = function( value ) { +/// +/// Remove a single class, multiple classes, or all classes from each element in the set of matched elements. +/// 1 - removeClass(className) +/// 2 - removeClass(function(index, class)) +/// +/// +/// One or more space-separated classes to be removed from the class attribute of each matched element. +/// +/// + + 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; + }; +jQuery.prototype.removeData = function( key ) { +/// +/// Remove a previously-stored piece of data. +/// +/// +/// A string naming the piece of data to delete. +/// +/// + + return this.each(function() { + jQuery.removeData( this, key ); + }); + }; +jQuery.prototype.removeProp = function( name ) { +/// +/// Remove a property for the set of matched elements. +/// +/// +/// The name of the property to set. +/// +/// + + 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 ) {} + }); + }; +jQuery.prototype.replaceAll = function( selector ) { +/// +/// Replace each target element with the set of matched elements. +/// +/// +/// A selector expression indicating which element(s) to replace. +/// +/// + + var ret = [], + insert = jQuery( selector ), + parent = this.length === 1 && this[0].parentNode; + + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + return this; + + } else { + for ( var i = 0, l = insert.length; i < l; i++ ) { + var elems = (i > 0 ? this.clone(true) : this).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +jQuery.prototype.replaceWith = function( value ) { +/// +/// Replace each element in the set of matched elements with the provided new content. +/// 1 - replaceWith(newContent) +/// 2 - replaceWith(function) +/// +/// +/// The content to insert. May be an HTML string, DOM element, or jQuery object. +/// +/// + + if ( this[0] && this[0].parentNode ) { + // Make sure that the elements are removed from the DOM before they are inserted + // this can help fix replacing a parent with child elements + if ( jQuery.isFunction( value ) ) { + return this.each(function(i) { + var self = jQuery(this), old = self.html(); + self.replaceWith( value.call( this, i, old ) ); + }); + } + + if ( typeof value !== "string" ) { + value = jQuery( value ).detach(); + } + + return this.each(function() { + var next = this.nextSibling, + parent = this.parentNode; + + jQuery( this ).remove(); + + if ( next ) { + jQuery(next).before( value ); + } else { + jQuery(parent).append( value ); + } + }); + } else { + return this.length ? + this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) : + this; + } + }; +jQuery.prototype.resize = function( data, fn ) { +/// +/// Bind an event handler to the "resize" JavaScript event, or trigger that event on an element. +/// 1 - resize(handler(eventObject)) +/// 2 - resize(eventData, handler(eventObject)) +/// 3 - resize() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.scroll = function( data, fn ) { +/// +/// Bind an event handler to the "scroll" JavaScript event, or trigger that event on an element. +/// 1 - scroll(handler(eventObject)) +/// 2 - scroll(eventData, handler(eventObject)) +/// 3 - scroll() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.scrollLeft = function( val ) { +/// +/// 1: Get the current horizontal position of the scroll bar for the first element in the set of matched elements. +/// 1.1 - scrollLeft() +/// 2: Set the current horizontal position of the scroll bar for each of the set of matched elements. +/// 2.1 - scrollLeft(value) +/// +/// +/// An integer indicating the new position to set the scroll bar to. +/// +/// + + var elem, win; + + if ( val === undefined ) { + elem = this[ 0 ]; + + if ( !elem ) { + return null; + } + + win = getWindow( elem ); + + // Return the scroll offset + return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] : + jQuery.support.boxModel && win.document.documentElement[ method ] || + win.document.body[ method ] : + elem[ method ]; + } + + // Set the scroll offset + return this.each(function() { + win = getWindow( this ); + + if ( win ) { + win.scrollTo( + !i ? val : jQuery( win ).scrollLeft(), + i ? val : jQuery( win ).scrollTop() + ); + + } else { + this[ method ] = val; + } + }); + }; +jQuery.prototype.scrollTop = function( val ) { +/// +/// 1: Get the current vertical position of the scroll bar for the first element in the set of matched elements. +/// 1.1 - scrollTop() +/// 2: Set the current vertical position of the scroll bar for each of the set of matched elements. +/// 2.1 - scrollTop(value) +/// +/// +/// An integer indicating the new position to set the scroll bar to. +/// +/// + + var elem, win; + + if ( val === undefined ) { + elem = this[ 0 ]; + + if ( !elem ) { + return null; + } + + win = getWindow( elem ); + + // Return the scroll offset + return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] : + jQuery.support.boxModel && win.document.documentElement[ method ] || + win.document.body[ method ] : + elem[ method ]; + } + + // Set the scroll offset + return this.each(function() { + win = getWindow( this ); + + if ( win ) { + win.scrollTo( + !i ? val : jQuery( win ).scrollLeft(), + i ? val : jQuery( win ).scrollTop() + ); + + } else { + this[ method ] = val; + } + }); + }; +jQuery.prototype.select = function( data, fn ) { +/// +/// Bind an event handler to the "select" JavaScript event, or trigger that event on an element. +/// 1 - select(handler(eventObject)) +/// 2 - select(eventData, handler(eventObject)) +/// 3 - select() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.serialize = function() { +/// +/// Encode a set of form elements as a string for submission. +/// +/// + + return jQuery.param( this.serializeArray() ); + }; +jQuery.prototype.serializeArray = function() { +/// +/// Encode a set of form elements as an array of names and values. +/// +/// + + return this.map(function(){ + return this.elements ? jQuery.makeArray( this.elements ) : this; + }) + .filter(function(){ + return this.name && !this.disabled && + ( this.checked || rselectTextarea.test( this.nodeName ) || + rinput.test( this.type ) ); + }) + .map(function( i, elem ){ + var val = jQuery( this ).val(); + + return val == null ? + null : + jQuery.isArray( val ) ? + jQuery.map( val, function( val, i ){ + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }) : + { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }).get(); + }; +jQuery.prototype.show = function( speed, easing, callback ) { +/// +/// Display the matched elements. +/// 1 - show() +/// 2 - show(duration, callback) +/// 3 - show(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + var elem, display; + + if ( speed || speed === 0 ) { + return this.animate( genFx("show", 3), speed, easing, callback); + + } else { + for ( var i = 0, j = this.length; i < j; i++ ) { + elem = this[i]; + + if ( elem.style ) { + display = elem.style.display; + + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !jQuery._data(elem, "olddisplay") && display === "none" ) { + display = elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( display === "" && jQuery.css( elem, "display" ) === "none" ) { + jQuery._data(elem, "olddisplay", defaultDisplay(elem.nodeName)); + } + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( i = 0; i < j; i++ ) { + elem = this[i]; + + if ( elem.style ) { + display = elem.style.display; + + if ( display === "" || display === "none" ) { + elem.style.display = jQuery._data(elem, "olddisplay") || ""; + } + } + } + + return this; + } + }; +jQuery.prototype.siblings = function( until, selector ) { +/// +/// Get the siblings of each element in the set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.size = function() { +/// +/// Return the number of elements in the jQuery object. +/// +/// + + return this.length; + }; +jQuery.prototype.slice = function() { +/// +/// Reduce the set of matched elements to a subset specified by a range of indices. +/// +/// +/// An integer indicating the 0-based position at which the elements begin to be selected. If negative, it indicates an offset from the end of the set. +/// +/// +/// An integer indicating the 0-based position at which the elements stop being selected. If negative, it indicates an offset from the end of the set. If omitted, the range continues until the end of the set. +/// +/// + + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }; +jQuery.prototype.slideDown = function( speed, easing, callback ) { +/// +/// Display the matched elements with a sliding motion. +/// 1 - slideDown(duration, callback) +/// 2 - slideDown(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.animate( props, speed, easing, callback ); + }; +jQuery.prototype.slideToggle = function( speed, easing, callback ) { +/// +/// Display or hide the matched elements with a sliding motion. +/// 1 - slideToggle(duration, callback) +/// 2 - slideToggle(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.animate( props, speed, easing, callback ); + }; +jQuery.prototype.slideUp = function( speed, easing, callback ) { +/// +/// Hide the matched elements with a sliding motion. +/// 1 - slideUp(duration, callback) +/// 2 - slideUp(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.animate( props, speed, easing, callback ); + }; +jQuery.prototype.stop = function( clearQueue, gotoEnd ) { +/// +/// Stop the currently-running animation on the matched elements. +/// +/// +/// A Boolean indicating whether to remove queued animation as well. Defaults to false. +/// +/// +/// A Boolean indicating whether to complete the current animation immediately. Defaults to false. +/// +/// + + if ( clearQueue ) { + this.queue([]); + } + + this.each(function() { + var timers = jQuery.timers, + i = timers.length; + // clear marker counters if we know they won't be + if ( !gotoEnd ) { + jQuery._unmark( true, this ); + } + while ( i-- ) { + if ( timers[i].elem === this ) { + if (gotoEnd) { + // force the next step to be the last + timers[i](true); + } + + timers.splice(i, 1); + } + } + }); + + // start the next in the queue if the last step wasn't forced + if ( !gotoEnd ) { + this.dequeue(); + } + + return this; + }; +jQuery.prototype.submit = function( data, fn ) { +/// +/// Bind an event handler to the "submit" JavaScript event, or trigger that event on an element. +/// 1 - submit(handler(eventObject)) +/// 2 - submit(eventData, handler(eventObject)) +/// 3 - submit() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.text = function( text ) { +/// +/// 1: Get the combined text contents of each element in the set of matched elements, including their descendants. +/// 1.1 - text() +/// 2: Set the content of each element in the set of matched elements to the specified text. +/// 2.1 - text(textString) +/// 2.2 - text(function(index, text)) +/// +/// +/// A string of text to set as the content of each matched element. +/// +/// + + if ( jQuery.isFunction(text) ) { + return this.each(function(i) { + var self = jQuery( this ); + + self.text( text.call(this, i, self.text()) ); + }); + } + + if ( typeof text !== "object" && text !== undefined ) { + return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); + } + + return jQuery.text( this ); + }; +jQuery.prototype.toArray = function() { +/// +/// Retrieve all the DOM elements contained in the jQuery set, as an array. +/// +/// + + return slice.call( this, 0 ); + }; +jQuery.prototype.toggle = function( fn, fn2, callback ) { +/// +/// 1: Bind two or more handlers to the matched elements, to be executed on alternate clicks. +/// 1.1 - toggle(handler(eventObject), handler(eventObject), handler(eventObject)) +/// 2: Display or hide the matched elements. +/// 2.1 - toggle(duration, callback) +/// 2.2 - toggle(duration, easing, callback) +/// 2.3 - toggle(showOrHide) +/// +/// +/// A function to execute every even time the element is clicked. +/// +/// +/// A function to execute every odd time the element is clicked. +/// +/// +/// Additional handlers to cycle through after clicks. +/// +/// + + var bool = typeof fn === "boolean"; + + if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) { + this._toggle.apply( this, arguments ); + + } else if ( fn == null || bool ) { + this.each(function() { + var state = bool ? fn : jQuery(this).is(":hidden"); + jQuery(this)[ state ? "show" : "hide" ](); + }); + + } else { + this.animate(genFx("toggle", 3), fn, fn2, callback); + } + + return this; + }; +jQuery.prototype.toggleClass = function( value, stateVal ) { +/// +/// Add or remove one or more classes from each element in the set of matched elements, depending on either the class's presence or the value of the switch argument. +/// 1 - toggleClass(className) +/// 2 - toggleClass(className, switch) +/// 3 - toggleClass(function(index, class, switch), switch) +/// +/// +/// One or more class names (separated by spaces) to be toggled for each element in the matched set. +/// +/// +/// A Boolean (not just truthy/falsy) value to determine whether the class should be added or removed. +/// +/// + + 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__" ) || ""; + } + }); + }; +jQuery.prototype.trigger = function( type, data ) { +/// +/// Execute all handlers and behaviors attached to the matched elements for the given event type. +/// 1 - trigger(eventType, extraParameters) +/// 2 - trigger(event) +/// +/// +/// A string containing a JavaScript event type, such as click or submit. +/// +/// +/// Additional parameters to pass along to the event handler. +/// +/// + + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }; +jQuery.prototype.triggerHandler = function( type, data ) { +/// +/// Execute all handlers attached to an element for an event. +/// +/// +/// A string containing a JavaScript event type, such as click or submit. +/// +/// +/// An array of additional parameters to pass along to the event handler. +/// +/// + + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }; +jQuery.prototype.unbind = function( type, fn ) { +/// +/// Remove a previously-attached event handler from the elements. +/// 1 - unbind(eventType, handler(eventObject)) +/// 2 - unbind(eventType, false) +/// 3 - unbind(event) +/// +/// +/// A string containing a JavaScript event type, such as click or submit. +/// +/// +/// The function that is to be no longer executed. +/// +/// + + // 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; + }; +jQuery.prototype.undelegate = function( selector, types, fn ) { +/// +/// Remove a handler from the event for all elements which match the current selector, now or in the future, based upon a specific set of root elements. +/// 1 - undelegate() +/// 2 - undelegate(selector, eventType) +/// 3 - undelegate(selector, eventType, handler) +/// 4 - undelegate(selector, events) +/// 5 - undelegate(namespace) +/// +/// +/// A selector which will be used to filter the event results. +/// +/// +/// A string containing a JavaScript event type, such as "click" or "keydown" +/// +/// +/// A function to execute at the time the event is triggered. +/// +/// + + if ( arguments.length === 0 ) { + return this.unbind( "live" ); + + } else { + return this.die( types, null, fn, selector ); + } + }; +jQuery.prototype.unload = function( data, fn ) { +/// +/// Bind an event handler to the "unload" JavaScript event. +/// 1 - unload(handler(eventObject)) +/// 2 - unload(eventData, handler(eventObject)) +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.unwrap = function() { +/// +/// Remove the parents of the set of matched elements from the DOM, leaving the matched elements in their place. +/// +/// + + return this.parent().each(function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + }).end(); + }; +jQuery.prototype.val = function( value ) { +/// +/// 1: Get the current value of the first element in the set of matched elements. +/// 1.1 - val() +/// 2: Set the value of each element in the set of matched elements. +/// 2.1 - val(value) +/// 2.2 - val(function(index, value)) +/// +/// +/// A string of text or an array of strings corresponding to the value of each matched element to set as selected/checked. +/// +/// + + 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.prototype.width = function( size ) { +/// +/// 1: Get the current computed width for the first element in the set of matched elements. +/// 1.1 - width() +/// 2: Set the CSS width of each element in the set of matched elements. +/// 2.1 - width(value) +/// 2.2 - width(function(index, width)) +/// +/// +/// An integer representing the number of pixels, or an integer along with an optional unit of measure appended (as a string). +/// +/// + + // Get window width or height + var elem = this[0]; + if ( !elem ) { + return size == null ? null : this; + } + + if ( jQuery.isFunction( size ) ) { + return this.each(function( i ) { + var self = jQuery( this ); + self[ type ]( size.call( this, i, self[ type ]() ) ); + }); + } + + if ( jQuery.isWindow( elem ) ) { + // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode + // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat + var docElemProp = elem.document.documentElement[ "client" + name ]; + return elem.document.compatMode === "CSS1Compat" && docElemProp || + elem.document.body[ "client" + name ] || docElemProp; + + // Get document width or height + } else if ( elem.nodeType === 9 ) { + // Either scroll[Width/Height] or offset[Width/Height], whichever is greater + return Math.max( + elem.documentElement["client" + name], + elem.body["scroll" + name], elem.documentElement["scroll" + name], + elem.body["offset" + name], elem.documentElement["offset" + name] + ); + + // Get or set width or height on the element + } else if ( size === undefined ) { + var orig = jQuery.css( elem, type ), + ret = parseFloat( orig ); + + return jQuery.isNaN( ret ) ? orig : ret; + + // Set the width or height on the element (default to pixels if value is unitless) + } else { + return this.css( type, typeof size === "string" ? size : size + "px" ); + } + }; +jQuery.prototype.wrap = function( html ) { +/// +/// Wrap an HTML structure around each element in the set of matched elements. +/// 1 - wrap(wrappingElement) +/// 2 - wrap(function(index)) +/// +/// +/// An HTML snippet, selector expression, jQuery object, or DOM element specifying the structure to wrap around the matched elements. +/// +/// + + return this.each(function() { + jQuery( this ).wrapAll( html ); + }); + }; +jQuery.prototype.wrapAll = function( html ) { +/// +/// Wrap an HTML structure around all elements in the set of matched elements. +/// +/// +/// An HTML snippet, selector expression, jQuery object, or DOM element specifying the structure to wrap around the matched elements. +/// +/// + + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapAll( html.call(this, i) ); + }); + } + + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); + + if ( this[0].parentNode ) { + wrap.insertBefore( this[0] ); + } + + wrap.map(function() { + var elem = this; + + while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { + elem = elem.firstChild; + } + + return elem; + }).append( this ); + } + + return this; + }; +jQuery.prototype.wrapInner = function( html ) { +/// +/// Wrap an HTML structure around the content of each element in the set of matched elements. +/// 1 - wrapInner(wrappingElement) +/// 2 - wrapInner(wrappingFunction) +/// +/// +/// An HTML snippet, selector expression, jQuery object, or DOM element specifying the structure to wrap around the content of the matched elements. +/// +/// + + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapInner( html.call(this, i) ); + }); + } + + return this.each(function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + }); + }; +jQuery.fn = jQuery.prototype; +jQuery.fn.init.prototype = jQuery.fn; +window.jQuery = window.$ = jQuery; +})(window); \ No newline at end of file diff --git a/SignalR/Scripts/jquery-1.6.2.js b/SignalR/Scripts/jquery-1.6.2.js new file mode 100644 index 000000000..f3201aacb --- /dev/null +++ b/SignalR/Scripts/jquery-1.6.2.js @@ -0,0 +1,8981 @@ +/*! + * jQuery JavaScript Library v1.6.2 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Thu Jun 30 14:16:56 2011 -0400 + */ +(function( window, undefined ) { + +// Use the correct document accordingly with window argument (sandbox) +var document = window.document, + navigator = window.navigator, + location = window.location; +var jQuery = (function() { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // (both of which we optimize for) + 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])/ig, + + // 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.2", + + // 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; + } + + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + 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 + // (xml & tmp used internally) + parseXML: function( data , xml , tmp ) { + + 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 ); + } + + tmp = xml.documentElement; + + if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) { + 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 ); + } + }, + + // Converts a dashed string to camelCased string; + // Used by both the css and data modules + camelCase: function( string ) { + return string.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 ( 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 ]( 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 + }; + if ( body ) { + jQuery.extend( testElementStyle, { + position: "absolute", + left: -1000, + top: -1000 + }); + } + 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 internalKey = jQuery.expando, getByName = typeof name === "string", thisCache, + + // 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 ][ 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; + } + + return getByName ? + // Check for both converted-to-camel and non-converted data property names + thisCache[ jQuery.camelCase( name ) ] || thisCache[ name ] : + thisCache; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var 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 ) { + var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ]; + + if ( thisCache ) { + 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 + if ( jQuery.support.deleteExpando || cache != window ) { + 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, + rinvalidChar = /\:|^on/, + formHook, 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].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 formHook for forms and if the name contains certain characters + } else if ( formHook && name !== "className" && + (jQuery.nodeName( elem, "form" ) || rinvalidChar.test( name )) ) { + + hooks = formHook; + } + } + } + + 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; + + if ( jQuery.support.getSetAttribute ) { + // Use removeAttribute in browsers that support it + elem.removeAttribute( name ); + } else { + jQuery.attr( elem, name, "" ); + elem.removeAttributeNode( elem.getAttributeNode( 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; + } + } + }, + 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; + } + }, + // Use the value property for back compat + // Use the formHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( formHook && jQuery.nodeName( elem, "button" ) ) { + return formHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( formHook && jQuery.nodeName( elem, "button" ) ) { + return formHook.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 )) !== undefined ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: {} +}); + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + return jQuery.prop( elem, name ) ? + 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 ) { + + // propFix is more comprehensive and contains all fixes + jQuery.attrFix = jQuery.propFix; + + // Use this for any attribute on a form in IE6/7 + formHook = jQuery.attrHooks.name = jQuery.attrHooks.title = 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 ) { + // Check form objects in IE (multiple bugs related) + // Only use nodeValue if the attribute node exists on the form + var ret = elem.getAttributeNode( name ); + if ( ret ) { + ret.nodeValue = value; + return 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; + } + } + } + }); +} + +// 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 = 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 = 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 = 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 ) { + if ( !elem || typeof elem === "string" ) { + return jQuery.inArray( this[0], + // If it receives a string, the selector is used + // If it receives nothing, the siblings are used + elem ? jQuery( elem ) : this.parent().children() ); + } + // 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 = /", "" ], + legend: [ 1, "
    ", "
    " ], + thead: [ 1, "", "
    " ], + 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 - + diff --git a/SignalR.Samples/Hubs/DemoHub/DemoHub.js b/SignalR.Samples/Hubs/DemoHub/DemoHub.js index 709a3e89f..466504968 100644 --- a/SignalR.Samples/Hubs/DemoHub/DemoHub.js +++ b/SignalR.Samples/Hubs/DemoHub/DemoHub.js @@ -1,11 +1,11 @@ $(function () { - var demo = signalR.demo; + var demo = $.connection.demo; demo.invoke = function (index) { $('#msg').append('
  • ' + index + ' client state index ->' + this.index + '
  • '); }; - signalR.hub.start(function () { + $.connection.hub.start(function () { demo.getValue(function (value) { $('#value').html('The value is ' + value + ' after 5 seconds'); }); diff --git a/SignalR.Samples/Hubs/DemoHub/index.htm b/SignalR.Samples/Hubs/DemoHub/index.htm index 83b6e5f8f..caf552330 100644 --- a/SignalR.Samples/Hubs/DemoHub/index.htm +++ b/SignalR.Samples/Hubs/DemoHub/index.htm @@ -4,12 +4,11 @@ - + -

    Generic Task

    @@ -30,4 +29,4 @@

    Message Pump

    - + \ No newline at end of file diff --git a/SignalR.Samples/Hubs/MouseTracking/MouseTracking.js b/SignalR.Samples/Hubs/MouseTracking/MouseTracking.js index aeb9e0acf..f3ae38ee7 100644 --- a/SignalR.Samples/Hubs/MouseTracking/MouseTracking.js +++ b/SignalR.Samples/Hubs/MouseTracking/MouseTracking.js @@ -1,5 +1,6 @@ $(function () { // Pure client side hub + var signalR = $.connection; signalR.mouseTracking.moveMouse = function (id, x, y) { if (id == this.id) { return; diff --git a/SignalR.Samples/Hubs/MouseTracking/index.htm b/SignalR.Samples/Hubs/MouseTracking/index.htm index 878972f30..787688bf2 100644 --- a/SignalR.Samples/Hubs/MouseTracking/index.htm +++ b/SignalR.Samples/Hubs/MouseTracking/index.htm @@ -5,11 +5,11 @@ - + - + \ No newline at end of file diff --git a/SignalR.Samples/Hubs/ShapeShare/ShapeShare.js b/SignalR.Samples/Hubs/ShapeShare/ShapeShare.js index a2485e052..a83ace5ba 100644 --- a/SignalR.Samples/Hubs/ShapeShare/ShapeShare.js +++ b/SignalR.Samples/Hubs/ShapeShare/ShapeShare.js @@ -7,7 +7,7 @@ $(function () { 'use strict'; - var shapeShare = signalR.shapeShare; + var shapeShare = $.connection.shapeShare; function changeShape(el) { shapeShare.changeShape($(el).data("shapeId"), el.offsetLeft, el.offsetTop || 0, el.clientWidth, el.clientHeight); @@ -139,7 +139,7 @@ $(function () { }); } - signalR.hub.start(function () { + $.connection.hub.start(function () { shapeShare.join($.cookie("userName"), function () { $.cookie("userName", shapeShare.user.Name, { expires: 30 }); $("#user").val(shapeShare.user.Name); diff --git a/SignalR.Samples/Hubs/ShapeShare/index.htm b/SignalR.Samples/Hubs/ShapeShare/index.htm index d0028ddc3..88437cd9d 100644 --- a/SignalR.Samples/Hubs/ShapeShare/index.htm +++ b/SignalR.Samples/Hubs/ShapeShare/index.htm @@ -6,7 +6,7 @@ - + diff --git a/SignalR.Samples/Performance/index.html b/SignalR.Samples/Performance/index.html index 508554dbc..da8ecd639 100644 --- a/SignalR.Samples/Performance/index.html +++ b/SignalR.Samples/Performance/index.html @@ -1,12 +1,12 @@ - + - + - + - + diff --git a/SignalR.Samples/Hubs/DemoHub/index.htm b/SignalR.Samples/Hubs/DemoHub/index.htm index 2cd68c7c5..f58e537da 100644 --- a/SignalR.Samples/Hubs/DemoHub/index.htm +++ b/SignalR.Samples/Hubs/DemoHub/index.htm @@ -2,6 +2,7 @@ + diff --git a/SignalR.Samples/Hubs/MouseTracking/index.htm b/SignalR.Samples/Hubs/MouseTracking/index.htm index 787688bf2..283df673a 100644 --- a/SignalR.Samples/Hubs/MouseTracking/index.htm +++ b/SignalR.Samples/Hubs/MouseTracking/index.htm @@ -2,6 +2,7 @@ + diff --git a/SignalR.Samples/Hubs/ShapeShare/index.htm b/SignalR.Samples/Hubs/ShapeShare/index.htm index 88437cd9d..2a21f11e1 100644 --- a/SignalR.Samples/Hubs/ShapeShare/index.htm +++ b/SignalR.Samples/Hubs/ShapeShare/index.htm @@ -2,6 +2,7 @@ SignalR Shape Share + diff --git a/SignalR.Samples/Streaming/index.html b/SignalR.Samples/Streaming/index.html index bcf1bf770..913bc3536 100644 --- a/SignalR.Samples/Streaming/index.html +++ b/SignalR.Samples/Streaming/index.html @@ -2,6 +2,7 @@ + + + + +HTML: + - + +HTML: + +

    Arbitrary Code

    +
    +
    +

    Generic Task

    diff --git a/SignalR/Hubs/Hub.cs b/SignalR/Hubs/Hub.cs index 795c1ff35..4b9069701 100644 --- a/SignalR/Hubs/Hub.cs +++ b/SignalR/Hubs/Hub.cs @@ -1,4 +1,5 @@ -namespace SignalR.Hubs { +using System.Threading.Tasks; +namespace SignalR.Hubs { public abstract class Hub : IHub { public IClientAgent Agent { get; set; } @@ -19,5 +20,23 @@ public void AddToGroup(string groupName) { public void RemoveFromGroup(string groupName) { GroupManager.RemoveFromGroup(Context.ClientId, groupName); } + + public static Task Invoke(string method, params object[] args) where T : IHub { + return Invoke(typeof(T).FullName, method, args); + } + + public static Task Invoke(string hubName, string method, params object[] args) { + var connection = Connection.GetConnection(); + return ClientAgent.Invoke(connection, method, hubName, method, args); + } + + public static dynamic GetClients() where T : IHub { + return GetClients(typeof(T).FullName); + } + + public static dynamic GetClients(string hubName) { + var connection = Connection.GetConnection(); + return new ClientAgent(connection, hubName); + } } } \ No newline at end of file From 9cc5fa2ac72092edaa0d28b093e14f5fe5af82e2 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Tue, 30 Aug 2011 13:22:03 -0700 Subject: [PATCH 0084/1128] Bump version to 0.2.2 --- Build/Build.proj | 2 +- Common/CommonAssemblyInfo.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Build/Build.proj b/Build/Build.proj index 15bfbd13c..213176288 100644 --- a/Build/Build.proj +++ b/Build/Build.proj @@ -11,7 +11,7 @@ 0 2 - 1 + 2 $(MajorVersion).$(MinorVersion).$(BuildVersion) diff --git a/Common/CommonAssemblyInfo.cs b/Common/CommonAssemblyInfo.cs index 09e2765b7..21258a8d4 100644 --- a/Common/CommonAssemblyInfo.cs +++ b/Common/CommonAssemblyInfo.cs @@ -10,5 +10,5 @@ [assembly: AssemblyCulture("")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyVersion("0.2.1.0")] +[assembly: AssemblyVersion("0.2.2.0")] [assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file From 7e3c0e608d1fa4ac336b04665ad7e96b6bb81d5d Mon Sep 17 00:00:00 2001 From: David Fowler Date: Tue, 30 Aug 2011 18:36:51 -0700 Subject: [PATCH 0085/1128] Make Group methods return Task --- SignalR.Samples/App_Start/Startup.cs | 28 +++++------ SignalR.Samples/Hubs/DemoHub/DemoHub.cs | 6 +++ SignalR.Samples/Hubs/DemoHub/DemoHub.js | 67 +++++++++++++------------ SignalR/Hubs/ClientAgent.cs | 12 ++--- SignalR/Hubs/Hub.cs | 8 +-- SignalR/IGroupManager.cs | 7 +-- SignalR/PersistentConnection.cs | 20 ++++---- 7 files changed, 80 insertions(+), 68 deletions(-) diff --git a/SignalR.Samples/App_Start/Startup.cs b/SignalR.Samples/App_Start/Startup.cs index 1c0781c91..55e840519 100644 --- a/SignalR.Samples/App_Start/Startup.cs +++ b/SignalR.Samples/App_Start/Startup.cs @@ -17,20 +17,20 @@ public static void Start() { //DependencyResolver.Register(typeof(ISignalBus), () => store); //DependencyResolver.Register(typeof(IMessageStore), () => store); - ThreadPool.QueueUserWorkItem(_ => { - var connection = Connection.GetConnection(); - var demoClients = Hub.GetClients(); - while (true) { - try { - connection.Broadcast(DateTime.Now.ToString()); - demoClients.fromArbitraryCode(DateTime.Now.ToString()); - } - catch (Exception ex) { - Trace.TraceError("SignalR error thrown in Streaming broadcast: {0}", ex); - } - Thread.Sleep(2000); - } - }); + //ThreadPool.QueueUserWorkItem(_ => { + // var connection = Connection.GetConnection(); + // var demoClients = Hub.GetClients(); + // while (true) { + // try { + // connection.Broadcast(DateTime.Now.ToString()); + // demoClients.fromArbitraryCode(DateTime.Now.ToString()); + // } + // catch (Exception ex) { + // Trace.TraceError("SignalR error thrown in Streaming broadcast: {0}", ex); + // } + // Thread.Sleep(2000); + // } + //}); } } } \ No newline at end of file diff --git a/SignalR.Samples/Hubs/DemoHub/DemoHub.cs b/SignalR.Samples/Hubs/DemoHub/DemoHub.cs index 56fd082df..a1c516ac2 100644 --- a/SignalR.Samples/Hubs/DemoHub/DemoHub.cs +++ b/SignalR.Samples/Hubs/DemoHub/DemoHub.cs @@ -13,6 +13,12 @@ public Task GetValue() { }); } + public void StuffWithGroups() { + Thread.Sleep(100); + AddToGroup("foo"); + Caller.foo("ok"); + } + public Task DynamicTask() { return Clients.signal(Guid.NewGuid()); } diff --git a/SignalR.Samples/Hubs/DemoHub/DemoHub.js b/SignalR.Samples/Hubs/DemoHub/DemoHub.js index a4dc9e7c8..6423d88a4 100644 --- a/SignalR.Samples/Hubs/DemoHub/DemoHub.js +++ b/SignalR.Samples/Hubs/DemoHub/DemoHub.js @@ -13,37 +13,42 @@ $('#arbitraryCode').html('Sending ' + value + ' from arbitrary code without the hub itself!'); }; - $.connection.hub.start(function () { - demo.getValue(function (value) { - $('#value').html('The value is ' + value + ' after 5 seconds'); - }); - - var p = { - Name: "Foo", - Age: 20, - Address: { Street: "One Microsoft Way", Zip: "98052" } - }; - - demo.complexType(p, function () { - $('#value').html('Complex Type ->' + window.JSON.stringify(this.person)); - }); - - demo.multipleCalls(); - - demo.dynamicTask().fail(function () { - $('#dynamicTask').html('The dynamic task failed :('); - }); - - demo.plainTask().done(function () { - $('#plainTask').html('Plain Task Result'); - }); - - demo.genericTaskTypedAsPlain().done(function (result) { - $('#genericTaskTypedAsPlain').html(result); - }); + demo.foo = function () { + $('#msg').append('foo'); + }; - demo.taskWithException().fail(function (e) { - $('#taskWithException').html(e); - }); + $.connection.hub.start(function () { + demo.stuffWithGroups(); + // demo.getValue(function (value) { + // $('#value').html('The value is ' + value + ' after 5 seconds'); + // }); + + // var p = { + // Name: "Foo", + // Age: 20, + // Address: { Street: "One Microsoft Way", Zip: "98052" } + // }; + + // demo.complexType(p, function () { + // $('#value').html('Complex Type ->' + window.JSON.stringify(this.person)); + // }); + + // demo.multipleCalls(); + + // demo.dynamicTask().fail(function () { + // $('#dynamicTask').html('The dynamic task failed :('); + // }); + + // demo.plainTask().done(function () { + // $('#plainTask').html('Plain Task Result'); + // }); + + // demo.genericTaskTypedAsPlain().done(function (result) { + // $('#genericTaskTypedAsPlain').html(result); + // }); + + // demo.taskWithException().fail(function (e) { + // $('#taskWithException').html(e); + // }); }); }); \ No newline at end of file diff --git a/SignalR/Hubs/ClientAgent.cs b/SignalR/Hubs/ClientAgent.cs index b620d29e6..61d7435b4 100644 --- a/SignalR/Hubs/ClientAgent.cs +++ b/SignalR/Hubs/ClientAgent.cs @@ -43,17 +43,17 @@ public static Task Invoke(IConnection connection, string signal, string hubName, return connection.Broadcast(signal, invocation); } - public void AddToGroup(string clientId, string groupName) { + public Task AddToGroup(string clientId, string groupName) { groupName = _hubName + "." + groupName; - SendCommand(clientId, CommandType.AddToGroup, groupName); + return SendCommand(clientId, CommandType.AddToGroup, groupName); } - public void RemoveFromGroup(string clientId, string groupName) { + public Task RemoveFromGroup(string clientId, string groupName) { groupName = _hubName + "." + groupName; - SendCommand(clientId, CommandType.RemoveFromGroup, groupName); + return SendCommand(clientId, CommandType.RemoveFromGroup, groupName); } - private void SendCommand(string clientId, CommandType commandType, object commandValue) { + private Task SendCommand(string clientId, CommandType commandType, object commandValue) { string signal = _hubName + "." + clientId + "." + PersistentConnection.SignalrCommand; var groupCommand = new SignalCommand { @@ -61,7 +61,7 @@ private void SendCommand(string clientId, CommandType commandType, object comman Value = commandValue }; - _connection.Broadcast(signal, groupCommand); + return _connection.Broadcast(signal, groupCommand); } } } diff --git a/SignalR/Hubs/Hub.cs b/SignalR/Hubs/Hub.cs index 4b9069701..1f863bbb4 100644 --- a/SignalR/Hubs/Hub.cs +++ b/SignalR/Hubs/Hub.cs @@ -13,12 +13,12 @@ public dynamic Clients { public HubContext Context { get; set; } public IGroupManager GroupManager { get; set; } - public void AddToGroup(string groupName) { - GroupManager.AddToGroup(Context.ClientId, groupName); + public Task AddToGroup(string groupName) { + return GroupManager.AddToGroup(Context.ClientId, groupName); } - public void RemoveFromGroup(string groupName) { - GroupManager.RemoveFromGroup(Context.ClientId, groupName); + public Task RemoveFromGroup(string groupName) { + return GroupManager.RemoveFromGroup(Context.ClientId, groupName); } public static Task Invoke(string method, params object[] args) where T : IHub { diff --git a/SignalR/IGroupManager.cs b/SignalR/IGroupManager.cs index 2dd30d2f0..8ed7f087d 100644 --- a/SignalR/IGroupManager.cs +++ b/SignalR/IGroupManager.cs @@ -1,6 +1,7 @@ -namespace SignalR { +using System.Threading.Tasks; +namespace SignalR { public interface IGroupManager { - void AddToGroup(string clientId, string groupName); - void RemoveFromGroup(string clientId, string groupName); + Task AddToGroup(string clientId, string groupName); + Task RemoveFromGroup(string clientId, string groupName); } } diff --git a/SignalR/PersistentConnection.cs b/SignalR/PersistentConnection.cs index 5d35122c3..caa004ec3 100644 --- a/SignalR/PersistentConnection.cs +++ b/SignalR/PersistentConnection.cs @@ -151,25 +151,25 @@ public void Send(object value) { _transport.Send(value); } - public void Send(string clientId, object value) { - Connection.Broadcast(clientId, value); + public Task Send(string clientId, object value) { + return Connection.Broadcast(clientId, value); } - public void SendToGroup(string groupName, object value) { - Connection.Broadcast(CreateQualifiedName(groupName), value); + public Task SendToGroup(string groupName, object value) { + return Connection.Broadcast(CreateQualifiedName(groupName), value); } - public void AddToGroup(string clientId, string groupName) { + public Task AddToGroup(string clientId, string groupName) { groupName = CreateQualifiedName(groupName); - SendCommand(clientId, CommandType.AddToGroup, groupName); + return SendCommand(clientId, CommandType.AddToGroup, groupName); } - public void RemoveFromGroup(string clientId, string groupName) { + public Task RemoveFromGroup(string clientId, string groupName) { groupName = CreateQualifiedName(groupName); - SendCommand(clientId, CommandType.RemoveFromGroup, groupName); + return SendCommand(clientId, CommandType.RemoveFromGroup, groupName); } - private void SendCommand(string clientId, CommandType type, object value) { + private Task SendCommand(string clientId, CommandType type, object value) { string signal = clientId + "." + SignalrCommand; var groupCommand = new SignalCommand { @@ -177,7 +177,7 @@ private void SendCommand(string clientId, CommandType type, object value) { Value = value }; - Connection.Broadcast(signal, groupCommand); + return Connection.Broadcast(signal, groupCommand); } private string CreateQualifiedName(string groupName) { From 98eb4c82a5ac71cf565f4d8734e6876f415ddf02 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Tue, 30 Aug 2011 18:50:30 -0700 Subject: [PATCH 0086/1128] Fixed issue where adding to groups and invoking a client action caused multiple calls to the client. - Fixed demo apps --- SignalR.Samples/App_Start/Startup.cs | 28 +++++----- SignalR.Samples/Hubs/DemoHub/DemoHub.cs | 6 +-- SignalR.Samples/Hubs/DemoHub/DemoHub.js | 69 +++++++++++++------------ SignalR.Samples/Hubs/DemoHub/index.htm | 4 ++ SignalR/Connection.cs | 2 +- 5 files changed, 57 insertions(+), 52 deletions(-) diff --git a/SignalR.Samples/App_Start/Startup.cs b/SignalR.Samples/App_Start/Startup.cs index 55e840519..1c0781c91 100644 --- a/SignalR.Samples/App_Start/Startup.cs +++ b/SignalR.Samples/App_Start/Startup.cs @@ -17,20 +17,20 @@ public static void Start() { //DependencyResolver.Register(typeof(ISignalBus), () => store); //DependencyResolver.Register(typeof(IMessageStore), () => store); - //ThreadPool.QueueUserWorkItem(_ => { - // var connection = Connection.GetConnection(); - // var demoClients = Hub.GetClients(); - // while (true) { - // try { - // connection.Broadcast(DateTime.Now.ToString()); - // demoClients.fromArbitraryCode(DateTime.Now.ToString()); - // } - // catch (Exception ex) { - // Trace.TraceError("SignalR error thrown in Streaming broadcast: {0}", ex); - // } - // Thread.Sleep(2000); - // } - //}); + ThreadPool.QueueUserWorkItem(_ => { + var connection = Connection.GetConnection(); + var demoClients = Hub.GetClients(); + while (true) { + try { + connection.Broadcast(DateTime.Now.ToString()); + demoClients.fromArbitraryCode(DateTime.Now.ToString()); + } + catch (Exception ex) { + Trace.TraceError("SignalR error thrown in Streaming broadcast: {0}", ex); + } + Thread.Sleep(2000); + } + }); } } } \ No newline at end of file diff --git a/SignalR.Samples/Hubs/DemoHub/DemoHub.cs b/SignalR.Samples/Hubs/DemoHub/DemoHub.cs index a1c516ac2..bf6661cbb 100644 --- a/SignalR.Samples/Hubs/DemoHub/DemoHub.cs +++ b/SignalR.Samples/Hubs/DemoHub/DemoHub.cs @@ -13,10 +13,10 @@ public Task GetValue() { }); } - public void StuffWithGroups() { - Thread.Sleep(100); + public void AddToGroups() { AddToGroup("foo"); - Caller.foo("ok"); + AddToGroup("bar"); + Caller.groupAdded(); } public Task DynamicTask() { diff --git a/SignalR.Samples/Hubs/DemoHub/DemoHub.js b/SignalR.Samples/Hubs/DemoHub/DemoHub.js index 6423d88a4..4575fba13 100644 --- a/SignalR.Samples/Hubs/DemoHub/DemoHub.js +++ b/SignalR.Samples/Hubs/DemoHub/DemoHub.js @@ -13,42 +13,43 @@ $('#arbitraryCode').html('Sending ' + value + ' from arbitrary code without the hub itself!'); }; - demo.foo = function () { - $('#msg').append('foo'); + demo.groupAdded = function () { + $('#groupAdded').append('Group Added'); }; $.connection.hub.start(function () { - demo.stuffWithGroups(); - // demo.getValue(function (value) { - // $('#value').html('The value is ' + value + ' after 5 seconds'); - // }); - - // var p = { - // Name: "Foo", - // Age: 20, - // Address: { Street: "One Microsoft Way", Zip: "98052" } - // }; - - // demo.complexType(p, function () { - // $('#value').html('Complex Type ->' + window.JSON.stringify(this.person)); - // }); - - // demo.multipleCalls(); - - // demo.dynamicTask().fail(function () { - // $('#dynamicTask').html('The dynamic task failed :('); - // }); - - // demo.plainTask().done(function () { - // $('#plainTask').html('Plain Task Result'); - // }); - - // demo.genericTaskTypedAsPlain().done(function (result) { - // $('#genericTaskTypedAsPlain').html(result); - // }); - - // demo.taskWithException().fail(function (e) { - // $('#taskWithException').html(e); - // }); + demo.getValue(function (value) { + $('#value').html('The value is ' + value + ' after 5 seconds'); + }); + + var p = { + Name: "Foo", + Age: 20, + Address: { Street: "One Microsoft Way", Zip: "98052" } + }; + + demo.complexType(p, function () { + $('#value').html('Complex Type ->' + window.JSON.stringify(this.person)); + }); + + demo.multipleCalls(); + + demo.dynamicTask().fail(function () { + $('#dynamicTask').html('The dynamic task failed :('); + }); + + demo.plainTask().done(function () { + $('#plainTask').html('Plain Task Result'); + }); + + demo.genericTaskTypedAsPlain().done(function (result) { + $('#genericTaskTypedAsPlain').html(result); + }); + + demo.taskWithException().fail(function (e) { + $('#taskWithException').html(e); + }); + + demo.addToGroups(); }); }); \ No newline at end of file diff --git a/SignalR.Samples/Hubs/DemoHub/index.htm b/SignalR.Samples/Hubs/DemoHub/index.htm index c31424fdd..e093dc313 100644 --- a/SignalR.Samples/Hubs/DemoHub/index.htm +++ b/SignalR.Samples/Hubs/DemoHub/index.htm @@ -14,6 +14,10 @@

    Arbitrary Code

    +

    Group Added

    +
    +
    +

    Generic Task

    diff --git a/SignalR/Connection.cs b/SignalR/Connection.cs index 5788108c3..713713396 100644 --- a/SignalR/Connection.cs +++ b/SignalR/Connection.cs @@ -144,7 +144,7 @@ private Task GetResponse(long messageId) { ProcessCommands(commands); - messageId = messageTask.Result.Last().Id; + messageId = messageTask.Result.Max(p => p.Id); // Get the message values and the max message id we received var messageValues = messageTask.Result.Except(commands) From 7a1fd66264a356b22a71a5cbf0819f9509b6aea8 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Tue, 30 Aug 2011 19:11:19 -0700 Subject: [PATCH 0087/1128] Bump version to 0.3 --- Build/Build.proj | 5 ++--- Common/CommonAssemblyInfo.cs | 2 +- SignalR.Ninject/Properties/SignalR.Ninject.nuspec | 2 +- SignalR.ScaleOut/Properties/SignalR.ScaleOut.nuspec | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Build/Build.proj b/Build/Build.proj index 213176288..4b32dabdf 100644 --- a/Build/Build.proj +++ b/Build/Build.proj @@ -10,9 +10,8 @@ 0 - 2 - 2 - $(MajorVersion).$(MinorVersion).$(BuildVersion) + 3 + $(MajorVersion).$(MinorVersion) diff --git a/Common/CommonAssemblyInfo.cs b/Common/CommonAssemblyInfo.cs index 21258a8d4..1b29f46b4 100644 --- a/Common/CommonAssemblyInfo.cs +++ b/Common/CommonAssemblyInfo.cs @@ -10,5 +10,5 @@ [assembly: AssemblyCulture("")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyVersion("0.2.2.0")] +[assembly: AssemblyVersion("0.3.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/SignalR.Ninject/Properties/SignalR.Ninject.nuspec b/SignalR.Ninject/Properties/SignalR.Ninject.nuspec index d09f1c33c..62246fcab 100644 --- a/SignalR.Ninject/Properties/SignalR.Ninject.nuspec +++ b/SignalR.Ninject/Properties/SignalR.Ninject.nuspec @@ -9,7 +9,7 @@ false $description$ - + \ No newline at end of file diff --git a/SignalR.ScaleOut/Properties/SignalR.ScaleOut.nuspec b/SignalR.ScaleOut/Properties/SignalR.ScaleOut.nuspec index d09f1c33c..62246fcab 100644 --- a/SignalR.ScaleOut/Properties/SignalR.ScaleOut.nuspec +++ b/SignalR.ScaleOut/Properties/SignalR.ScaleOut.nuspec @@ -9,7 +9,7 @@ false $description$ - + \ No newline at end of file From 84c08a357b32f58c5a69e9c18c9d4fdddf7434c9 Mon Sep 17 00:00:00 2001 From: Ducas Francis Date: Wed, 31 Aug 2011 13:55:53 +1000 Subject: [PATCH 0088/1128] fixed js bug - missing semicolons --- SignalR.Samples/Hubs/Chat/Chat.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SignalR.Samples/Hubs/Chat/Chat.js b/SignalR.Samples/Hubs/Chat/Chat.js index 7f392e3f7..1c1fa3644 100644 --- a/SignalR.Samples/Hubs/Chat/Chat.js +++ b/SignalR.Samples/Hubs/Chat/Chat.js @@ -41,7 +41,7 @@ $(function () { chat.showRooms = function (rooms) { if (!rooms.length) { - addMessage('No rooms available', 'notification') + addMessage('No rooms available', 'notification'); } else { $.each(rooms, function () { @@ -182,7 +182,7 @@ $(function () { chat.join() .done(function (success) { if (success === false) { - $.cookie('userid', '') + $.cookie('userid', ''); addMessage('Choose a name using "/nick nickname".', 'notification'); } addMessage('After that, you can view rooms using "/rooms" and join a room using "/join roomname".', 'notification'); From 3be48436c278440c91d6e262feb175f01f986f3f Mon Sep 17 00:00:00 2001 From: Ducas Francis Date: Wed, 31 Aug 2011 14:47:08 +1000 Subject: [PATCH 0089/1128] added jquery mobile and used it to enhance chat in mobile.htm --- .../Content/images/ajax-loader.png | Bin 0 -> 503 bytes .../Content/images/icon-search-black.png | Bin 0 -> 1245 bytes .../Content/images/icons-18-black.png | Bin 0 -> 2064 bytes .../Content/images/icons-18-white.png | Bin 0 -> 2200 bytes .../Content/images/icons-36-black.png | Bin 0 -> 3403 bytes .../Content/images/icons-36-white.png | Bin 0 -> 4020 bytes .../Content/jquery.mobile-1.0b2.css | 1640 +++++ .../Content/jquery.mobile-1.0b2.min.css | 8 + SignalR.Samples/Hubs/Chat/Chat.js | 17 + SignalR.Samples/Hubs/Chat/Mobile.css | 23 + SignalR.Samples/Hubs/Chat/mobile.htm | 53 + .../Scripts/jquery.mobile-1.0b2.js | 6259 +++++++++++++++++ .../Scripts/jquery.mobile-1.0b2.min.js | 155 + SignalR.Samples/SignalR.Samples.csproj | 2 + SignalR.Samples/packages.config | 17 +- 15 files changed, 8166 insertions(+), 8 deletions(-) create mode 100644 SignalR.Samples/Content/images/ajax-loader.png create mode 100644 SignalR.Samples/Content/images/icon-search-black.png create mode 100644 SignalR.Samples/Content/images/icons-18-black.png create mode 100644 SignalR.Samples/Content/images/icons-18-white.png create mode 100644 SignalR.Samples/Content/images/icons-36-black.png create mode 100644 SignalR.Samples/Content/images/icons-36-white.png create mode 100644 SignalR.Samples/Content/jquery.mobile-1.0b2.css create mode 100644 SignalR.Samples/Content/jquery.mobile-1.0b2.min.css create mode 100644 SignalR.Samples/Hubs/Chat/Mobile.css create mode 100644 SignalR.Samples/Hubs/Chat/mobile.htm create mode 100644 SignalR.Samples/Scripts/jquery.mobile-1.0b2.js create mode 100644 SignalR.Samples/Scripts/jquery.mobile-1.0b2.min.js diff --git a/SignalR.Samples/Content/images/ajax-loader.png b/SignalR.Samples/Content/images/ajax-loader.png new file mode 100644 index 0000000000000000000000000000000000000000..811a2cdd1b492c47faf85c1206ad6606521eb6e4 GIT binary patch literal 503 zcmeAS@N?(olHy`uVBq!ia0vp^${@_b3?$!tUzY`>n2Vh}LpV4%Za?&Y0OT43_=LFr z|Ns9ZDd7Dck0hX8fs!DgJ6C3d)qYMwA5Sr=f?F`h}-_x&BOOZ@XQnE}c<_xEZu zoIX1zlA-v_?sD-1ZfECkmVe(>o?xwNmGDbK;?2MR41rRzXA2bX{on66#ob*wrsMPf z`i=nRm=4)s4z4?=xi7SyIo*As^~h=O3#~U!U4D?`wnG2XC7uMQrY5F@nOn9v%usT4 zc-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxKsVXI%s|1+P|wiV z#N6CmN5ROz&_Lh7NZ-&%*U;R`*vQJjKmiJrfVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8EkR}&8R-I5=oVMzl_XZ^<`pZ$OmImpPAEg{v+u2}(t{7puX=A(aKG z`a!A1`K3k4z=%sz23b{L-^Aq1JP;qO z-q+X4Gq1QLF)umQ)5TT^Xo6m5W{Q=$frY8Dg{g^?frWvgp^KZNxsipFqp^##k(sHh zxvLpWuSMv>2~2MaLa!-Ky`ZF!TL84#CABECEH%ZgC_h&L>}9J=+-@<#X&zK> z3U0R;~ffC;SG&XwqMVf^ch~>^Yvx!tm1jqY457ni}SVjYVq%taI-#D(4G9|(WxaX8>%?0 zEnMtZ3AiXPE9g4;pNVTjo8X=Vv9Bv!*uMofb@+W(Ul1LCXEFch9S5!T3|b`D#=Epi z8!X$`Q#^T}SV3DB&kn}5KMpmXj4}KwX0EWLVYv=t|Er^U4l+M8P6%CCB3I7rXTtng z(ro%`J;}32*E3kFzD&w~E`3 zu1)(le}&!cgTHxZ+{t1VE?exk>B-DH*~0fXyl=eG&%n&E;IvbH^`pPBpaRF!)z4*} HQ$iB}ksr6J literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/images/icons-18-black.png b/SignalR.Samples/Content/images/icons-18-black.png new file mode 100644 index 0000000000000000000000000000000000000000..1ecfd26fbcd9708e4bd9d3a583dcd703461c766b GIT binary patch literal 2064 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 literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/images/icons-36-black.png b/SignalR.Samples/Content/images/icons-36-black.png new file mode 100644 index 0000000000000000000000000000000000000000..4c72adf1e81039a59ae8ab74cdeb6148c67acfbb GIT binary patch literal 3403 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 literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/images/icons-36-white.png b/SignalR.Samples/Content/images/icons-36-white.png new file mode 100644 index 0000000000000000000000000000000000000000..84ea9fb31ae442d0c60523fbce08ec5f202b7d9c GIT binary patch literal 4020 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 O16?#i8CL6~qyGU`IBHY? literal 0 HcmV?d00001 diff --git a/SignalR.Samples/Content/jquery.mobile-1.0b2.css b/SignalR.Samples/Content/jquery.mobile-1.0b2.css new file mode 100644 index 000000000..5685cd59a --- /dev/null +++ b/SignalR.Samples/Content/jquery.mobile-1.0b2.css @@ -0,0 +1,1640 @@ +/*! + * jQuery Mobile v1.0b2 + * http://jquerymobile.com/ + * + * Copyright 2010, jQuery Project + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + */ +/* +* jQuery Mobile Framework +* Copyright (c) jQuery Project +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. +*/ + + +/* A +-----------------------------------------------------------------------------------------------------------*/ + +.ui-bar-a { + border: 1px solid #2A2A2A; + background: #111111; + color: #ffffff; + font-weight: bold; + text-shadow: 0 -1px 1px #000000; + background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#111)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #3c3c3c, #111); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #3c3c3c, #111); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #3c3c3c, #111); /* IE10 */ + background-image: -o-linear-gradient(top, #3c3c3c, #111); /* Opera 11.10+ */ + background-image: linear-gradient(top, #3c3c3c, #111); +} +.ui-bar-a, +.ui-bar-a input, +.ui-bar-a select, +.ui-bar-a textarea, +.ui-bar-a button { + font-family: Helvetica, Arial, sans-serif; +} +.ui-bar-a .ui-link-inherit { + color: #fff; +} +.ui-bar-a .ui-link { + color: #7cc4e7; + font-weight: bold; +} +.ui-body-a { + border: 1px solid #2A2A2A; + background: #222222; + color: #fff; + text-shadow: 0 1px 0 #000; + font-weight: normal; + background-image: -webkit-gradient(linear, left top, left bottom, from(#666), to(#222)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #666, #222); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #666, #222); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #666, #222); /* IE10 */ + background-image: -o-linear-gradient(top, #666, #222); /* Opera 11.10+ */ + background-image: linear-gradient(top, #666, #222); +} +.ui-body-a, +.ui-body-a input, +.ui-body-a select, +.ui-body-a textarea, +.ui-body-a button { + font-family: Helvetica, Arial, sans-serif; +} +.ui-body-a .ui-link-inherit { + color: #fff; +} +.ui-body-a .ui-link { + color: #2489CE; + font-weight: bold; +} +.ui-br { + border-bottom: rgb(130,130,130); + border-bottom: rgba(130,130,130,.3); + border-bottom-width: 1px; + border-bottom-style: solid; +} +.ui-btn-up-a { + border: 1px solid #222; + background: #333333; + font-weight: bold; + color: #fff; + text-shadow: 0 -1px 1px #000; + background-image: -webkit-gradient(linear, left top, left bottom, from(#555), to(#333)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #555, #333); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #555, #333); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #555, #333); /* IE10 */ + background-image: -o-linear-gradient(top, #555, #333); /* Opera 11.10+ */ + background-image: linear-gradient(top, #555, #333); +} +.ui-btn-up-a a.ui-link-inherit { + color: #fff; +} +.ui-btn-hover-a { + border: 1px solid #000; + background: #444444; + font-weight: bold; + color: #fff; + text-shadow: 0 -1px 1px #000; + background-image: -webkit-gradient(linear, left top, left bottom, from(#666), to(#444)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #666, #444); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #666, #444); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #666, #444); /* IE10 */ + background-image: -o-linear-gradient(top, #666, #444); /* Opera 11.10+ */ + background-image: linear-gradient(top, #666, #444); +} +.ui-btn-hover-a a.ui-link-inherit { + color: #fff; +} +.ui-btn-down-a { + border: 1px solid #000; + background: #3d3d3d; + font-weight: bold; + color: #fff; + text-shadow: 0 -1px 1px #000; + background-image: -webkit-gradient(linear, left top, left bottom, from(#333), to(#5a5a5a)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #333, #5a5a5a); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #333, #5a5a5a); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #333, #5a5a5a); /* IE10 */ + background-image: -o-linear-gradient(top, #333, #5a5a5a); /* Opera 11.10+ */ + background-image: linear-gradient(top, #333, #5a5a5a); +} +.ui-btn-down-a a.ui-link-inherit { + color: #fff; +} +.ui-btn-up-a, +.ui-btn-hover-a, +.ui-btn-down-a { + font-family: Helvetica, Arial, sans-serif; + text-decoration: none; +} + + +/* B +-----------------------------------------------------------------------------------------------------------*/ + +.ui-bar-b { + border: 1px solid #456f9a; + background: #5e87b0; + color: #fff; + font-weight: bold; + text-shadow: 0 -1px 1px #254f7a; + background-image: -webkit-gradient(linear, left top, left bottom, from(#81a8ce), to(#5e87b0)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #81a8ce, #5e87b0); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #81a8ce, #5e87b0); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #81a8ce, #5e87b0); /* IE10 */ + background-image: -o-linear-gradient(top, #81a8ce, #5e87b0); /* Opera 11.10+ */ + background-image: linear-gradient(top, #81a8ce, #5e87b0); +} +.ui-bar-b, +.ui-bar-b input, +.ui-bar-b select, +.ui-bar-b textarea, +.ui-bar-b button { + font-family: Helvetica, Arial, sans-serif; +} +.ui-bar-b .ui-link-inherit { + color: #fff; +} +.ui-bar-b .ui-link { + color: #7cc4e7; + font-weight: bold; +} + +.ui-body-b { + border: 1px solid #C6C6C6; + background: #cccccc; + color: #333333; + text-shadow: 0 1px 0 #fff; + font-weight: normal; + background-image: -webkit-gradient(linear, left top, left bottom, from(#e6e6e6), to(#ccc)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #e6e6e6, #ccc); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #e6e6e6, #ccc); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #e6e6e6, #ccc); /* IE10 */ + background-image: -o-linear-gradient(top, #e6e6e6, #ccc); /* Opera 11.10+ */ + background-image: linear-gradient(top, #e6e6e6, #ccc); +} +.ui-body-b, +.ui-body-b input, +.ui-body-b select, +.ui-body-b textarea, +.ui-body-b button { + font-family: Helvetica, Arial, sans-serif; +} +.ui-body-b .ui-link-inherit { + color: #333333; +} +.ui-body-b .ui-link { + color: #2489CE; + font-weight: bold; +} +.ui-btn-up-b { + border: 1px solid #145072; + background: #2567ab; + font-weight: bold; + color: #fff; + text-shadow: 0 -1px 1px #145072; + background-image: -webkit-gradient(linear, left top, left bottom, from(#5f9cc5), to(#396b9e)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #5f9cc5, #396b9e); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #5f9cc5, #396b9e); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #5f9cc5, #396b9e); /* IE10 */ + background-image: -o-linear-gradient(top, #5f9cc5, #396b9e); /* Opera 11.10+ */ + background-image: linear-gradient(top, #5f9cc5, #396b9e); +} +.ui-btn-up-b a.ui-link-inherit { + color: #fff; +} +.ui-btn-hover-b { + border: 1px solid #00516e; + background: #4b88b6; + font-weight: bold; + color: #fff; + text-shadow: 0 -1px 1px #014D68; + background-image: -webkit-gradient(linear, left top, left bottom, from(#72b0d4), to(#4b88b6)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #72b0d4, #4b88b6); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #72b0d4, #4b88b6); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #72b0d4, #4b88b6); /* IE10 */ + background-image: -o-linear-gradient(top, #72b0d4, #4b88b6); /* Opera 11.10+ */ + background-image: linear-gradient(top, #72b0d4, #4b88b6); +} +.ui-btn-hover-b a.ui-link-inherit { + color: #fff; +} +.ui-btn-down-b { + border: 1px solid #225377; + background: #4e89c5; + font-weight: bold; + color: #fff; + text-shadow: 0 -1px 1px #225377; + background-image: -webkit-gradient(linear, left top, left bottom, from(#396b9e), to(#4e89c5)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #396b9e, #4e89c5); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #396b9e, #4e89c5); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #396b9e, #4e89c5); /* IE10 */ + background-image: -o-linear-gradient(top, #396b9e, #4e89c5); /* Opera 11.10+ */ + background-image: linear-gradient(top, #396b9e, #4e89c5); +} +.ui-btn-down-b a.ui-link-inherit { + color: #fff; +} +.ui-btn-up-b, +.ui-btn-hover-b, +.ui-btn-down-b { + font-family: Helvetica, Arial, sans-serif; + text-decoration: none; +} + + +/* C +-----------------------------------------------------------------------------------------------------------*/ + +.ui-bar-c { + border: 1px solid #B3B3B3; + background: #e9eaeb; + color: #3E3E3E; + font-weight: bold; + text-shadow: 0 1px 1px #fff; + background-image: -webkit-gradient(linear, left top, left bottom, from(#f0f0f0), to(#e9eaeb)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #f0f0f0, #e9eaeb); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #f0f0f0, #e9eaeb); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #f0f0f0, #e9eaeb); /* IE10 */ + background-image: -o-linear-gradient(top, #f0f0f0, #e9eaeb); /* Opera 11.10+ */ + background-image: linear-gradient(top, #f0f0f0, #e9eaeb); +} +.ui-bar-c, +.ui-bar-c input, +.ui-bar-c select, +.ui-bar-c textarea, +.ui-bar-c button { + font-family: Helvetica, Arial, sans-serif; +} +.ui-body-c { + border: 1px solid #B3B3B3; + color: #333333; + text-shadow: 0 1px 0 #fff; + background: #f0f0f0; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#ddd)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #eee, #ddd); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #eee, #ddd); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #eee, #ddd); /* IE10 */ + background-image: -o-linear-gradient(top, #eee, #ddd); /* Opera 11.10+ */ + background-image: linear-gradient(top, #eee, #ddd); +} +.ui-body-c, +.ui-body-c input, +.ui-body-c select, +.ui-body-c textarea, +.ui-body-c button { + font-family: Helvetica, Arial, sans-serif; +} +.ui-body-c .ui-link-inherit { + color: #333333; +} +.ui-body-c .ui-link { + color: #2489CE; + font-weight: bold; +} + +.ui-btn-up-c { + border: 1px solid #ccc; + background: #eee; + font-weight: bold; + color: #444; + text-shadow: 0 1px 1px #f6f6f6; + background-image: -webkit-gradient(linear, left top, left bottom, from(#fdfdfd), to(#eee)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #fdfdfd, #eee); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #fdfdfd, #eee); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #fdfdfd, #eee); /* IE10 */ + background-image: -o-linear-gradient(top, #fdfdfd, #eee); /* Opera 11.10+ */ + background-image: linear-gradient(top, #fdfdfd, #eee); +} +.ui-btn-up-c a.ui-link-inherit { + color: #2F3E46; +} + +.ui-btn-hover-c { + border: 1px solid #bbb; + background: #dadada; + font-weight: bold; + color: #101010; + text-shadow: 0 1px 1px #fff; + background-image: -webkit-gradient(linear, left top, left bottom, from(#ededed), to(#dadada)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #ededed, #dadada); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #ededed, #dadada); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #ededed, #dadada); /* IE10 */ + background-image: -o-linear-gradient(top, #ededed, #dadada); /* Opera 11.10+ */ + background-image: linear-gradient(top, #ededed, #dadada); +} +.ui-btn-hover-c a.ui-link-inherit { + color: #2F3E46; +} +.ui-btn-down-c { + border: 1px solid #808080; + background: #fdfdfd; + font-weight: bold; + color: #111111; + text-shadow: 0 1px 1px #ffffff; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#fdfdfd)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #eee, #fdfdfd); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #eee, #fdfdfd); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #eee, #fdfdfd); /* IE10 */ + background-image: -o-linear-gradient(top, #eee, #fdfdfd); /* Opera 11.10+ */ + background-image: linear-gradient(top, #eee, #fdfdfd); +} +.ui-btn-down-c a.ui-link-inherit { + color: #2F3E46; +} +.ui-btn-up-c, +.ui-btn-hover-c, +.ui-btn-down-c { + font-family: Helvetica, Arial, sans-serif; + text-decoration: none; +} + + +/* D +-----------------------------------------------------------------------------------------------------------*/ + +.ui-bar-d { + border: 1px solid #ccc; + background: #bbb; + color: #333; + text-shadow: 0 1px 0 #eee; + background-image: -webkit-gradient(linear, left top, left bottom, from(#ddd), to(#bbb)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #ddd, #bbb); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #ddd, #bbb); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #ddd, #bbb); /* IE10 */ + background-image: -o-linear-gradient(top, #ddd, #bbb); /* Opera 11.10+ */ + background-image: linear-gradient(top, #ddd, #bbb); +} +.ui-bar-d, +.ui-bar-d input, +.ui-bar-d select, +.ui-bar-d textarea, +.ui-bar-d button { + font-family: Helvetica, Arial, sans-serif; +} +.ui-bar-d .ui-link-inherit { + color: #333; +} +.ui-bar-d .ui-link { + color: #2489CE; + font-weight: bold; +} +.ui-body-d { + border: 1px solid #ccc; + color: #333333; + text-shadow: 0 1px 0 #fff; + background: #ffffff; +} +.ui-body-d, +.ui-body-d input, +.ui-body-d select, +.ui-body-d textarea, +.ui-body-d button { + font-family: Helvetica, Arial, sans-serif; +} +.ui-body-d .ui-link-inherit { + color: #333333; +} +.ui-body-d .ui-link { + color: #2489CE; + font-weight: bold; +} +.ui-btn-up-d { + border: 1px solid #ccc; + background: #fff; + font-weight: bold; + color: #444; + text-shadow: 0 1px 1px #fff; +} +.ui-btn-up-d a.ui-link-inherit { + color: #333; +} +.ui-btn-hover-d { + border: 1px solid #aaa; + background: #eeeeee; + font-weight: bold; + color: #222; + cursor: pointer; + text-shadow: 0 1px 1px #fff; + background-image: -webkit-gradient(linear, left top, left bottom, from(#fdfdfd), to(#eee)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #fdfdfd, #eee); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #fdfdfd, #eee); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #fdfdfd, #eee); /* IE10 */ + background-image: -o-linear-gradient(top, #fdfdfd, #eee); /* Opera 11.10+ */ + background-image: linear-gradient(top, #fdfdfd, #eee); +} +.ui-btn-hover-d a.ui-link-inherit { + color: #222; +} +.ui-btn-down-d { + border: 1px solid #aaaaaa; + background: #ffffff; + font-weight: bold; + color: #111; + text-shadow: 0 1px 1px #ffffff; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#fff)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #eee, #fff); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #eee, #fff); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #eee, #fff); /* IE10 */ + background-image: -o-linear-gradient(top, #eee, #fff); /* Opera 11.10+ */ + background-image: linear-gradient(top, #eee, #fff); +} +.ui-btn-down-d a.ui-link-inherit { + color: #111; +} +.ui-btn-up-d, +.ui-btn-hover-d, +.ui-btn-down-d { + font-family: Helvetica, Arial, sans-serif; + text-decoration: none; +} + + +/* E +-----------------------------------------------------------------------------------------------------------*/ + +.ui-bar-e { + border: 1px solid #F7C942; + background: #fadb4e; + color: #333; + text-shadow: 0 1px 0 #fff; + background-image: -webkit-gradient(linear, left top, left bottom, from(#fceda7), to(#fadb4e)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #fceda7, #fadb4e); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #fceda7, #fadb4e); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #fceda7, #fadb4e); /* IE10 */ + background-image: -o-linear-gradient(top, #fceda7, #fadb4e); /* Opera 11.10+ */ + background-image: linear-gradient(top, #fceda7, #fadb4e); +} +.ui-bar-e, +.ui-bar-e input, +.ui-bar-e select, +.ui-bar-e textarea, +.ui-bar-e button { + font-family: Helvetica, Arial, sans-serif; +} +.ui-bar-e .ui-link-inherit { + color: #333; +} +.ui-bar-e .ui-link { + color: #2489CE; + font-weight: bold; +} +.ui-body-e { + border: 1px solid #F7C942; + color: #333333; + text-shadow: 0 1px 0 #fff; + background: #faeb9e; + background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#faeb9e)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #fff, #faeb9e); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #fff, #faeb9e); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #fff, #faeb9e); /* IE10 */ + background-image: -o-linear-gradient(top, #fff, #faeb9e); /* Opera 11.10+ */ + background-image: linear-gradient(top, #fff, #faeb9e); +} +.ui-body-e, +.ui-body-e input, +.ui-body-e select, +.ui-body-e textarea, +.ui-body-e button { + font-family: Helvetica, Arial, sans-serif; +} +.ui-body-e .ui-link-inherit { + color: #333333; +} +.ui-body-e .ui-link { + color: #2489CE; + font-weight: bold; +} +.ui-btn-up-e { + border: 1px solid #F7C942; + background: #fadb4e; + font-weight: bold; + color: #333; + text-shadow: 0 1px 0 #fff; + background-image: -webkit-gradient(linear, left top, left bottom, from(#fceda7), to(#fadb4e)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #fceda7, #fadb4e); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #fceda7, #fadb4e); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #fceda7, #fadb4e); /* IE10 */ + background-image: -o-linear-gradient(top, #fceda7, #fadb4e); /* Opera 11.10+ */ + background-image: linear-gradient(top, #fceda7, #fadb4e); +} +.ui-btn-up-e a.ui-link-inherit { + color: #333; +} +.ui-btn-hover-e { + border: 1px solid #e79952; + background: #fbe26f; + font-weight: bold; + color: #111; + text-shadow: 0 1px 1px #fff; + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf0b5), to(#fbe26f)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #fcf0b5, #fbe26f); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #fcf0b5, #fbe26f); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #fcf0b5, #fbe26f); /* IE10 */ + background-image: -o-linear-gradient(top, #fcf0b5, #fbe26f); /* Opera 11.10+ */ + background-image: linear-gradient(top, #fcf0b5, #fbe26f); +} + +.ui-btn-hover-e a.ui-link-inherit { + color: #333; +} +.ui-btn-down-e { + border: 1px solid #F7C942; + background: #fceda7; + font-weight: bold; + color: #111; + text-shadow: 0 1px 1px #ffffff; + background-image: -webkit-gradient(linear, left top, left bottom, from(#fadb4e), to(#fceda7)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #fadb4e, #fceda7); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #fadb4e, #fceda7); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #fadb4e, #fceda7); /* IE10 */ + background-image: -o-linear-gradient(top, #fadb4e, #fceda7); /* Opera 11.10+ */ + background-image: linear-gradient(top, #fadb4e, #fceda7); +} +.ui-btn-down-e a.ui-link-inherit { + color: #333; +} +.ui-btn-up-e, +.ui-btn-hover-e, +.ui-btn-down-e { + font-family: Helvetica, Arial, sans-serif; + text-decoration: none; +} + + +/* links within "buttons" +-----------------------------------------------------------------------------------------------------------*/ + +a.ui-link-inherit { + text-decoration: none !important; +} + + +/* Active class used as the "on" state across all themes +-----------------------------------------------------------------------------------------------------------*/ + +.ui-btn-active { + border: 1px solid #155678; + background: #4596ce; + font-weight: bold; + color: #fff; + cursor: pointer; + text-shadow: 0 -1px 1px #145072; + text-decoration: none; + background-image: -webkit-gradient(linear, left top, left bottom, from(#85bae4), to(#5393c5)); /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, #85bae4, #5393c5); /* Chrome 10+, Saf5.1+ */ + background-image: -moz-linear-gradient(top, #85bae4, #5393c5); /* FF3.6 */ + background-image: -ms-linear-gradient(top, #85bae4, #5393c5); /* IE10 */ + background-image: -o-linear-gradient(top, #85bae4, #5393c5); /* Opera 11.10+ */ + background-image: linear-gradient(top, #85bae4, #5393c5); + outline: none; +} +.ui-btn-active a.ui-link-inherit { + color: #fff; +} + + +/* button inner top highlight +-----------------------------------------------------------------------------------------------------------*/ + +.ui-btn-inner { + border-top: 1px solid #fff; + border-color: rgba(255,255,255,.3); +} + + +/* corner rounding classes +-----------------------------------------------------------------------------------------------------------*/ + +.ui-corner-tl { + -moz-border-radius-topleft: .6em; + -webkit-border-top-left-radius: .6em; + border-top-left-radius: .6em; +} +.ui-corner-tr { + -moz-border-radius-topright: .6em; + -webkit-border-top-right-radius: .6em; + border-top-right-radius: .6em; +} +.ui-corner-bl { + -moz-border-radius-bottomleft: .6em; + -webkit-border-bottom-left-radius: .6em; + border-bottom-left-radius: .6em; +} +.ui-corner-br { + -moz-border-radius-bottomright: .6em; + -webkit-border-bottom-right-radius: .6em; + border-bottom-right-radius: .6em; +} +.ui-corner-top { + -moz-border-radius-topleft: .6em; + -webkit-border-top-left-radius: .6em; + border-top-left-radius: .6em; + -moz-border-radius-topright: .6em; + -webkit-border-top-right-radius: .6em; + border-top-right-radius: .6em; +} +.ui-corner-bottom { + -moz-border-radius-bottomleft: .6em; + -webkit-border-bottom-left-radius: .6em; + border-bottom-left-radius: .6em; + -moz-border-radius-bottomright: .6em; + -webkit-border-bottom-right-radius: .6em; + border-bottom-right-radius: .6em; + } +.ui-corner-right { + -moz-border-radius-topright: .6em; + -webkit-border-top-right-radius: .6em; + border-top-right-radius: .6em; + -moz-border-radius-bottomright: .6em; + -webkit-border-bottom-right-radius: .6em; + border-bottom-right-radius: .6em; +} +.ui-corner-left { + -moz-border-radius-topleft: .6em; + -webkit-border-top-left-radius: .6em; + border-top-left-radius: .6em; + -moz-border-radius-bottomleft: .6em; + -webkit-border-bottom-left-radius: .6em; + border-bottom-left-radius: .6em; +} +.ui-corner-all { + -moz-border-radius: .6em; + -webkit-border-radius: .6em; + border-radius: .6em; +} + + + +/* Interaction cues +-----------------------------------------------------------------------------------------------------------*/ +.ui-disabled { + opacity: .3; +} +.ui-disabled, +.ui-disabled a { + cursor: default; +} + +/* Icons +-----------------------------------------------------------------------------------------------------------*/ + +.ui-icon { + background: #666; + background: rgba(0,0,0,.4); + background-image: url(images/icons-18-white.png); + background-repeat: no-repeat; + -moz-border-radius: 9px; + -webkit-border-radius: 9px; + border-radius: 9px; +} + + +/* Alt icon color +-----------------------------------------------------------------------------------------------------------*/ + +.ui-icon-alt { + background: #fff; + background: rgba(255,255,255,.3); + background-image: url(images/icons-18-black.png); + background-repeat: no-repeat; +} + +/* HD/"retina" sprite +-----------------------------------------------------------------------------------------------------------*/ + +@media only screen and (-webkit-min-device-pixel-ratio: 1.5), + only screen and (min--moz-device-pixel-ratio: 1.5), + only screen and (min-resolution: 240dpi) { + + .ui-icon-plus, .ui-icon-minus, .ui-icon-delete, .ui-icon-arrow-r, + .ui-icon-arrow-l, .ui-icon-arrow-u, .ui-icon-arrow-d, .ui-icon-check, + .ui-icon-gear, .ui-icon-refresh, .ui-icon-forward, .ui-icon-back, + .ui-icon-grid, .ui-icon-star, .ui-icon-alert, .ui-icon-info, .ui-icon-home, .ui-icon-search, + .ui-icon-checkbox-off, .ui-icon-checkbox-on, .ui-icon-radio-off, .ui-icon-radio-on { + background-image: url(images/icons-36-white.png); + -moz-background-size: 776px 18px; + -o-background-size: 776px 18px; + -webkit-background-size: 776px 18px; + background-size: 776px 18px; + } + .ui-icon-alt { + background-image: url(images/icons-36-black.png); + } +} + +/* plus minus */ +.ui-icon-plus { + background-position: -0 50%; +} +.ui-icon-minus { + background-position: -36px 50%; +} + +/* delete/close */ +.ui-icon-delete { + background-position: -72px 50%; +} + +/* arrows */ +.ui-icon-arrow-r { + background-position: -108px 50%; +} +.ui-icon-arrow-l { + background-position: -144px 50%; +} +.ui-icon-arrow-u { + background-position: -180px 50%; +} +.ui-icon-arrow-d { + background-position: -216px 50%; +} + +/* misc */ +.ui-icon-check { + background-position: -252px 50%; +} +.ui-icon-gear { + background-position: -288px 50%; +} +.ui-icon-refresh { + background-position: -324px 50%; +} +.ui-icon-forward { + background-position: -360px 50%; +} +.ui-icon-back { + background-position: -396px 50%; +} +.ui-icon-grid { + background-position: -432px 50%; +} +.ui-icon-star { + background-position: -468px 50%; +} +.ui-icon-alert { + background-position: -504px 50%; +} +.ui-icon-info { + background-position: -540px 50%; +} +.ui-icon-home { + background-position: -576px 50%; +} +.ui-icon-search { + background-position: -612px 50%; +} +.ui-icon-checkbox-off { + background-position: -684px 50%; +} +.ui-icon-checkbox-on { + background-position: -648px 50%; +} +.ui-icon-radio-off { + background-position: -756px 50%; +} +.ui-icon-radio-on { + background-position: -720px 50%; +} + + +/* checks,radios */ +.ui-checkbox .ui-icon { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; +} +.ui-icon-checkbox-off, +.ui-icon-radio-off { + background-color: transparent; +} +.ui-checkbox-on .ui-icon, +.ui-radio-on .ui-icon { + background-color: #4596ce; /* NOTE: this hex should match the active state color. It's repeated here for cascade */ +} +.ui-icon-searchfield { + background-image: url(images/icon-search-black.png); + background-size: 16px 16px; +} + +/* loading icon */ +.ui-icon-loading { + background-image: url(images/ajax-loader.png); + width: 40px; + height: 40px; + -moz-border-radius: 20px; + -webkit-border-radius: 20px; + border-radius: 20px; + background-size: 35px 35px; +} + + +/* Button corner classes +-----------------------------------------------------------------------------------------------------------*/ + +.ui-btn-corner-tl { + -moz-border-radius-topleft: 1em; + -webkit-border-top-left-radius: 1em; + border-top-left-radius: 1em; +} +.ui-btn-corner-tr { + -moz-border-radius-topright: 1em; + -webkit-border-top-right-radius: 1em; + border-top-right-radius: 1em; +} +.ui-btn-corner-bl { + -moz-border-radius-bottomleft: 1em; + -webkit-border-bottom-left-radius: 1em; + border-bottom-left-radius: 1em; +} +.ui-btn-corner-br { + -moz-border-radius-bottomright: 1em; + -webkit-border-bottom-right-radius: 1em; + border-bottom-right-radius: 1em; +} +.ui-btn-corner-top { + -moz-border-radius-topleft: 1em; + -webkit-border-top-left-radius: 1em; + border-top-left-radius: 1em; + -moz-border-radius-topright: 1em; + -webkit-border-top-right-radius: 1em; + border-top-right-radius: 1em; +} +.ui-btn-corner-bottom { + -moz-border-radius-bottomleft: 1em; + -webkit-border-bottom-left-radius: 1em; + border-bottom-left-radius: 1em; + -moz-border-radius-bottomright: 1em; + -webkit-border-bottom-right-radius: 1em; + border-bottom-right-radius: 1em; +} +.ui-btn-corner-right { + -moz-border-radius-topright: 1em; + -webkit-border-top-right-radius: 1em; + border-top-right-radius: 1em; + -moz-border-radius-bottomright: 1em; + -webkit-border-bottom-right-radius: 1em; + border-bottom-right-radius: 1em; +} +.ui-btn-corner-left { + -moz-border-radius-topleft: 1em; + -webkit-border-top-left-radius: 1em; + border-top-left-radius: 1em; + -moz-border-radius-bottomleft: 1em; + -webkit-border-bottom-left-radius: 1em; + border-bottom-left-radius: 1em; +} +.ui-btn-corner-all { + -moz-border-radius: 1em; + -webkit-border-radius: 1em; + border-radius: 1em; +} + +/* radius clip workaround for cleaning up corner trapping */ +.ui-corner-tl, +.ui-corner-tr, +.ui-corner-bl, +.ui-corner-br, +.ui-corner-top, +.ui-corner-bottom, +.ui-corner-right, +.ui-corner-left, +.ui-corner-all, +.ui-btn-corner-tl, +.ui-btn-corner-tr, +.ui-btn-corner-bl, +.ui-btn-corner-br, +.ui-btn-corner-top, +.ui-btn-corner-bottom, +.ui-btn-corner-right, +.ui-btn-corner-left, +.ui-btn-corner-all { + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +} + +/* Overlay / modal +-----------------------------------------------------------------------------------------------------------*/ + +.ui-overlay { + background: #666; + opacity: .5; + filter: Alpha(Opacity=50); + position: absolute; + width: 100%; + height: 100%; +} +.ui-overlay-shadow { + -moz-box-shadow: 0px 0px 12px rgba(0,0,0,.6); + -webkit-box-shadow: 0px 0px 12px rgba(0,0,0,.6); + box-shadow: 0px 0px 12px rgba(0,0,0,.6); +} +.ui-shadow { + -moz-box-shadow: 0px 1px 4px rgba(0,0,0,.3); + -webkit-box-shadow: 0px 1px 4px rgba(0,0,0,.3); + box-shadow: 0px 1px 4px rgba(0,0,0,.3); +} +.ui-bar-a .ui-shadow, +.ui-bar-b .ui-shadow , +.ui-bar-c .ui-shadow { + -moz-box-shadow: 0px 1px 0 rgba(255,255,255,.3); + -webkit-box-shadow: 0px 1px 0 rgba(255,255,255,.3); + box-shadow: 0px 1px 0 rgba(255,255,255,.3); +} +.ui-shadow-inset { + -moz-box-shadow: inset 0px 1px 4px rgba(0,0,0,.2); + -webkit-box-shadow: inset 0px 1px 4px rgba(0,0,0,.2); + box-shadow: inset 0px 1px 4px rgba(0,0,0,.2); +} +.ui-icon-shadow { + -moz-box-shadow: 0px 1px 0 rgba(255,255,255,.4); + -webkit-box-shadow: 0px 1px 0 rgba(255,255,255,.4); + box-shadow: 0px 1px 0 rgba(255,255,255,.4); +} + + +/* Focus state - set here for specificity +-----------------------------------------------------------------------------------------------------------*/ + +.ui-focus { + -moz-box-shadow: 0px 0px 12px #387bbe; + -webkit-box-shadow: 0px 0px 12px #387bbe; + box-shadow: 0px 0px 12px #387bbe; +} + +/* unset box shadow in browsers that don't do it right +-----------------------------------------------------------------------------------------------------------*/ + +.ui-mobile-nosupport-boxshadow * { + -moz-box-shadow: none !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; +} + +/* ...and bring back focus */ +.ui-mobile-nosupport-boxshadow .ui-focus { + outline-width: 2px; +}/* +* jQuery Mobile Framework +* Copyright (c) jQuery Project +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. +*/ + +/* some unsets - more probably needed */ +.ui-mobile, .ui-mobile body { height: 100%; } +.ui-mobile fieldset, .ui-page { padding: 0; margin: 0; } +.ui-mobile a img, .ui-mobile fieldset { border: 0; } + +/* responsive page widths */ +.ui-mobile-viewport { margin: 0; overflow-x: hidden; -webkit-text-size-adjust: none; -ms-text-size-adjust:none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } + +/* "page" containers - full-screen views, one should always be in view post-pageload */ +.ui-mobile [data-role=page], .ui-mobile [data-role=dialog], .ui-page { top: 0; left: 0; width: 100%; min-height: 100%; position: absolute; display: none; border: 0; } +.ui-mobile .ui-page-active { display: block; overflow: visible; } + +/*orientations from js are available */ +.portrait, +.portrait .ui-page { min-height: 420px; } +.landscape, +.landscape .ui-page { min-height: 300px; } + +/* loading screen */ +.ui-loading .ui-mobile-viewport { overflow: hidden !important; } +.ui-loading .ui-loader { display: block; } +.ui-loading .ui-page { overflow: hidden; } +.ui-loader { display: none; position: absolute; opacity: .85; z-index: 100; left: 50%; width: 200px; margin-left: -130px; margin-top: -35px; padding: 10px 30px; } +.ui-loader h1 { font-size: 15px; text-align: center; } +.ui-loader .ui-icon { position: static; display: block; opacity: .9; margin: 0 auto; width: 35px; height: 35px; background-color: transparent; } + +/*fouc*/ +.ui-mobile-rendering > * { visibility: hidden; } + +/*headers, content panels*/ +.ui-bar, .ui-body { position: relative; padding: .4em 15px; overflow: hidden; display: block; clear:both; } +.ui-bar { font-size: 16px; margin: 0; } +.ui-bar h1, .ui-bar h2, .ui-bar h3, .ui-bar h4, .ui-bar h5, .ui-bar h6 { margin: 0; padding: 0; font-size: 16px; display: inline-block; } + +.ui-header, .ui-footer { display: block; } +.ui-page .ui-header, .ui-page .ui-footer { position: relative; } +.ui-header .ui-btn-left { position: absolute; left: 10px; top: .4em; } +.ui-header .ui-btn-right { position: absolute; right: 10px; top: .4em; } +.ui-header .ui-title, .ui-footer .ui-title { min-height: 1.1em; text-align: center; font-size: 16px; display: block; margin: .6em 90px .8em; padding: 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; outline: 0 !important; } + +/*content area*/ +.ui-content { border-width: 0; overflow: visible; overflow-x: hidden; padding: 15px; } +.ui-page-fullscreen .ui-content { padding:0; } + +/* icons sizing */ +.ui-icon { width: 18px; height: 18px; } + +/* fullscreen class on ui-content div */ +.ui-fullscreen { } +.ui-fullscreen img { max-width: 100%; } + +/* non-js content hiding */ +.ui-nojs { position: absolute; left: -9999px; } +/* +* jQuery Mobile Framework +* Copyright (c) jQuery Project +* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. +*/ +.spin { + -webkit-transform: rotate(360deg); + -webkit-animation-name: spin; + -webkit-animation-duration: 1s; + -webkit-animation-iteration-count: infinite; + -webkit-animation-timing-function: linear; +} +@-webkit-keyframes spin { + from {-webkit-transform: rotate(0deg);} + to {-webkit-transform: rotate(360deg);} +} + +/* Transitions from jQtouch (with small modifications): http://www.jqtouch.com/ +Built by David Kaneda and maintained by Jonathan Stark. +*/ +.in, .out { + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-duration: 350ms; +} + +.slide.in { + -webkit-transform: translateX(0); + -webkit-animation-name: slideinfromright; +} + +.slide.out { + -webkit-transform: translateX(-100%); + -webkit-animation-name: slideouttoleft; +} + +.slide.in.reverse { + -webkit-transform: translateX(0); + -webkit-animation-name: slideinfromleft; +} + +.slide.out.reverse { + -webkit-transform: translateX(100%); + -webkit-animation-name: slideouttoright; +} + +.slideup.in { + -webkit-transform: translateY(0); + -webkit-animation-name: slideinfrombottom; + z-index: 10; +} + +.slideup.out { + -webkit-animation-name: dontmove; + z-index: 0; +} + +.slideup.out.reverse { + -webkit-transform: translateY(100%); + z-index: 10; + -webkit-animation-name: slideouttobottom; +} + +.slideup.in.reverse { + z-index: 0; + -webkit-animation-name: dontmove; +} +.slidedown.in { + -webkit-transform: translateY(0); + -webkit-animation-name: slideinfromtop; + z-index: 10; +} + +.slidedown.out { + -webkit-animation-name: dontmove; + z-index: 0; +} + +.slidedown.out.reverse { + -webkit-transform: translateY(-100%); + z-index: 10; + -webkit-animation-name: slideouttotop; +} + +.slidedown.in.reverse { + z-index: 0; + -webkit-animation-name: dontmove; +} + +@-webkit-keyframes slideinfromright { + from { -webkit-transform: translateX(100%); } + to { -webkit-transform: translateX(0); } +} + +@-webkit-keyframes slideinfromleft { + from { -webkit-transform: translateX(-100%); } + to { -webkit-transform: translateX(0); } +} + +@-webkit-keyframes slideouttoleft { + from { -webkit-transform: translateX(0); } + to { -webkit-transform: translateX(-100%); } +} + +@-webkit-keyframes slideouttoright { + from { -webkit-transform: translateX(0); } + to { -webkit-transform: translateX(100%); } +} + + +@-webkit-keyframes slideinfromtop { + from { -webkit-transform: translateY(-100%); } + to { -webkit-transform: translateY(0); } +} + +@-webkit-keyframes slideinfrombottom { + from { -webkit-transform: translateY(100%); } + to { -webkit-transform: translateY(0); } +} + +@-webkit-keyframes slideouttobottom { + from { -webkit-transform: translateY(0); } + to { -webkit-transform: translateY(100%); } +} + +@-webkit-keyframes slideouttotop { + from { -webkit-transform: translateY(0); } + to { -webkit-transform: translateY(-100%); } +} +@-webkit-keyframes fadein { + from { opacity: 0; } + to { opacity: 1; } +} + +@-webkit-keyframes fadeout { + from { opacity: 1; } + to { opacity: 0; } +} + +.fade.in { + opacity: 1; + z-index: 10; + -webkit-animation-name: fadein; +} +.fade.out { + z-index: 0; + -webkit-animation-name: fadeout; +} + +/* The properties in this rule are only necessary for the 'flip' transition. + * We need specify the perspective to create a projection matrix. This will add + * some depth as the element flips. The depth number represents the distance of + * the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate + * value. + */ +.viewport-flip { + -webkit-perspective: 1000; + position: absolute; +} + +.ui-mobile-viewport-transitioning, +.ui-mobile-viewport-transitioning .ui-page { + width: 100%; + height: 100%; + overflow: hidden; +} + +.flip { + -webkit-animation-duration: .65s; + -webkit-backface-visibility:hidden; + -webkit-transform:translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */ +} + +.flip.in { + -webkit-transform: rotateY(0) scale(1); + -webkit-animation-name: flipinfromleft; +} + +.flip.out { + -webkit-transform: rotateY(-180deg) scale(.8); + -webkit-animation-name: flipouttoleft; +} + +/* Shake it all about */ + +.flip.in.reverse { + -webkit-transform: rotateY(0) scale(1); + -webkit-animation-name: flipinfromright; +} + +.flip.out.reverse { + -webkit-transform: rotateY(180deg) scale(.8); + -webkit-animation-name: flipouttoright; +} + +@-webkit-keyframes flipinfromright { + from { -webkit-transform: rotateY(-180deg) scale(.8); } + to { -webkit-transform: rotateY(0) scale(1); } +} + +@-webkit-keyframes flipinfromleft { + from { -webkit-transform: rotateY(180deg) scale(.8); } + to { -webkit-transform: rotateY(0) scale(1); } +} + +@-webkit-keyframes flipouttoleft { + from { -webkit-transform: rotateY(0) scale(1); } + to { -webkit-transform: rotateY(-180deg) scale(.8); } +} + +@-webkit-keyframes flipouttoright { + from { -webkit-transform: rotateY(0) scale(1); } + to { -webkit-transform: rotateY(180deg) scale(.8); } +} + + +/* Hackish, but reliable. */ + +@-webkit-keyframes dontmove { + from { opacity: 1; } + to { opacity: 1; } +} + +.pop { + -webkit-transform-origin: 50% 50%; +} + +.pop.in { + -webkit-transform: scale(1); + opacity: 1; + -webkit-animation-name: popin; + z-index: 10; +} + +.pop.out.reverse { + -webkit-transform: scale(.2); + opacity: 0; + -webkit-animation-name: popout; + z-index: 10; +} + +.pop.in.reverse { + z-index: 0; + -webkit-animation-name: dontmove; +} + +@-webkit-keyframes popin { + from { + -webkit-transform: scale(.2); + opacity: 0; + } + to { + -webkit-transform: scale(1); + opacity: 1; + } +} + +@-webkit-keyframes popout { + from { + -webkit-transform: scale(1); + opacity: 1; + } + to { + -webkit-transform: scale(.2); + opacity: 0; + } +}/* +* jQuery Mobile Framework +* Copyright (c) jQuery Project +* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. +*/ + +/* content configurations. */ +.ui-grid-a, .ui-grid-b, .ui-grid-c, .ui-grid-d { overflow: hidden; } +.ui-block-a, .ui-block-b, .ui-block-c, .ui-block-d, .ui-block-e { margin: 0; padding: 0; border: 0; float: left; min-height:1px;} + +/* grid solo: 100 - single item fallback */ +.ui-grid-solo .ui-block-a { width: 100%; float: none; } + +/* grid a: 50/50 */ +.ui-grid-a .ui-block-a, .ui-grid-a .ui-block-b { width: 50%; } +.ui-grid-a .ui-block-a { clear: left; } + +/* grid b: 33/33/33 */ +.ui-grid-b .ui-block-a, .ui-grid-b .ui-block-b, .ui-grid-b .ui-block-c { width: 33.333%; } +.ui-grid-b .ui-block-a { clear: left; } + +/* grid c: 25/25/25/25 */ +.ui-grid-c .ui-block-a, .ui-grid-c .ui-block-b, .ui-grid-c .ui-block-c, .ui-grid-c .ui-block-d { width: 25%; } +.ui-grid-c .ui-block-a { clear: left; } + +/* grid d: 20/20/20/20/20 */ +.ui-grid-d .ui-block-a, .ui-grid-d .ui-block-b, .ui-grid-d .ui-block-c, .ui-grid-d .ui-block-d, .ui-grid-d .ui-block-e { width: 20%; } +.ui-grid-d .ui-block-a { clear: left; } +/* +* jQuery Mobile Framework +* Copyright (c) jQuery Project +* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. +*/ +/* fixed page header & footer configuration */ +.ui-header, .ui-footer, .ui-page-fullscreen .ui-header, .ui-page-fullscreen .ui-footer { position: absolute; overflow: hidden; width: 100%; border-left-width: 0; border-right-width: 0; } +.ui-header-fixed, .ui-footer-fixed { + z-index: 1000; + -webkit-transform: translateZ(0); /* Force header/footer rendering to go through the same rendering pipeline as native page scrolling. */ +} +.ui-footer-duplicate, .ui-page-fullscreen .ui-fixed-inline { display: none; } +.ui-page-fullscreen .ui-header, .ui-page-fullscreen .ui-footer { opacity: .9; } +/* +* jQuery Mobile Framework +* Copyright (c) jQuery Project +* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. +*/ +.ui-navbar { overflow: hidden; } +.ui-navbar ul, .ui-navbar-expanded ul { list-style:none; padding: 0; margin: 0; position: relative; display: block; border: 0;} +.ui-navbar-collapsed ul { float: left; width: 75%; margin-right: -2px; } +.ui-navbar-collapsed .ui-navbar-toggle { float: left; width: 25%; } +.ui-navbar li.ui-navbar-truncate { position: absolute; left: -9999px; top: -9999px; } +.ui-navbar li .ui-btn, .ui-navbar .ui-navbar-toggle .ui-btn { display: block; font-size: 12px; text-align: center; margin: 0; border-right-width: 0; } +.ui-navbar li .ui-btn { margin-right: -1px; } +.ui-navbar li .ui-btn:last-child { margin-right: 0; } +.ui-header .ui-navbar li .ui-btn, .ui-header .ui-navbar .ui-navbar-toggle .ui-btn, +.ui-footer .ui-navbar li .ui-btn, .ui-footer .ui-navbar .ui-navbar-toggle .ui-btn { border-top-width: 0; border-bottom-width: 0; } +.ui-navbar .ui-btn-inner { padding-left: 2px; padding-right: 2px; } +.ui-navbar-noicons li .ui-btn .ui-btn-inner, .ui-navbar-noicons .ui-navbar-toggle .ui-btn-inner { padding-top: .8em; padding-bottom: .9em; } +/*expanded page styles*/ +.ui-navbar-expanded .ui-btn { margin: 0; font-size: 14px; } +.ui-navbar-expanded .ui-btn-inner { padding-left: 5px; padding-right: 5px; } +.ui-navbar-expanded .ui-btn-icon-top .ui-btn-inner { padding: 45px 5px 15px; text-align: center; } +.ui-navbar-expanded .ui-btn-icon-top .ui-icon { top: 15px; } +.ui-navbar-expanded .ui-btn-icon-bottom .ui-btn-inner { padding: 15px 5px 45px; text-align: center; } +.ui-navbar-expanded .ui-btn-icon-bottom .ui-icon { bottom: 15px; } +.ui-navbar-expanded li .ui-btn .ui-btn-inner { min-height: 2.5em; } +.ui-navbar-expanded .ui-navbar-noicons .ui-btn .ui-btn-inner { padding-top: 1.8em; padding-bottom: 1.9em; } +/* +* jQuery Mobile Framework +* Copyright (c) jQuery Project +* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. +*/ +.ui-btn { display: block; text-align: center; cursor:pointer; position: relative; margin: .5em 5px; padding: 0; } +.ui-btn:focus, .ui-btn:active { outline: none; } +.ui-header .ui-btn, .ui-footer .ui-btn, .ui-bar .ui-btn { display: inline-block; font-size: 13px; margin: 0; } +.ui-btn-inline { display: inline-block; } +.ui-btn-inner { padding: .6em 25px; display: block; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; position: relative; zoom: 1; } +.ui-header .ui-btn-inner, .ui-footer .ui-btn-inner, .ui-bar .ui-btn-inner { padding: .4em 8px .5em; } +.ui-btn-icon-notext { display: inline-block; width: 20px; height: 20px; padding: 2px 1px 2px 3px; text-indent: -9999px; } +.ui-btn-icon-notext .ui-btn-inner { padding: 0; } +.ui-btn-icon-notext .ui-btn-text { position: absolute; left: -999px; } +.ui-btn-icon-left .ui-btn-inner { padding-left: 33px; } +.ui-header .ui-btn-icon-left .ui-btn-inner, +.ui-footer .ui-btn-icon-left .ui-btn-inner, +.ui-bar .ui-btn-icon-left .ui-btn-inner { padding-left: 27px; } +.ui-btn-icon-right .ui-btn-inner { padding-right: 33px; } +.ui-header .ui-btn-icon-right .ui-btn-inner, +.ui-footer .ui-btn-icon-right .ui-btn-inner, +.ui-bar .ui-btn-icon-right .ui-btn-inner { padding-right: 27px; } +.ui-btn-icon-top .ui-btn-inner { padding-top: 33px; } +.ui-header .ui-btn-icon-top .ui-btn-inner, +.ui-footer .ui-btn-icon-top .ui-btn-inner, +.ui-bar .ui-btn-icon-top .ui-btn-inner { padding-top: 27px; } +.ui-btn-icon-bottom .ui-btn-inner { padding-bottom: 33px; } +.ui-header .ui-btn-icon-bottom .ui-btn-inner, +.ui-footer .ui-btn-icon-bottom .ui-btn-inner, +.ui-bar .ui-btn-icon-bottom .ui-btn-inner { padding-bottom: 27px; } + +/*btn icon positioning*/ +.ui-btn-icon-notext .ui-icon { display: block; } +.ui-btn-icon-left .ui-icon, .ui-btn-icon-right .ui-icon { position: absolute; top: 50%; margin-top: -9px; } +.ui-btn-icon-top .ui-icon, .ui-btn-icon-bottom .ui-icon { position: absolute; left: 50%; margin-left: -9px; } +.ui-btn-icon-left .ui-icon { left: 10px; } +.ui-btn-icon-right .ui-icon {right: 10px; } +.ui-header .ui-btn-icon-left .ui-icon, +.ui-footer .ui-btn-icon-left .ui-icon, +.ui-bar .ui-btn-icon-left .ui-icon { left: 4px; } +.ui-header .ui-btn-icon-right .ui-icon, +.ui-footer .ui-btn-icon-right .ui-icon, +.ui-bar .ui-btn-icon-right .ui-icon { right: 4px; } +.ui-header .ui-btn-icon-top .ui-icon, +.ui-footer .ui-btn-icon-top .ui-icon, +.ui-bar .ui-btn-icon-top .ui-icon { top: 4px; } +.ui-header .ui-btn-icon-bottom .ui-icon, +.ui-footer .ui-btn-icon-bottom .ui-icon, +.ui-bar .ui-btn-icon-bottom .ui-icon { bottom: 4px; } +.ui-btn-icon-top .ui-icon { top: 5px; } +.ui-btn-icon-bottom .ui-icon { bottom: 5px; } +/*hiding native button,inputs */ +.ui-btn-hidden { position: absolute; top: 0; left: 0; width: 100%; height: 100%; -webkit-appearance: button; opacity: 0; cursor: pointer; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter: alpha(opacity=0); background: transparent; } +/* +* jQuery Mobile Framework +* Copyright (c) jQuery Project +* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. +*/ +.ui-collapsible-contain { margin: .5em 0; } +.ui-collapsible-heading { font-size: 16px; display: block; margin: 0 -8px; padding: 0; border-width: 0 0 1px 0; position: relative; } +.ui-collapsible-heading a { text-align: left; margin: 0; } +.ui-collapsible-heading a .ui-btn-inner { padding-left: 40px; } +.ui-collapsible-heading a span.ui-btn { position: absolute; left: 6px; top: 50%; margin: -12px 0 0 0; width: 20px; height: 20px; padding: 1px 0px 1px 2px; text-indent: -9999px; } +.ui-collapsible-heading a span.ui-btn .ui-btn-inner { padding: 10px 0; } +.ui-collapsible-heading a span.ui-btn .ui-icon { left: 0; margin-top: -10px; } +.ui-collapsible-heading-status { position:absolute; left:-9999px; } +.ui-collapsible-content { display: block; padding: 10px 0 10px 8px; } +.ui-collapsible-content-collapsed { display: none; } + +.ui-collapsible-set { margin: .5em 0; } +.ui-collapsible-set .ui-collapsible-contain { margin: -1px 0 0; } +/* +* jQuery Mobile Framework +* Copyright (c) jQuery Project +* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. +*/ +.ui-controlgroup, fieldset.ui-controlgroup { padding: 0; margin: .5em 0 1em; } +.ui-bar .ui-controlgroup { margin: 0 .3em; } +.ui-controlgroup-label { font-size: 16px; line-height: 1.4; font-weight: normal; margin: 0 0 .3em; } +.ui-controlgroup-controls { display: block; width: 95%;} +.ui-controlgroup li { list-style: none; } +.ui-controlgroup-vertical .ui-btn, +.ui-controlgroup-vertical .ui-checkbox, .ui-controlgroup-vertical .ui-radio { margin: 0; border-bottom-width: 0; } +.ui-controlgroup-vertical .ui-controlgroup-last { border-bottom-width: 1px; } +.ui-controlgroup-horizontal { padding: 0; } +.ui-controlgroup-horizontal .ui-btn, +.ui-controlgroup-horizontal .ui-checkbox, .ui-controlgroup-horizontal .ui-radio { display: inline-block; margin: 0 -5px 0 0; } +.ui-controlgroup-horizontal .ui-checkbox, .ui-controlgroup-horizontal .ui-radio { display: inline; } +.ui-controlgroup-horizontal .ui-checkbox .ui-btn, .ui-controlgroup-horizontal .ui-radio .ui-btn, +.ui-controlgroup-horizontal .ui-checkbox:last-child, .ui-controlgroup-horizontal .ui-radio:last-child { margin-right: 0; } +.ui-controlgroup-horizontal .ui-controlgroup-last { margin-right: 0; } +.ui-controlgroup .ui-checkbox label, .ui-controlgroup .ui-radio label { font-size: 16px; } +/* conflicts with listview.. +.ui-controlgroup .ui-btn-icon-notext { width: 30px; height: 30px; text-indent: -9999px; } +.ui-controlgroup .ui-btn-icon-notext .ui-btn-inner { padding: 5px 6px 5px 5px; } +*/ + +@media all and (min-width: 450px){ + .ui-controlgroup-label { vertical-align: top; display: inline-block; width: 20%; margin: 0 2% 0 0; } + .ui-controlgroup-controls { width: 60%; display: inline-block; } +} /* +* jQuery Mobile Framework +* Copyright (c) jQuery Project +* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. +*/ +.ui-dialog { min-height: 480px; } +.ui-dialog .ui-header, .ui-dialog .ui-content, .ui-dialog .ui-footer { margin: 15px; position: relative; } +.ui-dialog .ui-header, .ui-dialog .ui-footer { z-index: 10; width: auto; } +.ui-dialog .ui-content, .ui-dialog .ui-footer { margin-top: -15px; }/* +* jQuery Mobile Framework +* Copyright (c) jQuery Project +* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. +*/ +.ui-checkbox, .ui-radio { position:relative; margin: .2em 0 .5em; z-index: 1; } +.ui-checkbox .ui-btn, .ui-radio .ui-btn { margin: 0; text-align: left; z-index: 2; } +.ui-checkbox .ui-btn-inner, .ui-radio .ui-btn-inner { white-space: normal; } +.ui-checkbox .ui-btn-icon-left .ui-btn-inner,.ui-radio .ui-btn-icon-left .ui-btn-inner { padding-left: 45px; } +.ui-checkbox .ui-btn-icon-right .ui-btn-inner, .ui-radio .ui-btn-icon-right .ui-btn-inner { padding-right: 45px; } +.ui-checkbox .ui-icon, .ui-radio .ui-icon { top: 1.1em; } +.ui-checkbox .ui-btn-icon-left .ui-icon, .ui-radio .ui-btn-icon-left .ui-icon {left: 15px; } +.ui-checkbox .ui-btn-icon-right .ui-icon, .ui-radio .ui-btn-icon-right .ui-icon {right: 15px; } +/* input, label positioning */ +.ui-checkbox input,.ui-radio input { position:absolute; left:20px; top:50%; width: 10px; height: 10px; margin:-5px 0 0 0; outline: 0 !important; z-index: 1; }/* +* jQuery Mobile Framework +* Copyright (c) jQuery Project +* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. +*/ +.ui-field-contain { padding: 1.5em 0; margin: 0; border-bottom-width: 1px; overflow: visible; } +.ui-field-contain:first-child { border-top-width: 0; } +@media all and (min-width: 450px){ + .ui-field-contain { border-width: 0; padding: 0; margin: 1em 0; } +} /* +* jQuery Mobile Framework +* Copyright (c) jQuery Project +* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. +*/ +.ui-select { display: block; position: relative; } +.ui-select select { position: absolute; left: -9999px; top: -9999px; } +.ui-select .ui-btn { overflow: hidden; } +.ui-select .ui-btn select { cursor: pointer; -webkit-appearance: button; left: 0; top:0; width: 100%; height: 100%; opacity: 0; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter: alpha(opacity=0); } +@-moz-document url-prefix() {.ui-select .ui-btn select { opacity: 0.0001; }} +.ui-select .ui-btn select.ui-select-nativeonly { opacity: 1; text-indent: 0; } + +.ui-select .ui-btn-icon-right .ui-btn-inner { padding-right: 45px; } +.ui-select .ui-btn-icon-right .ui-icon { right: 15px; } + +/* labels */ +label.ui-select { font-size: 16px; line-height: 1.4; font-weight: normal; margin: 0 0 .3em; display: block; } + +/*listbox*/ +.ui-select .ui-btn-text, .ui-selectmenu .ui-btn-text { display: block; min-height: 1em; } +.ui-select .ui-btn-text { text-overflow: ellipsis; overflow: hidden;} + +.ui-selectmenu { position: absolute; padding: 0; z-index: 100 !important; width: 80%; max-width: 350px; padding: 6px; } +.ui-selectmenu .ui-listview { margin: 0; } +.ui-selectmenu .ui-btn.ui-li-divider { cursor: default; } +.ui-selectmenu-hidden { top: -9999px; left: -9999px; } +.ui-selectmenu-screen { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 99; } +.ui-screen-hidden, .ui-selectmenu-list .ui-li .ui-icon { display: none; } +.ui-selectmenu-list .ui-li .ui-icon { display: block; } +.ui-li.ui-selectmenu-placeholder { display: none; } +.ui-selectmenu .ui-header .ui-title { margin: 0.6em 46px 0.8em; } + +@media all and (min-width: 450px){ + label.ui-select { display: inline-block; width: 20%; margin: 0 2% 0 0; } + .ui-select { width: 60%; display: inline-block; } +} + +/* when no placeholder is defined in a multiple select, the header height doesn't even extend past the close button. this shim's content in there */ +.ui-selectmenu .ui-header h1:after { content: '.'; visibility: hidden; }/* +* jQuery Mobile Framework +* Copyright (c) jQuery Project +* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. +*/ +label.ui-input-text { font-size: 16px; line-height: 1.4; display: block; font-weight: normal; margin: 0 0 .3em; } +input.ui-input-text, textarea.ui-input-text { background-image: none; padding: .4em; line-height: 1.4; font-size: 16px; display: block; width: 95%; } +input.ui-input-text { -webkit-appearance: none; } +textarea.ui-input-text { height: 50px; -webkit-transition: height 200ms linear; -moz-transition: height 200ms linear; -o-transition: height 200ms linear; transition: height 200ms linear; } +.ui-input-search { padding: 0 30px; width: 77%; background-position: 8px 50%; background-repeat: no-repeat; position: relative; } +.ui-input-search input.ui-input-text { border: none; width: 98%; padding: .4em 0; margin: 0; display: block; background: transparent none; outline: 0 !important; } +.ui-input-search .ui-input-clear { position: absolute; right: 0; top: 50%; margin-top: -14px; } +.ui-input-search .ui-input-clear-hidden { display: none; } + +/* orientation adjustments - incomplete!*/ +@media all and (min-width: 450px){ + label.ui-input-text { vertical-align: top; display: inline-block; width: 20%; margin: 0 2% 0 0 } + input.ui-input-text, + textarea.ui-input-text, + .ui-input-search { width: 60%; display: inline-block; } + .ui-input-search { width: 50%; } + .ui-input-search input.ui-input-text { width: 98%; /*echos rule from above*/ } +}/* +* jQuery Mobile Framework +* Copyright (c) jQuery Project +* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. +*/ +.ui-listview { margin: 0; counter-reset: listnumbering; } +.ui-content .ui-listview { margin: -15px; } +.ui-content .ui-listview-inset { margin: 1em 0; } +.ui-listview, .ui-li { list-style:none; padding:0; } +.ui-li, .ui-li.ui-field-contain { display: block; margin:0; position: relative; overflow: visible; text-align: left; border-width: 0; border-top-width: 1px; } +.ui-li .ui-btn-text a.ui-link-inherit { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } +.ui-li-divider, .ui-li-static { padding: .5em 15px; font-size: 14px; font-weight: bold; } +.ui-li-divider { counter-reset: listnumbering; } +ol.ui-listview .ui-link-inherit:before, ol.ui-listview .ui-li-static:before, .ui-li-dec { font-size: .8em; display: inline-block; padding-right: .3em; font-weight: normal;counter-increment: listnumbering; content: counter(listnumbering) ". "; } +ol.ui-listview .ui-li-jsnumbering:before { content: "" !important; } /* to avoid chance of duplication */ +.ui-listview-inset .ui-li { border-right-width: 1px; border-left-width: 1px; } +.ui-li:last-child, .ui-li.ui-field-contain:last-child { border-bottom-width: 1px; } +.ui-li>.ui-btn-inner { display: block; position: relative; padding: 0; } +.ui-li .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li { padding: .7em 75px .7em 15px; display: block; } +.ui-li-has-thumb .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-thumb { min-height: 60px; padding-left: 100px; } +.ui-li-has-icon .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-icon { min-height: 20px; padding-left: 40px; } +.ui-li-heading { font-size: 16px; font-weight: bold; display: block; margin: .6em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } +.ui-li-desc { font-size: 12px; font-weight: normal; display: block; margin: -.5em 0 .6em; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } +.ui-li-thumb, .ui-li-icon { position: absolute; left: 1px; top: 0; max-height: 80px; max-width: 80px; } +.ui-li-icon { max-height: 40px; max-width: 40px; left: 10px; top: .9em; } +.ui-li-thumb, .ui-li-icon, .ui-li-content { float: left; margin-right: 10px; } + +.ui-li-aside { float: right; width: 50%; text-align: right; margin: .3em 0; } +@media all and (min-width: 480px){ + .ui-li-aside { width: 45%; } +} +.ui-li-divider { cursor: default; } +.ui-li-has-alt .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-alt { padding-right: 95px; } +.ui-li-count { position: absolute; font-size: 11px; font-weight: bold; padding: .2em .5em; top: 50%; margin-top: -.9em; right: 38px; } +.ui-li-divider .ui-li-count, .ui-li-static .ui-li-count { right: 10px; } +.ui-li-has-alt .ui-li-count { right: 55px; } +.ui-li-link-alt { position: absolute; width: 40px; height: 100%; border-width: 0; border-left-width: 1px; top: 0; right: 0; margin: 0; padding: 0; } +.ui-li-link-alt .ui-btn { overflow: hidden; position: absolute; right: 8px; top: 50%; margin: -11px 0 0 0; border-bottom-width: 1px; } +.ui-li-link-alt .ui-btn-inner { padding: 0; position: static; } +.ui-li-link-alt .ui-btn .ui-icon { right: 50%; margin-right: -9px; } + +.ui-listview-filter { border-width: 0; overflow: hidden; margin: -15px -15px 15px -15px } +.ui-listview-filter .ui-input-search { margin: 5px; width: auto; display: block; } + +.ui-listview-filter-inset { margin: -15px -5px -15px -5px; background: transparent; } +.ui-li.ui-screen-hidden{display:none;} +/* Odd iPad positioning issue. */ +@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) { + .ui-li .ui-btn-text { overflow: visible; } +}/* +* jQuery Mobile Framework +* Copyright (c) jQuery Project +* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. +*/ +label.ui-slider { display: block; } +input.ui-slider-input { display: inline-block; width: 50px; } +select.ui-slider-switch { display: none; } +div.ui-slider { position: relative; display: inline-block; overflow: visible; height: 15px; padding: 0; margin: 0 2% 0 20px; top: 4px; width: 66%; } +a.ui-slider-handle { position: absolute; z-index: 10; top: 50%; width: 28px; height: 28px; margin-top: -15px; margin-left: -15px; } +a.ui-slider-handle .ui-btn-inner { padding-left: 0; padding-right: 0; } +@media all and (min-width: 480px){ + label.ui-slider { display: inline-block; width: 20%; margin: 0 2% 0 0; } + div.ui-slider { width: 45%; } +} + +div.ui-slider-switch { height: 32px; overflow: hidden; margin-left: 0; } +div.ui-slider-inneroffset { margin-left: 50%; position: absolute; top: 1px; height: 100%; width: 50%; } +div.ui-slider-handle-snapping { -webkit-transition: left 100ms linear; } +div.ui-slider-labelbg { position: absolute; top:0; margin: 0; border-width: 0; } +div.ui-slider-switch div.ui-slider-labelbg-a { width: 60%; height: 100%; left: 0; } +div.ui-slider-switch div.ui-slider-labelbg-b { width: 60%; height: 100%; right: 0; } +.ui-slider-switch-a div.ui-slider-labelbg-a, .ui-slider-switch-b div.ui-slider-labelbg-b { z-index: -1; } +.ui-slider-switch-a div.ui-slider-labelbg-b, .ui-slider-switch-b div.ui-slider-labelbg-a { z-index: 0; } + +div.ui-slider-switch a.ui-slider-handle { z-index: 20; width: 101%; height: 32px; margin-top: -18px; margin-left: -101%; } +span.ui-slider-label { width: 100%; position: absolute;height: 32px; font-size: 16px; text-align: center; line-height: 2; background: none; border-color: transparent; } +span.ui-slider-label-a { left: -100%; margin-right: -1px } +span.ui-slider-label-b { right: -100%; margin-left: -1px } diff --git a/SignalR.Samples/Content/jquery.mobile-1.0b2.min.css b/SignalR.Samples/Content/jquery.mobile-1.0b2.min.css new file mode 100644 index 000000000..4e9814d62 --- /dev/null +++ b/SignalR.Samples/Content/jquery.mobile-1.0b2.min.css @@ -0,0 +1,8 @@ +/*! + * jQuery Mobile v1.0b2 + * http://jquerymobile.com/ + * + * Copyright 2010, jQuery Project + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + */.ui-bar-a{border:1px solid #2a2a2a;background:#111;color:#fff;font-weight:bold;text-shadow:0 -1px 1px #000;background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#111));background-image:-webkit-linear-gradient(top,#3c3c3c,#111);background-image:-moz-linear-gradient(top,#3c3c3c,#111);background-image:-ms-linear-gradient(top,#3c3c3c,#111);background-image:-o-linear-gradient(top,#3c3c3c,#111);background-image:linear-gradient(top,#3c3c3c,#111)}.ui-bar-a,.ui-bar-a input,.ui-bar-a select,.ui-bar-a textarea,.ui-bar-a button{font-family:Helvetica,Arial,sans-serif}.ui-bar-a .ui-link-inherit{color:#fff}.ui-bar-a .ui-link{color:#7cc4e7;font-weight:bold}.ui-body-a{border:1px solid #2a2a2a;background:#222;color:#fff;text-shadow:0 1px 0 #000;font-weight:normal;background-image:-webkit-gradient(linear,left top,left bottom,from(#666),to(#222));background-image:-webkit-linear-gradient(top,#666,#222);background-image:-moz-linear-gradient(top,#666,#222);background-image:-ms-linear-gradient(top,#666,#222);background-image:-o-linear-gradient(top,#666,#222);background-image:linear-gradient(top,#666,#222)}.ui-body-a,.ui-body-a input,.ui-body-a select,.ui-body-a textarea,.ui-body-a button{font-family:Helvetica,Arial,sans-serif}.ui-body-a .ui-link-inherit{color:#fff}.ui-body-a .ui-link{color:#2489ce;font-weight:bold}.ui-br{border-bottom:#828282;border-bottom:rgba(130,130,130,.3);border-bottom-width:1px;border-bottom-style:solid}.ui-btn-up-a{border:1px solid #222;background:#333;font-weight:bold;color:#fff;text-shadow:0 -1px 1px #000;background-image:-webkit-gradient(linear,left top,left bottom,from(#555),to(#333));background-image:-webkit-linear-gradient(top,#555,#333);background-image:-moz-linear-gradient(top,#555,#333);background-image:-ms-linear-gradient(top,#555,#333);background-image:-o-linear-gradient(top,#555,#333);background-image:linear-gradient(top,#555,#333)}.ui-btn-up-a a.ui-link-inherit{color:#fff}.ui-btn-hover-a{border:1px solid #000;background:#444;font-weight:bold;color:#fff;text-shadow:0 -1px 1px #000;background-image:-webkit-gradient(linear,left top,left bottom,from(#666),to(#444));background-image:-webkit-linear-gradient(top,#666,#444);background-image:-moz-linear-gradient(top,#666,#444);background-image:-ms-linear-gradient(top,#666,#444);background-image:-o-linear-gradient(top,#666,#444);background-image:linear-gradient(top,#666,#444)}.ui-btn-hover-a a.ui-link-inherit{color:#fff}.ui-btn-down-a{border:1px solid #000;background:#3d3d3d;font-weight:bold;color:#fff;text-shadow:0 -1px 1px #000;background-image:-webkit-gradient(linear,left top,left bottom,from(#333),to(#5a5a5a));background-image:-webkit-linear-gradient(top,#333,#5a5a5a);background-image:-moz-linear-gradient(top,#333,#5a5a5a);background-image:-ms-linear-gradient(top,#333,#5a5a5a);background-image:-o-linear-gradient(top,#333,#5a5a5a);background-image:linear-gradient(top,#333,#5a5a5a)}.ui-btn-down-a a.ui-link-inherit{color:#fff}.ui-btn-up-a,.ui-btn-hover-a,.ui-btn-down-a{font-family:Helvetica,Arial,sans-serif;text-decoration:none}.ui-bar-b{border:1px solid #456f9a;background:#5e87b0;color:#fff;font-weight:bold;text-shadow:0 -1px 1px #254f7a;background-image:-webkit-gradient(linear,left top,left bottom,from(#81a8ce),to(#5e87b0));background-image:-webkit-linear-gradient(top,#81a8ce,#5e87b0);background-image:-moz-linear-gradient(top,#81a8ce,#5e87b0);background-image:-ms-linear-gradient(top,#81a8ce,#5e87b0);background-image:-o-linear-gradient(top,#81a8ce,#5e87b0);background-image:linear-gradient(top,#81a8ce,#5e87b0)}.ui-bar-b,.ui-bar-b input,.ui-bar-b select,.ui-bar-b textarea,.ui-bar-b button{font-family:Helvetica,Arial,sans-serif}.ui-bar-b .ui-link-inherit{color:#fff}.ui-bar-b .ui-link{color:#7cc4e7;font-weight:bold}.ui-body-b{border:1px solid #c6c6c6;background:#ccc;color:#333;text-shadow:0 1px 0 #fff;font-weight:normal;background-image:-webkit-gradient(linear,left top,left bottom,from(#e6e6e6),to(#ccc));background-image:-webkit-linear-gradient(top,#e6e6e6,#ccc);background-image:-moz-linear-gradient(top,#e6e6e6,#ccc);background-image:-ms-linear-gradient(top,#e6e6e6,#ccc);background-image:-o-linear-gradient(top,#e6e6e6,#ccc);background-image:linear-gradient(top,#e6e6e6,#ccc)}.ui-body-b,.ui-body-b input,.ui-body-b select,.ui-body-b textarea,.ui-body-b button{font-family:Helvetica,Arial,sans-serif}.ui-body-b .ui-link-inherit{color:#333}.ui-body-b .ui-link{color:#2489ce;font-weight:bold}.ui-btn-up-b{border:1px solid #145072;background:#2567ab;font-weight:bold;color:#fff;text-shadow:0 -1px 1px #145072;background-image:-webkit-gradient(linear,left top,left bottom,from(#5f9cc5),to(#396b9e));background-image:-webkit-linear-gradient(top,#5f9cc5,#396b9e);background-image:-moz-linear-gradient(top,#5f9cc5,#396b9e);background-image:-ms-linear-gradient(top,#5f9cc5,#396b9e);background-image:-o-linear-gradient(top,#5f9cc5,#396b9e);background-image:linear-gradient(top,#5f9cc5,#396b9e)}.ui-btn-up-b a.ui-link-inherit{color:#fff}.ui-btn-hover-b{border:1px solid #00516e;background:#4b88b6;font-weight:bold;color:#fff;text-shadow:0 -1px 1px #014d68;background-image:-webkit-gradient(linear,left top,left bottom,from(#72b0d4),to(#4b88b6));background-image:-webkit-linear-gradient(top,#72b0d4,#4b88b6);background-image:-moz-linear-gradient(top,#72b0d4,#4b88b6);background-image:-ms-linear-gradient(top,#72b0d4,#4b88b6);background-image:-o-linear-gradient(top,#72b0d4,#4b88b6);background-image:linear-gradient(top,#72b0d4,#4b88b6)}.ui-btn-hover-b a.ui-link-inherit{color:#fff}.ui-btn-down-b{border:1px solid #225377;background:#4e89c5;font-weight:bold;color:#fff;text-shadow:0 -1px 1px #225377;background-image:-webkit-gradient(linear,left top,left bottom,from(#396b9e),to(#4e89c5));background-image:-webkit-linear-gradient(top,#396b9e,#4e89c5);background-image:-moz-linear-gradient(top,#396b9e,#4e89c5);background-image:-ms-linear-gradient(top,#396b9e,#4e89c5);background-image:-o-linear-gradient(top,#396b9e,#4e89c5);background-image:linear-gradient(top,#396b9e,#4e89c5)}.ui-btn-down-b a.ui-link-inherit{color:#fff}.ui-btn-up-b,.ui-btn-hover-b,.ui-btn-down-b{font-family:Helvetica,Arial,sans-serif;text-decoration:none}.ui-bar-c{border:1px solid #b3b3b3;background:#e9eaeb;color:#3e3e3e;font-weight:bold;text-shadow:0 1px 1px #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#f0f0f0),to(#e9eaeb));background-image:-webkit-linear-gradient(top,#f0f0f0,#e9eaeb);background-image:-moz-linear-gradient(top,#f0f0f0,#e9eaeb);background-image:-ms-linear-gradient(top,#f0f0f0,#e9eaeb);background-image:-o-linear-gradient(top,#f0f0f0,#e9eaeb);background-image:linear-gradient(top,#f0f0f0,#e9eaeb)}.ui-bar-c,.ui-bar-c input,.ui-bar-c select,.ui-bar-c textarea,.ui-bar-c button{font-family:Helvetica,Arial,sans-serif}.ui-body-c{border:1px solid #b3b3b3;color:#333;text-shadow:0 1px 0 #fff;background:#f0f0f0;background-image:-webkit-gradient(linear,left top,left bottom,from(#eee),to(#ddd));background-image:-webkit-linear-gradient(top,#eee,#ddd);background-image:-moz-linear-gradient(top,#eee,#ddd);background-image:-ms-linear-gradient(top,#eee,#ddd);background-image:-o-linear-gradient(top,#eee,#ddd);background-image:linear-gradient(top,#eee,#ddd)}.ui-body-c,.ui-body-c input,.ui-body-c select,.ui-body-c textarea,.ui-body-c button{font-family:Helvetica,Arial,sans-serif}.ui-body-c .ui-link-inherit{color:#333}.ui-body-c .ui-link{color:#2489ce;font-weight:bold}.ui-btn-up-c{border:1px solid #ccc;background:#eee;font-weight:bold;color:#444;text-shadow:0 1px 1px #f6f6f6;background-image:-webkit-gradient(linear,left top,left bottom,from(#fdfdfd),to(#eee));background-image:-webkit-linear-gradient(top,#fdfdfd,#eee);background-image:-moz-linear-gradient(top,#fdfdfd,#eee);background-image:-ms-linear-gradient(top,#fdfdfd,#eee);background-image:-o-linear-gradient(top,#fdfdfd,#eee);background-image:linear-gradient(top,#fdfdfd,#eee)}.ui-btn-up-c a.ui-link-inherit{color:#2f3e46}.ui-btn-hover-c{border:1px solid #bbb;background:#dadada;font-weight:bold;color:#101010;text-shadow:0 1px 1px #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#ededed),to(#dadada));background-image:-webkit-linear-gradient(top,#ededed,#dadada);background-image:-moz-linear-gradient(top,#ededed,#dadada);background-image:-ms-linear-gradient(top,#ededed,#dadada);background-image:-o-linear-gradient(top,#ededed,#dadada);background-image:linear-gradient(top,#ededed,#dadada)}.ui-btn-hover-c a.ui-link-inherit{color:#2f3e46}.ui-btn-down-c{border:1px solid #808080;background:#fdfdfd;font-weight:bold;color:#111;text-shadow:0 1px 1px #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#eee),to(#fdfdfd));background-image:-webkit-linear-gradient(top,#eee,#fdfdfd);background-image:-moz-linear-gradient(top,#eee,#fdfdfd);background-image:-ms-linear-gradient(top,#eee,#fdfdfd);background-image:-o-linear-gradient(top,#eee,#fdfdfd);background-image:linear-gradient(top,#eee,#fdfdfd)}.ui-btn-down-c a.ui-link-inherit{color:#2f3e46}.ui-btn-up-c,.ui-btn-hover-c,.ui-btn-down-c{font-family:Helvetica,Arial,sans-serif;text-decoration:none}.ui-bar-d{border:1px solid #ccc;background:#bbb;color:#333;text-shadow:0 1px 0 #eee;background-image:-webkit-gradient(linear,left top,left bottom,from(#ddd),to(#bbb));background-image:-webkit-linear-gradient(top,#ddd,#bbb);background-image:-moz-linear-gradient(top,#ddd,#bbb);background-image:-ms-linear-gradient(top,#ddd,#bbb);background-image:-o-linear-gradient(top,#ddd,#bbb);background-image:linear-gradient(top,#ddd,#bbb)}.ui-bar-d,.ui-bar-d input,.ui-bar-d select,.ui-bar-d textarea,.ui-bar-d button{font-family:Helvetica,Arial,sans-serif}.ui-bar-d .ui-link-inherit{color:#333}.ui-bar-d .ui-link{color:#2489ce;font-weight:bold}.ui-body-d{border:1px solid #ccc;color:#333;text-shadow:0 1px 0 #fff;background:#fff}.ui-body-d,.ui-body-d input,.ui-body-d select,.ui-body-d textarea,.ui-body-d button{font-family:Helvetica,Arial,sans-serif}.ui-body-d .ui-link-inherit{color:#333}.ui-body-d .ui-link{color:#2489ce;font-weight:bold}.ui-btn-up-d{border:1px solid #ccc;background:#fff;font-weight:bold;color:#444;text-shadow:0 1px 1px #fff}.ui-btn-up-d a.ui-link-inherit{color:#333}.ui-btn-hover-d{border:1px solid #aaa;background:#eee;font-weight:bold;color:#222;cursor:pointer;text-shadow:0 1px 1px #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fdfdfd),to(#eee));background-image:-webkit-linear-gradient(top,#fdfdfd,#eee);background-image:-moz-linear-gradient(top,#fdfdfd,#eee);background-image:-ms-linear-gradient(top,#fdfdfd,#eee);background-image:-o-linear-gradient(top,#fdfdfd,#eee);background-image:linear-gradient(top,#fdfdfd,#eee)}.ui-btn-hover-d a.ui-link-inherit{color:#222}.ui-btn-down-d{border:1px solid #aaa;background:#fff;font-weight:bold;color:#111;text-shadow:0 1px 1px #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#eee),to(#fff));background-image:-webkit-linear-gradient(top,#eee,#fff);background-image:-moz-linear-gradient(top,#eee,#fff);background-image:-ms-linear-gradient(top,#eee,#fff);background-image:-o-linear-gradient(top,#eee,#fff);background-image:linear-gradient(top,#eee,#fff)}.ui-btn-down-d a.ui-link-inherit{color:#111}.ui-btn-up-d,.ui-btn-hover-d,.ui-btn-down-d{font-family:Helvetica,Arial,sans-serif;text-decoration:none}.ui-bar-e{border:1px solid #f7c942;background:#fadb4e;color:#333;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fceda7),to(#fadb4e));background-image:-webkit-linear-gradient(top,#fceda7,#fadb4e);background-image:-moz-linear-gradient(top,#fceda7,#fadb4e);background-image:-ms-linear-gradient(top,#fceda7,#fadb4e);background-image:-o-linear-gradient(top,#fceda7,#fadb4e);background-image:linear-gradient(top,#fceda7,#fadb4e)}.ui-bar-e,.ui-bar-e input,.ui-bar-e select,.ui-bar-e textarea,.ui-bar-e button{font-family:Helvetica,Arial,sans-serif}.ui-bar-e .ui-link-inherit{color:#333}.ui-bar-e .ui-link{color:#2489ce;font-weight:bold}.ui-body-e{border:1px solid #f7c942;color:#333;text-shadow:0 1px 0 #fff;background:#faeb9e;background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#faeb9e));background-image:-webkit-linear-gradient(top,#fff,#faeb9e);background-image:-moz-linear-gradient(top,#fff,#faeb9e);background-image:-ms-linear-gradient(top,#fff,#faeb9e);background-image:-o-linear-gradient(top,#fff,#faeb9e);background-image:linear-gradient(top,#fff,#faeb9e)}.ui-body-e,.ui-body-e input,.ui-body-e select,.ui-body-e textarea,.ui-body-e button{font-family:Helvetica,Arial,sans-serif}.ui-body-e .ui-link-inherit{color:#333}.ui-body-e .ui-link{color:#2489ce;font-weight:bold}.ui-btn-up-e{border:1px solid #f7c942;background:#fadb4e;font-weight:bold;color:#333;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fceda7),to(#fadb4e));background-image:-webkit-linear-gradient(top,#fceda7,#fadb4e);background-image:-moz-linear-gradient(top,#fceda7,#fadb4e);background-image:-ms-linear-gradient(top,#fceda7,#fadb4e);background-image:-o-linear-gradient(top,#fceda7,#fadb4e);background-image:linear-gradient(top,#fceda7,#fadb4e)}.ui-btn-up-e a.ui-link-inherit{color:#333}.ui-btn-hover-e{border:1px solid #e79952;background:#fbe26f;font-weight:bold;color:#111;text-shadow:0 1px 1px #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf0b5),to(#fbe26f));background-image:-webkit-linear-gradient(top,#fcf0b5,#fbe26f);background-image:-moz-linear-gradient(top,#fcf0b5,#fbe26f);background-image:-ms-linear-gradient(top,#fcf0b5,#fbe26f);background-image:-o-linear-gradient(top,#fcf0b5,#fbe26f);background-image:linear-gradient(top,#fcf0b5,#fbe26f)}.ui-btn-hover-e a.ui-link-inherit{color:#333}.ui-btn-down-e{border:1px solid #f7c942;background:#fceda7;font-weight:bold;color:#111;text-shadow:0 1px 1px #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fadb4e),to(#fceda7));background-image:-webkit-linear-gradient(top,#fadb4e,#fceda7);background-image:-moz-linear-gradient(top,#fadb4e,#fceda7);background-image:-ms-linear-gradient(top,#fadb4e,#fceda7);background-image:-o-linear-gradient(top,#fadb4e,#fceda7);background-image:linear-gradient(top,#fadb4e,#fceda7)}.ui-btn-down-e a.ui-link-inherit{color:#333}.ui-btn-up-e,.ui-btn-hover-e,.ui-btn-down-e{font-family:Helvetica,Arial,sans-serif;text-decoration:none}a.ui-link-inherit{text-decoration:none!important}.ui-btn-active{border:1px solid #155678;background:#4596ce;font-weight:bold;color:#fff;cursor:pointer;text-shadow:0 -1px 1px #145072;text-decoration:none;background-image:-webkit-gradient(linear,left top,left bottom,from(#85bae4),to(#5393c5));background-image:-webkit-linear-gradient(top,#85bae4,#5393c5);background-image:-moz-linear-gradient(top,#85bae4,#5393c5);background-image:-ms-linear-gradient(top,#85bae4,#5393c5);background-image:-o-linear-gradient(top,#85bae4,#5393c5);background-image:linear-gradient(top,#85bae4,#5393c5);outline:0}.ui-btn-active a.ui-link-inherit{color:#fff}.ui-btn-inner{border-top:1px solid #fff;border-color:rgba(255,255,255,.3)}.ui-corner-tl{-moz-border-radius-topleft:.6em;-webkit-border-top-left-radius:.6em;border-top-left-radius:.6em}.ui-corner-tr{-moz-border-radius-topright:.6em;-webkit-border-top-right-radius:.6em;border-top-right-radius:.6em}.ui-corner-bl{-moz-border-radius-bottomleft:.6em;-webkit-border-bottom-left-radius:.6em;border-bottom-left-radius:.6em}.ui-corner-br{-moz-border-radius-bottomright:.6em;-webkit-border-bottom-right-radius:.6em;border-bottom-right-radius:.6em}.ui-corner-top{-moz-border-radius-topleft:.6em;-webkit-border-top-left-radius:.6em;border-top-left-radius:.6em;-moz-border-radius-topright:.6em;-webkit-border-top-right-radius:.6em;border-top-right-radius:.6em}.ui-corner-bottom{-moz-border-radius-bottomleft:.6em;-webkit-border-bottom-left-radius:.6em;border-bottom-left-radius:.6em;-moz-border-radius-bottomright:.6em;-webkit-border-bottom-right-radius:.6em;border-bottom-right-radius:.6em}.ui-corner-right{-moz-border-radius-topright:.6em;-webkit-border-top-right-radius:.6em;border-top-right-radius:.6em;-moz-border-radius-bottomright:.6em;-webkit-border-bottom-right-radius:.6em;border-bottom-right-radius:.6em}.ui-corner-left{-moz-border-radius-topleft:.6em;-webkit-border-top-left-radius:.6em;border-top-left-radius:.6em;-moz-border-radius-bottomleft:.6em;-webkit-border-bottom-left-radius:.6em;border-bottom-left-radius:.6em}.ui-corner-all{-moz-border-radius:.6em;-webkit-border-radius:.6em;border-radius:.6em}.ui-disabled{opacity:.3}.ui-disabled,.ui-disabled a{cursor:default}.ui-icon{background:#666;background:rgba(0,0,0,.4);background-image:url(images/icons-18-white.png);background-repeat:no-repeat;-moz-border-radius:9px;-webkit-border-radius:9px;border-radius:9px}.ui-icon-alt{background:#fff;background:rgba(255,255,255,.3);background-image:url(images/icons-18-black.png);background-repeat:no-repeat}@media only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min--moz-device-pixel-ratio:1.5),only screen and (min-resolution:240dpi){.ui-icon-plus,.ui-icon-minus,.ui-icon-delete,.ui-icon-arrow-r,.ui-icon-arrow-l,.ui-icon-arrow-u,.ui-icon-arrow-d,.ui-icon-check,.ui-icon-gear,.ui-icon-refresh,.ui-icon-forward,.ui-icon-back,.ui-icon-grid,.ui-icon-star,.ui-icon-alert,.ui-icon-info,.ui-icon-home,.ui-icon-search,.ui-icon-checkbox-off,.ui-icon-checkbox-on,.ui-icon-radio-off,.ui-icon-radio-on{background-image:url(images/icons-36-white.png);-moz-background-size:776px 18px;-o-background-size:776px 18px;-webkit-background-size:776px 18px;background-size:776px 18px}.ui-icon-alt{background-image:url(images/icons-36-black.png)}}.ui-icon-plus{background-position:-0 50%}.ui-icon-minus{background-position:-36px 50%}.ui-icon-delete{background-position:-72px 50%}.ui-icon-arrow-r{background-position:-108px 50%}.ui-icon-arrow-l{background-position:-144px 50%}.ui-icon-arrow-u{background-position:-180px 50%}.ui-icon-arrow-d{background-position:-216px 50%}.ui-icon-check{background-position:-252px 50%}.ui-icon-gear{background-position:-288px 50%}.ui-icon-refresh{background-position:-324px 50%}.ui-icon-forward{background-position:-360px 50%}.ui-icon-back{background-position:-396px 50%}.ui-icon-grid{background-position:-432px 50%}.ui-icon-star{background-position:-468px 50%}.ui-icon-alert{background-position:-504px 50%}.ui-icon-info{background-position:-540px 50%}.ui-icon-home{background-position:-576px 50%}.ui-icon-search{background-position:-612px 50%}.ui-icon-checkbox-off{background-position:-684px 50%}.ui-icon-checkbox-on{background-position:-648px 50%}.ui-icon-radio-off{background-position:-756px 50%}.ui-icon-radio-on{background-position:-720px 50%}.ui-checkbox .ui-icon{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}.ui-icon-checkbox-off,.ui-icon-radio-off{background-color:transparent}.ui-checkbox-on .ui-icon,.ui-radio-on .ui-icon{background-color:#4596ce}.ui-icon-searchfield{background-image:url(images/icon-search-black.png);background-size:16px 16px}.ui-icon-loading{background-image:url(images/ajax-loader.png);width:40px;height:40px;-moz-border-radius:20px;-webkit-border-radius:20px;border-radius:20px;background-size:35px 35px}.ui-btn-corner-tl{-moz-border-radius-topleft:1em;-webkit-border-top-left-radius:1em;border-top-left-radius:1em}.ui-btn-corner-tr{-moz-border-radius-topright:1em;-webkit-border-top-right-radius:1em;border-top-right-radius:1em}.ui-btn-corner-bl{-moz-border-radius-bottomleft:1em;-webkit-border-bottom-left-radius:1em;border-bottom-left-radius:1em}.ui-btn-corner-br{-moz-border-radius-bottomright:1em;-webkit-border-bottom-right-radius:1em;border-bottom-right-radius:1em}.ui-btn-corner-top{-moz-border-radius-topleft:1em;-webkit-border-top-left-radius:1em;border-top-left-radius:1em;-moz-border-radius-topright:1em;-webkit-border-top-right-radius:1em;border-top-right-radius:1em}.ui-btn-corner-bottom{-moz-border-radius-bottomleft:1em;-webkit-border-bottom-left-radius:1em;border-bottom-left-radius:1em;-moz-border-radius-bottomright:1em;-webkit-border-bottom-right-radius:1em;border-bottom-right-radius:1em}.ui-btn-corner-right{-moz-border-radius-topright:1em;-webkit-border-top-right-radius:1em;border-top-right-radius:1em;-moz-border-radius-bottomright:1em;-webkit-border-bottom-right-radius:1em;border-bottom-right-radius:1em}.ui-btn-corner-left{-moz-border-radius-topleft:1em;-webkit-border-top-left-radius:1em;border-top-left-radius:1em;-moz-border-radius-bottomleft:1em;-webkit-border-bottom-left-radius:1em;border-bottom-left-radius:1em}.ui-btn-corner-all{-moz-border-radius:1em;-webkit-border-radius:1em;border-radius:1em}.ui-corner-tl,.ui-corner-tr,.ui-corner-bl,.ui-corner-br,.ui-corner-top,.ui-corner-bottom,.ui-corner-right,.ui-corner-left,.ui-corner-all,.ui-btn-corner-tl,.ui-btn-corner-tr,.ui-btn-corner-bl,.ui-btn-corner-br,.ui-btn-corner-top,.ui-btn-corner-bottom,.ui-btn-corner-right,.ui-btn-corner-left,.ui-btn-corner-all{-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.ui-overlay{background:#666;opacity:.5;filter:Alpha(Opacity=50);position:absolute;width:100%;height:100%}.ui-overlay-shadow{-moz-box-shadow:0 0 12px rgba(0,0,0,.6);-webkit-box-shadow:0 0 12px rgba(0,0,0,.6);box-shadow:0 0 12px rgba(0,0,0,.6)}.ui-shadow{-moz-box-shadow:0 1px 4px rgba(0,0,0,.3);-webkit-box-shadow:0 1px 4px rgba(0,0,0,.3);box-shadow:0 1px 4px rgba(0,0,0,.3)}.ui-bar-a .ui-shadow,.ui-bar-b .ui-shadow,.ui-bar-c .ui-shadow{-moz-box-shadow:0 1px 0 rgba(255,255,255,.3);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.3);box-shadow:0 1px 0 rgba(255,255,255,.3)}.ui-shadow-inset{-moz-box-shadow:inset 0 1px 4px rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 4px rgba(0,0,0,.2);box-shadow:inset 0 1px 4px rgba(0,0,0,.2)}.ui-icon-shadow{-moz-box-shadow:0 1px 0 rgba(255,255,255,.4);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.4);box-shadow:0 1px 0 rgba(255,255,255,.4)}.ui-focus{-moz-box-shadow:0 0 12px #387bbe;-webkit-box-shadow:0 0 12px #387bbe;box-shadow:0 0 12px #387bbe}.ui-mobile-nosupport-boxshadow *{-moz-box-shadow:none!important;-webkit-box-shadow:none!important;box-shadow:none!important}.ui-mobile-nosupport-boxshadow .ui-focus{outline-width:2px}.ui-mobile,.ui-mobile body{height:100%}.ui-mobile fieldset,.ui-page{padding:0;margin:0}.ui-mobile a img,.ui-mobile fieldset{border:0}.ui-mobile-viewport{margin:0;overflow-x:hidden;-webkit-text-size-adjust:none;-ms-text-size-adjust:none;-webkit-tap-highlight-color:rgba(0,0,0,0)}.ui-mobile [data-role=page],.ui-mobile [data-role=dialog],.ui-page{top:0;left:0;width:100%;min-height:100%;position:absolute;display:none;border:0}.ui-mobile .ui-page-active{display:block;overflow:visible}.portrait,.portrait .ui-page{min-height:420px}.landscape,.landscape .ui-page{min-height:300px}.ui-loading .ui-mobile-viewport{overflow:hidden!important}.ui-loading .ui-loader{display:block}.ui-loading .ui-page{overflow:hidden}.ui-loader{display:none;position:absolute;opacity:.85;z-index:100;left:50%;width:200px;margin-left:-130px;margin-top:-35px;padding:10px 30px}.ui-loader h1{font-size:15px;text-align:center}.ui-loader .ui-icon{position:static;display:block;opacity:.9;margin:0 auto;width:35px;height:35px;background-color:transparent}.ui-mobile-rendering>*{visibility:hidden}.ui-bar,.ui-body{position:relative;padding:.4em 15px;overflow:hidden;display:block;clear:both}.ui-bar{font-size:16px;margin:0}.ui-bar h1,.ui-bar h2,.ui-bar h3,.ui-bar h4,.ui-bar h5,.ui-bar h6{margin:0;padding:0;font-size:16px;display:inline-block}.ui-header,.ui-footer{display:block}.ui-page .ui-header,.ui-page .ui-footer{position:relative}.ui-header .ui-btn-left{position:absolute;left:10px;top:.4em}.ui-header .ui-btn-right{position:absolute;right:10px;top:.4em}.ui-header .ui-title,.ui-footer .ui-title{min-height:1.1em;text-align:center;font-size:16px;display:block;margin:.6em 90px .8em;padding:0;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;outline:0!important}.ui-content{border-width:0;overflow:visible;overflow-x:hidden;padding:15px}.ui-page-fullscreen .ui-content{padding:0}.ui-icon{width:18px;height:18px}.ui-fullscreen img{max-width:100%}.ui-nojs{position:absolute;left:-9999px}.spin{-webkit-transform:rotate(360deg);-webkit-animation-name:spin;-webkit-animation-duration:1s;-webkit-animation-iteration-count:infinite;-webkit-animation-timing-function:linear}@-webkit-keyframes spin{from{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(360deg)}}.in,.out{-webkit-animation-timing-function:ease-in-out;-webkit-animation-duration:350ms}.slide.in{-webkit-transform:translateX(0);-webkit-animation-name:slideinfromright}.slide.out{-webkit-transform:translateX(-100%);-webkit-animation-name:slideouttoleft}.slide.in.reverse{-webkit-transform:translateX(0);-webkit-animation-name:slideinfromleft}.slide.out.reverse{-webkit-transform:translateX(100%);-webkit-animation-name:slideouttoright}.slideup.in{-webkit-transform:translateY(0);-webkit-animation-name:slideinfrombottom;z-index:10}.slideup.out{-webkit-animation-name:dontmove;z-index:0}.slideup.out.reverse{-webkit-transform:translateY(100%);z-index:10;-webkit-animation-name:slideouttobottom}.slideup.in.reverse{z-index:0;-webkit-animation-name:dontmove}.slidedown.in{-webkit-transform:translateY(0);-webkit-animation-name:slideinfromtop;z-index:10}.slidedown.out{-webkit-animation-name:dontmove;z-index:0}.slidedown.out.reverse{-webkit-transform:translateY(-100%);z-index:10;-webkit-animation-name:slideouttotop}.slidedown.in.reverse{z-index:0;-webkit-animation-name:dontmove}@-webkit-keyframes slideinfromright{from{-webkit-transform:translateX(100%)}to{-webkit-transform:translateX(0)}}@-webkit-keyframes slideinfromleft{from{-webkit-transform:translateX(-100%)}to{-webkit-transform:translateX(0)}}@-webkit-keyframes slideouttoleft{from{-webkit-transform:translateX(0)}to{-webkit-transform:translateX(-100%)}}@-webkit-keyframes slideouttoright{from{-webkit-transform:translateX(0)}to{-webkit-transform:translateX(100%)}}@-webkit-keyframes slideinfromtop{from{-webkit-transform:translateY(-100%)}to{-webkit-transform:translateY(0)}}@-webkit-keyframes slideinfrombottom{from{-webkit-transform:translateY(100%)}to{-webkit-transform:translateY(0)}}@-webkit-keyframes slideouttobottom{from{-webkit-transform:translateY(0)}to{-webkit-transform:translateY(100%)}}@-webkit-keyframes slideouttotop{from{-webkit-transform:translateY(0)}to{-webkit-transform:translateY(-100%)}}@-webkit-keyframes fadein{from{opacity:0}to{opacity:1}}@-webkit-keyframes fadeout{from{opacity:1}to{opacity:0}}.fade.in{opacity:1;z-index:10;-webkit-animation-name:fadein}.fade.out{z-index:0;-webkit-animation-name:fadeout}.viewport-flip{-webkit-perspective:1000;position:absolute}.ui-mobile-viewport-transitioning,.ui-mobile-viewport-transitioning .ui-page{width:100%;height:100%;overflow:hidden}.flip{-webkit-animation-duration:.65s;-webkit-backface-visibility:hidden;-webkit-transform:translateX(0)}.flip.in{-webkit-transform:rotateY(0) scale(1);-webkit-animation-name:flipinfromleft}.flip.out{-webkit-transform:rotateY(-180deg) scale(.8);-webkit-animation-name:flipouttoleft}.flip.in.reverse{-webkit-transform:rotateY(0) scale(1);-webkit-animation-name:flipinfromright}.flip.out.reverse{-webkit-transform:rotateY(180deg) scale(.8);-webkit-animation-name:flipouttoright}@-webkit-keyframes flipinfromright{from{-webkit-transform:rotateY(-180deg) scale(.8)}to{-webkit-transform:rotateY(0) scale(1)}}@-webkit-keyframes flipinfromleft{from{-webkit-transform:rotateY(180deg) scale(.8)}to{-webkit-transform:rotateY(0) scale(1)}}@-webkit-keyframes flipouttoleft{from{-webkit-transform:rotateY(0) scale(1)}to{-webkit-transform:rotateY(-180deg) scale(.8)}}@-webkit-keyframes flipouttoright{from{-webkit-transform:rotateY(0) scale(1)}to{-webkit-transform:rotateY(180deg) scale(.8)}}@-webkit-keyframes dontmove{from{opacity:1}to{opacity:1}}.pop{-webkit-transform-origin:50% 50%}.pop.in{-webkit-transform:scale(1);opacity:1;-webkit-animation-name:popin;z-index:10}.pop.out.reverse{-webkit-transform:scale(.2);opacity:0;-webkit-animation-name:popout;z-index:10}.pop.in.reverse{z-index:0;-webkit-animation-name:dontmove}@-webkit-keyframes popin{from{-webkit-transform:scale(.2);opacity:0}to{-webkit-transform:scale(1);opacity:1}}@-webkit-keyframes popout{from{-webkit-transform:scale(1);opacity:1}to{-webkit-transform:scale(.2);opacity:0}}.ui-grid-a,.ui-grid-b,.ui-grid-c,.ui-grid-d{overflow:hidden}.ui-block-a,.ui-block-b,.ui-block-c,.ui-block-d,.ui-block-e{margin:0;padding:0;border:0;float:left;min-height:1px}.ui-grid-solo .ui-block-a{width:100%;float:none}.ui-grid-a .ui-block-a,.ui-grid-a .ui-block-b{width:50%}.ui-grid-a .ui-block-a{clear:left}.ui-grid-b .ui-block-a,.ui-grid-b .ui-block-b,.ui-grid-b .ui-block-c{width:33.333%}.ui-grid-b .ui-block-a{clear:left}.ui-grid-c .ui-block-a,.ui-grid-c .ui-block-b,.ui-grid-c .ui-block-c,.ui-grid-c .ui-block-d{width:25%}.ui-grid-c .ui-block-a{clear:left}.ui-grid-d .ui-block-a,.ui-grid-d .ui-block-b,.ui-grid-d .ui-block-c,.ui-grid-d .ui-block-d,.ui-grid-d .ui-block-e{width:20%}.ui-grid-d .ui-block-a{clear:left}.ui-header,.ui-footer,.ui-page-fullscreen .ui-header,.ui-page-fullscreen .ui-footer{position:absolute;overflow:hidden;width:100%;border-left-width:0;border-right-width:0}.ui-header-fixed,.ui-footer-fixed{z-index:1000;-webkit-transform:translateZ(0)}.ui-footer-duplicate,.ui-page-fullscreen .ui-fixed-inline{display:none}.ui-page-fullscreen .ui-header,.ui-page-fullscreen .ui-footer{opacity:.9}.ui-navbar{overflow:hidden}.ui-navbar ul,.ui-navbar-expanded ul{list-style:none;padding:0;margin:0;position:relative;display:block;border:0}.ui-navbar-collapsed ul{float:left;width:75%;margin-right:-2px}.ui-navbar-collapsed .ui-navbar-toggle{float:left;width:25%}.ui-navbar li.ui-navbar-truncate{position:absolute;left:-9999px;top:-9999px}.ui-navbar li .ui-btn,.ui-navbar .ui-navbar-toggle .ui-btn{display:block;font-size:12px;text-align:center;margin:0;border-right-width:0}.ui-navbar li .ui-btn{margin-right:-1px}.ui-navbar li .ui-btn:last-child{margin-right:0}.ui-header .ui-navbar li .ui-btn,.ui-header .ui-navbar .ui-navbar-toggle .ui-btn,.ui-footer .ui-navbar li .ui-btn,.ui-footer .ui-navbar .ui-navbar-toggle .ui-btn{border-top-width:0;border-bottom-width:0}.ui-navbar .ui-btn-inner{padding-left:2px;padding-right:2px}.ui-navbar-noicons li .ui-btn .ui-btn-inner,.ui-navbar-noicons .ui-navbar-toggle .ui-btn-inner{padding-top:.8em;padding-bottom:.9em}.ui-navbar-expanded .ui-btn{margin:0;font-size:14px}.ui-navbar-expanded .ui-btn-inner{padding-left:5px;padding-right:5px}.ui-navbar-expanded .ui-btn-icon-top .ui-btn-inner{padding:45px 5px 15px;text-align:center}.ui-navbar-expanded .ui-btn-icon-top .ui-icon{top:15px}.ui-navbar-expanded .ui-btn-icon-bottom .ui-btn-inner{padding:15px 5px 45px;text-align:center}.ui-navbar-expanded .ui-btn-icon-bottom .ui-icon{bottom:15px}.ui-navbar-expanded li .ui-btn .ui-btn-inner{min-height:2.5em}.ui-navbar-expanded .ui-navbar-noicons .ui-btn .ui-btn-inner{padding-top:1.8em;padding-bottom:1.9em}.ui-btn{display:block;text-align:center;cursor:pointer;position:relative;margin:.5em 5px;padding:0}.ui-btn:focus,.ui-btn:active{outline:0}.ui-header .ui-btn,.ui-footer .ui-btn,.ui-bar .ui-btn{display:inline-block;font-size:13px;margin:0}.ui-btn-inline{display:inline-block}.ui-btn-inner{padding:.6em 25px;display:block;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;position:relative;zoom:1}.ui-header .ui-btn-inner,.ui-footer .ui-btn-inner,.ui-bar .ui-btn-inner{padding:.4em 8px .5em}.ui-btn-icon-notext{display:inline-block;width:20px;height:20px;padding:2px 1px 2px 3px;text-indent:-9999px}.ui-btn-icon-notext .ui-btn-inner{padding:0}.ui-btn-icon-notext .ui-btn-text{position:absolute;left:-999px}.ui-btn-icon-left .ui-btn-inner{padding-left:33px}.ui-header .ui-btn-icon-left .ui-btn-inner,.ui-footer .ui-btn-icon-left .ui-btn-inner,.ui-bar .ui-btn-icon-left .ui-btn-inner{padding-left:27px}.ui-btn-icon-right .ui-btn-inner{padding-right:33px}.ui-header .ui-btn-icon-right .ui-btn-inner,.ui-footer .ui-btn-icon-right .ui-btn-inner,.ui-bar .ui-btn-icon-right .ui-btn-inner{padding-right:27px}.ui-btn-icon-top .ui-btn-inner{padding-top:33px}.ui-header .ui-btn-icon-top .ui-btn-inner,.ui-footer .ui-btn-icon-top .ui-btn-inner,.ui-bar .ui-btn-icon-top .ui-btn-inner{padding-top:27px}.ui-btn-icon-bottom .ui-btn-inner{padding-bottom:33px}.ui-header .ui-btn-icon-bottom .ui-btn-inner,.ui-footer .ui-btn-icon-bottom .ui-btn-inner,.ui-bar .ui-btn-icon-bottom .ui-btn-inner{padding-bottom:27px}.ui-btn-icon-notext .ui-icon{display:block}.ui-btn-icon-left .ui-icon,.ui-btn-icon-right .ui-icon{position:absolute;top:50%;margin-top:-9px}.ui-btn-icon-top .ui-icon,.ui-btn-icon-bottom .ui-icon{position:absolute;left:50%;margin-left:-9px}.ui-btn-icon-left .ui-icon{left:10px}.ui-btn-icon-right .ui-icon{right:10px}.ui-header .ui-btn-icon-left .ui-icon,.ui-footer .ui-btn-icon-left .ui-icon,.ui-bar .ui-btn-icon-left .ui-icon{left:4px}.ui-header .ui-btn-icon-right .ui-icon,.ui-footer .ui-btn-icon-right .ui-icon,.ui-bar .ui-btn-icon-right .ui-icon{right:4px}.ui-header .ui-btn-icon-top .ui-icon,.ui-footer .ui-btn-icon-top .ui-icon,.ui-bar .ui-btn-icon-top .ui-icon{top:4px}.ui-header .ui-btn-icon-bottom .ui-icon,.ui-footer .ui-btn-icon-bottom .ui-icon,.ui-bar .ui-btn-icon-bottom .ui-icon{bottom:4px}.ui-btn-icon-top .ui-icon{top:5px}.ui-btn-icon-bottom .ui-icon{bottom:5px}.ui-btn-hidden{position:absolute;top:0;left:0;width:100%;height:100%;-webkit-appearance:button;opacity:0;cursor:pointer;-ms-filter:"alpha(opacity=0)";filter:alpha(opacity=0);background:transparent}.ui-collapsible-contain{margin:.5em 0}.ui-collapsible-heading{font-size:16px;display:block;margin:0 -8px;padding:0;border-width:0 0 1px 0;position:relative}.ui-collapsible-heading a{text-align:left;margin:0}.ui-collapsible-heading a .ui-btn-inner{padding-left:40px}.ui-collapsible-heading a span.ui-btn{position:absolute;left:6px;top:50%;margin:-12px 0 0 0;width:20px;height:20px;padding:1px 0 1px 2px;text-indent:-9999px}.ui-collapsible-heading a span.ui-btn .ui-btn-inner{padding:10px 0}.ui-collapsible-heading a span.ui-btn .ui-icon{left:0;margin-top:-10px}.ui-collapsible-heading-status{position:absolute;left:-9999px}.ui-collapsible-content{display:block;padding:10px 0 10px 8px}.ui-collapsible-content-collapsed{display:none}.ui-collapsible-set{margin:.5em 0}.ui-collapsible-set .ui-collapsible-contain{margin:-1px 0 0}.ui-controlgroup,fieldset.ui-controlgroup{padding:0;margin:.5em 0 1em}.ui-bar .ui-controlgroup{margin:0 .3em}.ui-controlgroup-label{font-size:16px;line-height:1.4;font-weight:normal;margin:0 0 .3em}.ui-controlgroup-controls{display:block;width:95%}.ui-controlgroup li{list-style:none}.ui-controlgroup-vertical .ui-btn,.ui-controlgroup-vertical .ui-checkbox,.ui-controlgroup-vertical .ui-radio{margin:0;border-bottom-width:0}.ui-controlgroup-vertical .ui-controlgroup-last{border-bottom-width:1px}.ui-controlgroup-horizontal{padding:0}.ui-controlgroup-horizontal .ui-btn,.ui-controlgroup-horizontal .ui-checkbox,.ui-controlgroup-horizontal .ui-radio{display:inline-block;margin:0 -5px 0 0}.ui-controlgroup-horizontal .ui-checkbox,.ui-controlgroup-horizontal .ui-radio{display:inline}.ui-controlgroup-horizontal .ui-checkbox .ui-btn,.ui-controlgroup-horizontal .ui-radio .ui-btn,.ui-controlgroup-horizontal .ui-checkbox:last-child,.ui-controlgroup-horizontal .ui-radio:last-child{margin-right:0}.ui-controlgroup-horizontal .ui-controlgroup-last{margin-right:0}.ui-controlgroup .ui-checkbox label,.ui-controlgroup .ui-radio label{font-size:16px}@media all and (min-width:450px){.ui-controlgroup-label{vertical-align:top;display:inline-block;width:20%;margin:0 2% 0 0}.ui-controlgroup-controls{width:60%;display:inline-block}}.ui-dialog{min-height:480px}.ui-dialog .ui-header,.ui-dialog .ui-content,.ui-dialog .ui-footer{margin:15px;position:relative}.ui-dialog .ui-header,.ui-dialog .ui-footer{z-index:10;width:auto}.ui-dialog .ui-content,.ui-dialog .ui-footer{margin-top:-15px}.ui-checkbox,.ui-radio{position:relative;margin:.2em 0 .5em;z-index:1}.ui-checkbox .ui-btn,.ui-radio .ui-btn{margin:0;text-align:left;z-index:2}.ui-checkbox .ui-btn-inner,.ui-radio .ui-btn-inner{white-space:normal}.ui-checkbox .ui-btn-icon-left .ui-btn-inner,.ui-radio .ui-btn-icon-left .ui-btn-inner{padding-left:45px}.ui-checkbox .ui-btn-icon-right .ui-btn-inner,.ui-radio .ui-btn-icon-right .ui-btn-inner{padding-right:45px}.ui-checkbox .ui-icon,.ui-radio .ui-icon{top:1.1em}.ui-checkbox .ui-btn-icon-left .ui-icon,.ui-radio .ui-btn-icon-left .ui-icon{left:15px}.ui-checkbox .ui-btn-icon-right .ui-icon,.ui-radio .ui-btn-icon-right .ui-icon{right:15px}.ui-checkbox input,.ui-radio input{position:absolute;left:20px;top:50%;width:10px;height:10px;margin:-5px 0 0 0;outline:0!important;z-index:1}.ui-field-contain{padding:1.5em 0;margin:0;border-bottom-width:1px;overflow:visible}.ui-field-contain:first-child{border-top-width:0}@media all and (min-width:450px){.ui-field-contain{border-width:0;padding:0;margin:1em 0}}.ui-select{display:block;position:relative}.ui-select select{position:absolute;left:-9999px;top:-9999px}.ui-select .ui-btn{overflow:hidden}.ui-select .ui-btn select{cursor:pointer;-webkit-appearance:button;left:0;top:0;width:100%;height:100%;opacity:0;-ms-filter:"alpha(opacity=0)";filter:alpha(opacity=0)}@-moz-document url-prefix(){.ui-select .ui-btn select{opacity:.0001}}.ui-select .ui-btn select.ui-select-nativeonly{opacity:1;text-indent:0}.ui-select .ui-btn-icon-right .ui-btn-inner{padding-right:45px}.ui-select .ui-btn-icon-right .ui-icon{right:15px}label.ui-select{font-size:16px;line-height:1.4;font-weight:normal;margin:0 0 .3em;display:block}.ui-select .ui-btn-text,.ui-selectmenu .ui-btn-text{display:block;min-height:1em}.ui-select .ui-btn-text{text-overflow:ellipsis;overflow:hidden}.ui-selectmenu{position:absolute;padding:0;z-index:100!important;width:80%;max-width:350px;padding:6px}.ui-selectmenu .ui-listview{margin:0}.ui-selectmenu .ui-btn.ui-li-divider{cursor:default}.ui-selectmenu-hidden{top:-9999px;left:-9999px}.ui-selectmenu-screen{position:absolute;top:0;left:0;width:100%;height:100%;z-index:99}.ui-screen-hidden,.ui-selectmenu-list .ui-li .ui-icon{display:none}.ui-selectmenu-list .ui-li .ui-icon{display:block}.ui-li.ui-selectmenu-placeholder{display:none}.ui-selectmenu .ui-header .ui-title{margin:.6em 46px .8em}@media all and (min-width:450px){label.ui-select{display:inline-block;width:20%;margin:0 2% 0 0}.ui-select{width:60%;display:inline-block}}.ui-selectmenu .ui-header h1:after{content:'.';visibility:hidden}label.ui-input-text{font-size:16px;line-height:1.4;display:block;font-weight:normal;margin:0 0 .3em}input.ui-input-text,textarea.ui-input-text{background-image:none;padding:.4em;line-height:1.4;font-size:16px;display:block;width:95%}input.ui-input-text{-webkit-appearance:none}textarea.ui-input-text{height:50px;-webkit-transition:height 200ms linear;-moz-transition:height 200ms linear;-o-transition:height 200ms linear;transition:height 200ms linear}.ui-input-search{padding:0 30px;width:77%;background-position:8px 50%;background-repeat:no-repeat;position:relative}.ui-input-search input.ui-input-text{border:0;width:98%;padding:.4em 0;margin:0;display:block;background:transparent none;outline:0!important}.ui-input-search .ui-input-clear{position:absolute;right:0;top:50%;margin-top:-14px}.ui-input-search .ui-input-clear-hidden{display:none}@media all and (min-width:450px){label.ui-input-text{vertical-align:top;display:inline-block;width:20%;margin:0 2% 0 0}input.ui-input-text,textarea.ui-input-text,.ui-input-search{width:60%;display:inline-block}.ui-input-search{width:50%}.ui-input-search input.ui-input-text{width:98%}}.ui-listview{margin:0;counter-reset:listnumbering}.ui-content .ui-listview{margin:-15px}.ui-content .ui-listview-inset{margin:1em 0}.ui-listview,.ui-li{list-style:none;padding:0}.ui-li,.ui-li.ui-field-contain{display:block;margin:0;position:relative;overflow:visible;text-align:left;border-width:0;border-top-width:1px}.ui-li .ui-btn-text a.ui-link-inherit{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.ui-li-divider,.ui-li-static{padding:.5em 15px;font-size:14px;font-weight:bold}.ui-li-divider{counter-reset:listnumbering}ol.ui-listview .ui-link-inherit:before,ol.ui-listview .ui-li-static:before,.ui-li-dec{font-size:.8em;display:inline-block;padding-right:.3em;font-weight:normal;counter-increment:listnumbering;content:counter(listnumbering) ". "}ol.ui-listview .ui-li-jsnumbering:before{content:""!important}.ui-listview-inset .ui-li{border-right-width:1px;border-left-width:1px}.ui-li:last-child,.ui-li.ui-field-contain:last-child{border-bottom-width:1px}.ui-li>.ui-btn-inner{display:block;position:relative;padding:0}.ui-li .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li{padding:.7em 75px .7em 15px;display:block}.ui-li-has-thumb .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-thumb{min-height:60px;padding-left:100px}.ui-li-has-icon .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-icon{min-height:20px;padding-left:40px}.ui-li-heading{font-size:16px;font-weight:bold;display:block;margin:.6em 0;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.ui-li-desc{font-size:12px;font-weight:normal;display:block;margin:-.5em 0 .6em;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.ui-li-thumb,.ui-li-icon{position:absolute;left:1px;top:0;max-height:80px;max-width:80px}.ui-li-icon{max-height:40px;max-width:40px;left:10px;top:.9em}.ui-li-thumb,.ui-li-icon,.ui-li-content{float:left;margin-right:10px}.ui-li-aside{float:right;width:50%;text-align:right;margin:.3em 0}@media all and (min-width:480px){.ui-li-aside{width:45%}}.ui-li-divider{cursor:default}.ui-li-has-alt .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-alt{padding-right:95px}.ui-li-count{position:absolute;font-size:11px;font-weight:bold;padding:.2em .5em;top:50%;margin-top:-.9em;right:38px}.ui-li-divider .ui-li-count,.ui-li-static .ui-li-count{right:10px}.ui-li-has-alt .ui-li-count{right:55px}.ui-li-link-alt{position:absolute;width:40px;height:100%;border-width:0;border-left-width:1px;top:0;right:0;margin:0;padding:0}.ui-li-link-alt .ui-btn{overflow:hidden;position:absolute;right:8px;top:50%;margin:-11px 0 0 0;border-bottom-width:1px}.ui-li-link-alt .ui-btn-inner{padding:0;position:static}.ui-li-link-alt .ui-btn .ui-icon{right:50%;margin-right:-9px}.ui-listview-filter{border-width:0;overflow:hidden;margin:-15px -15px 15px -15px}.ui-listview-filter .ui-input-search{margin:5px;width:auto;display:block}.ui-listview-filter-inset{margin:-15px -5px -15px -5px;background:transparent}.ui-li.ui-screen-hidden{display:none}@media only screen and (min-device-width:768px) and (max-device-width:1024px){.ui-li .ui-btn-text{overflow:visible}}label.ui-slider{display:block}input.ui-slider-input{display:inline-block;width:50px}select.ui-slider-switch{display:none}div.ui-slider{position:relative;display:inline-block;overflow:visible;height:15px;padding:0;margin:0 2% 0 20px;top:4px;width:66%}a.ui-slider-handle{position:absolute;z-index:10;top:50%;width:28px;height:28px;margin-top:-15px;margin-left:-15px}a.ui-slider-handle .ui-btn-inner{padding-left:0;padding-right:0}@media all and (min-width:480px){label.ui-slider{display:inline-block;width:20%;margin:0 2% 0 0}div.ui-slider{width:45%}}div.ui-slider-switch{height:32px;overflow:hidden;margin-left:0}div.ui-slider-inneroffset{margin-left:50%;position:absolute;top:1px;height:100%;width:50%}div.ui-slider-handle-snapping{-webkit-transition:left 100ms linear}div.ui-slider-labelbg{position:absolute;top:0;margin:0;border-width:0}div.ui-slider-switch div.ui-slider-labelbg-a{width:60%;height:100%;left:0}div.ui-slider-switch div.ui-slider-labelbg-b{width:60%;height:100%;right:0}.ui-slider-switch-a div.ui-slider-labelbg-a,.ui-slider-switch-b div.ui-slider-labelbg-b{z-index:-1}.ui-slider-switch-a div.ui-slider-labelbg-b,.ui-slider-switch-b div.ui-slider-labelbg-a{z-index:0}div.ui-slider-switch a.ui-slider-handle{z-index:20;width:101%;height:32px;margin-top:-18px;margin-left:-101%}span.ui-slider-label{width:100%;position:absolute;height:32px;font-size:16px;text-align:center;line-height:2;background:0;border-color:transparent}span.ui-slider-label-a{left:-100%;margin-right:-1px}span.ui-slider-label-b{right:-100%;margin-left:-1px} \ No newline at end of file diff --git a/SignalR.Samples/Hubs/Chat/Chat.js b/SignalR.Samples/Hubs/Chat/Chat.js index 1c1fa3644..f088fbfde 100644 --- a/SignalR.Samples/Hubs/Chat/Chat.js +++ b/SignalR.Samples/Hubs/Chat/Chat.js @@ -9,12 +9,24 @@ $(function () { $('#messages').html(''); } + function refreshMessages() { refreshList($('#messages')); } + function clearUsers() { $('#users').html(''); } + function refreshUsers() { refreshList($('#users')); } + + function refreshList(list) { + if (list.is('.ui-listview')) { + list.listview('refresh'); + } + } + function addMessage(content, type) { var e = $('
  • ').html(content).appendTo($('#messages')); + refreshMessages(); + if (type) { e.addClass(type); } @@ -32,6 +44,7 @@ $(function () { $.each(users, function () { chat.addUser(this, true); }); + refreshUsers(); $('#new-message').focus(); }); @@ -52,6 +65,7 @@ $(function () { chat.addMessageContent = function (id, content) { var e = $('#m-' + id).append(content); + refreshMessages(); updateUnread(); e[0].scrollIntoView(); }; @@ -65,6 +79,7 @@ $(function () { var e = $('#new-message-template').tmpl(data) .appendTo($('#messages')); + refreshMessages(); updateUnread(); e[0].scrollIntoView(); }; @@ -82,6 +97,7 @@ $(function () { var e = $('#new-user-template').tmpl(data) .appendTo($('#users')); + refreshUsers(); if (!exists && this.name != user.Name) { addMessage(user.Name + ' just entered ' + this.room, 'notification'); @@ -98,6 +114,7 @@ $(function () { hash: newUser.Hash }) ); + refreshUsers(); if (oldUser.Name === this.name) { addMessage('Your name is now ' + newUser.Name, 'notification'); diff --git a/SignalR.Samples/Hubs/Chat/Mobile.css b/SignalR.Samples/Hubs/Chat/Mobile.css new file mode 100644 index 000000000..353ce18f0 --- /dev/null +++ b/SignalR.Samples/Hubs/Chat/Mobile.css @@ -0,0 +1,23 @@ +#messages, #users { + margin: 0; +} + +#messages .ui-li, #users .ui-li { + font-weight: normal; +} + +#users .ui-li-icon { + -webkit-background-clip: border-box; + -moz-background-clip: border-box; + background-clip: border; +} +#users .ui-li-icon.ui-corner-tl { + -moz-border-radius-topleft: 0; + -webkit-border-top-left-radius: 0; + border-top-left-radius: 0; +} +#users .ui-li-icon.ui-corner-bl { + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; +} diff --git a/SignalR.Samples/Hubs/Chat/mobile.htm b/SignalR.Samples/Hubs/Chat/mobile.htm new file mode 100644 index 000000000..f39b5efa1 --- /dev/null +++ b/SignalR.Samples/Hubs/Chat/mobile.htm @@ -0,0 +1,53 @@ + + + + SignalR Chat + + + + + + + + + + + + + + + +
    +
    +

    SignalR Chat

    +
    +
    +
    +

    Messages

    +
      +
    +
    +
    +

    Users

    +
      +
    +
    +
    +
    +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/SignalR.Samples/Scripts/jquery.mobile-1.0b2.js b/SignalR.Samples/Scripts/jquery.mobile-1.0b2.js new file mode 100644 index 000000000..ab546a786 --- /dev/null +++ b/SignalR.Samples/Scripts/jquery.mobile-1.0b2.js @@ -0,0 +1,6259 @@ +/*! + * jQuery Mobile v1.0b2 + * http://jquerymobile.com/ + * + * Copyright 2010, jQuery Project + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + */ +/*! + * jQuery UI Widget @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Widget + */ +(function( $, undefined ) { + +// jQuery 1.4+ +if ( $.cleanData ) { + var _cleanData = $.cleanData; + $.cleanData = function( elems ) { + for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { + $( elem ).triggerHandler( "remove" ); + } + _cleanData( elems ); + }; +} else { + var _remove = $.fn.remove; + $.fn.remove = function( selector, keepData ) { + return this.each(function() { + if ( !keepData ) { + if ( !selector || $.filter( selector, [ this ] ).length ) { + $( "*", this ).add( [ this ] ).each(function() { + $( this ).triggerHandler( "remove" ); + }); + } + } + return _remove.call( $(this), selector, keepData ); + }); + }; +} + +$.widget = function( name, base, prototype ) { + var namespace = name.split( "." )[ 0 ], + fullName; + name = name.split( "." )[ 1 ]; + fullName = namespace + "-" + name; + + if ( !prototype ) { + prototype = base; + base = $.Widget; + } + + // create selector for plugin + $.expr[ ":" ][ fullName ] = function( elem ) { + return !!$.data( elem, name ); + }; + + $[ namespace ] = $[ namespace ] || {}; + $[ namespace ][ name ] = function( options, element ) { + // allow instantiation without initializing for simple inheritance + if ( arguments.length ) { + this._createWidget( options, element ); + } + }; + + var basePrototype = new base(); + // we need to make the options hash a property directly on the new instance + // otherwise we'll modify the options hash on the prototype that we're + // inheriting from +// $.each( basePrototype, function( key, val ) { +// if ( $.isPlainObject(val) ) { +// basePrototype[ key ] = $.extend( {}, val ); +// } +// }); + basePrototype.options = $.extend( true, {}, basePrototype.options ); + $[ namespace ][ name ].prototype = $.extend( true, basePrototype, { + namespace: namespace, + widgetName: name, + widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name, + widgetBaseClass: fullName + }, prototype ); + + $.widget.bridge( name, $[ namespace ][ name ] ); +}; + +$.widget.bridge = function( name, object ) { + $.fn[ name ] = function( options ) { + var isMethodCall = typeof options === "string", + args = Array.prototype.slice.call( arguments, 1 ), + returnValue = this; + + // allow multiple hashes to be passed on init + options = !isMethodCall && args.length ? + $.extend.apply( null, [ true, options ].concat(args) ) : + options; + + // prevent calls to internal methods + if ( isMethodCall && options.charAt( 0 ) === "_" ) { + return returnValue; + } + + if ( isMethodCall ) { + this.each(function() { + var instance = $.data( this, name ); + if ( !instance ) { + throw "cannot call methods on " + name + " prior to initialization; " + + "attempted to call method '" + options + "'"; + } + if ( !$.isFunction( instance[options] ) ) { + throw "no such method '" + options + "' for " + name + " widget instance"; + } + var methodValue = instance[ options ].apply( instance, args ); + if ( methodValue !== instance && methodValue !== undefined ) { + returnValue = methodValue; + return false; + } + }); + } else { + this.each(function() { + var instance = $.data( this, name ); + if ( instance ) { + instance.option( options || {} )._init(); + } else { + $.data( this, name, new object( options, this ) ); + } + }); + } + + return returnValue; + }; +}; + +$.Widget = function( options, element ) { + // allow instantiation without initializing for simple inheritance + if ( arguments.length ) { + this._createWidget( options, element ); + } +}; + +$.Widget.prototype = { + widgetName: "widget", + widgetEventPrefix: "", + options: { + disabled: false + }, + _createWidget: function( options, element ) { + // $.widget.bridge stores the plugin instance, but we do it anyway + // so that it's stored even before the _create function runs + $.data( element, this.widgetName, this ); + this.element = $( element ); + this.options = $.extend( true, {}, + this.options, + this._getCreateOptions(), + options ); + + var self = this; + this.element.bind( "remove." + this.widgetName, function() { + self.destroy(); + }); + + this._create(); + this._trigger( "create" ); + this._init(); + }, + _getCreateOptions: function() { + var options = {}; + if ( $.metadata ) { + options = $.metadata.get( element )[ this.widgetName ]; + } + return options; + }, + _create: function() {}, + _init: function() {}, + + destroy: function() { + this.element + .unbind( "." + this.widgetName ) + .removeData( this.widgetName ); + this.widget() + .unbind( "." + this.widgetName ) + .removeAttr( "aria-disabled" ) + .removeClass( + this.widgetBaseClass + "-disabled " + + "ui-state-disabled" ); + }, + + widget: function() { + return this.element; + }, + + option: function( key, value ) { + var options = key; + + if ( arguments.length === 0 ) { + // don't return a reference to the internal hash + return $.extend( {}, this.options ); + } + + if (typeof key === "string" ) { + if ( value === undefined ) { + return this.options[ key ]; + } + options = {}; + options[ key ] = value; + } + + this._setOptions( options ); + + return this; + }, + _setOptions: function( options ) { + var self = this; + $.each( options, function( key, value ) { + self._setOption( key, value ); + }); + + return this; + }, + _setOption: function( key, value ) { + this.options[ key ] = value; + + if ( key === "disabled" ) { + this.widget() + [ value ? "addClass" : "removeClass"]( + this.widgetBaseClass + "-disabled" + " " + + "ui-state-disabled" ) + .attr( "aria-disabled", value ); + } + + return this; + }, + + enable: function() { + return this._setOption( "disabled", false ); + }, + disable: function() { + return this._setOption( "disabled", true ); + }, + + _trigger: function( type, event, data ) { + var callback = this.options[ type ]; + + event = $.Event( event ); + event.type = ( type === this.widgetEventPrefix ? + type : + this.widgetEventPrefix + type ).toLowerCase(); + data = data || {}; + + // copy original event properties over to the new event + // this would happen if we could call $.event.fix instead of $.Event + // but we don't have a way to force an event to be fixed multiple times + if ( event.originalEvent ) { + for ( var i = $.event.props.length, prop; i; ) { + prop = $.event.props[ --i ]; + event[ prop ] = event.originalEvent[ prop ]; + } + } + + this.element.trigger( event, data ); + + return !( $.isFunction(callback) && + callback.call( this.element[0], event, data ) === false || + event.isDefaultPrevented() ); + } +}; + +})( jQuery ); +/* +* jQuery Mobile Framework : widget factory extentions for mobile +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +(function( $, undefined ) { + +$.widget( "mobile.widget", { + _getCreateOptions: function() { + + var elem = this.element, + options = {}; + + $.each( this.options, function( option ) { + + var value = elem.jqmData( option.replace( /[A-Z]/g, function( c ) { + return "-" + c.toLowerCase(); + }) + ); + + if ( value !== undefined ) { + options[ option ] = value; + } + }); + + return options; + } +}); + +})( jQuery ); +/* +* jQuery Mobile Framework : resolution and CSS media query related helpers and behavior +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ +(function( $, undefined ) { + +var $window = $( window ), + $html = $( "html" ); + +/* $.mobile.media method: pass a CSS media type or query and get a bool return + note: this feature relies on actual media query support for media queries, though types will work most anywhere + examples: + $.mobile.media('screen') //>> tests for screen media type + $.mobile.media('screen and (min-width: 480px)') //>> tests for screen media type with window width > 480px + $.mobile.media('@media screen and (-webkit-min-device-pixel-ratio: 2)') //>> tests for webkit 2x pixel ratio (iPhone 4) +*/ +$.mobile.media = (function() { + // TODO: use window.matchMedia once at least one UA implements it + var cache = {}, + testDiv = $( "
    " ), + fakeBody = $( "" ).append( testDiv ); + + return function( query ) { + if ( !( query in cache ) ) { + var styleBlock = document.createElement( "style" ), + cssrule = "@media " + query + " { #jquery-mediatest { position:absolute; } }"; + + //must set type for IE! + styleBlock.type = "text/css"; + + if ( styleBlock.styleSheet ){ + styleBlock.styleSheet.cssText = cssrule; + } else { + styleBlock.appendChild( document.createTextNode(cssrule) ); + } + + $html.prepend( fakeBody ).prepend( styleBlock ); + cache[ query ] = testDiv.css( "position" ) === "absolute"; + fakeBody.add( styleBlock ).remove(); + } + return cache[ query ]; + }; +})(); + +})(jQuery);/* +* jQuery Mobile Framework : support tests +* Copyright (c) jQuery Project +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. +*/ + +(function( $, undefined ) { + +var fakeBody = $( "" ).prependTo( "html" ), + fbCSS = fakeBody[ 0 ].style, + vendors = [ "webkit", "moz", "o" ], + webos = "palmGetResource" in window, //only used to rule out scrollTop + bb = window.blackberry; //only used to rule out box shadow, as it's filled opaque on BB + +// thx Modernizr +function propExists( prop ) { + var uc_prop = prop.charAt( 0 ).toUpperCase() + prop.substr( 1 ), + props = ( prop + " " + vendors.join( uc_prop + " " ) + uc_prop ).split( " " ); + + for ( var v in props ){ + if ( fbCSS[ v ] !== undefined ) { + return true; + } + } +} + +// Test for dynamic-updating base tag support ( allows us to avoid href,src attr rewriting ) +function baseTagTest() { + var fauxBase = location.protocol + "//" + location.host + location.pathname + "ui-dir/", + base = $( "head base" ), + fauxEle = null, + href = "", + link, rebase; + + if ( !base.length ) { + base = fauxEle = $( "", { "href": fauxBase }).appendTo( "head" ); + } else { + href = base.attr( "href" ); + } + + link = $( "" ).prependTo( fakeBody ); + rebase = link[ 0 ].href; + base[ 0 ].href = href ? href : location.pathname; + + if ( fauxEle ) { + fauxEle.remove(); + } + return rebase.indexOf( fauxBase ) === 0; +} + + +// non-UA-based IE version check by James Padolsey, modified by jdalton - from http://gist.github.com/527683 +// allows for inclusion of IE 6+, including Windows Mobile 7 +$.mobile.browser = {}; +$.mobile.browser.ie = (function() { + var v = 3, + div = document.createElement( "div" ), + a = div.all || []; + + while ( div.innerHTML = "", a[ 0 ] ); + + return v > 4 ? v : !v; +})(); + + +$.extend( $.support, { + orientation: "orientation" in window, + touch: "ontouchend" in document, + cssTransitions: "WebKitTransitionEvent" in window, + pushState: !!history.pushState, + mediaquery: $.mobile.media( "only all" ), + cssPseudoElement: !!propExists( "content" ), + boxShadow: !!propExists( "boxShadow" ) && !bb, + scrollTop: ( "pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[ 0 ] ) && !webos, + dynamicBaseTag: baseTagTest(), + // TODO: This is a weak test. We may want to beef this up later. + eventCapture: "addEventListener" in document +}); + +fakeBody.remove(); + + +// $.mobile.ajaxBlacklist is used to override ajaxEnabled on platforms that have known conflicts with hash history updates (BB5, Symbian) +// or that generally work better browsing in regular http for full page refreshes (Opera Mini) +// Note: This detection below is used as a last resort. +// We recommend only using these detection methods when all other more reliable/forward-looking approaches are not possible +var nokiaLTE7_3 = (function(){ + + var ua = window.navigator.userAgent; + + //The following is an attempt to match Nokia browsers that are running Symbian/s60, with webkit, version 7.3 or older + return ua.indexOf( "Nokia" ) > -1 && + ( ua.indexOf( "Symbian/3" ) > -1 || ua.indexOf( "Series60/5" ) > -1 ) && + ua.indexOf( "AppleWebKit" ) > -1 && + ua.match( /(BrowserNG|NokiaBrowser)\/7\.[0-3]/ ); +})(); + +$.mobile.ajaxBlacklist = + // BlackBerry browsers, pre-webkit + window.blackberry && !window.WebKitPoint || + // Opera Mini + window.operamini && Object.prototype.toString.call( window.operamini ) === "[object OperaMini]" || + // Symbian webkits pre 7.3 + nokiaLTE7_3; + +// Lastly, this workaround is the only way we've found so far to get pre 7.3 Symbian webkit devices +// to render the stylesheets when they're referenced before this script, as we'd recommend doing. +// This simply reappends the CSS in place, which for some reason makes it apply +if ( nokiaLTE7_3 ) { + $(function() { + $( "head link[rel=stylesheet]" ).attr( "rel", "alternate stylesheet" ).attr( "rel", "stylesheet" ); + }); +} + +// For ruling out shadows via css +if ( !$.support.boxShadow ) { + $( "html" ).addClass( "ui-mobile-nosupport-boxshadow" ); +} + +})( jQuery );/* +* jQuery Mobile Framework : "mouse" plugin +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +// This plugin is an experiment for abstracting away the touch and mouse +// events so that developers don't have to worry about which method of input +// the device their document is loaded on supports. +// +// The idea here is to allow the developer to register listeners for the +// basic mouse events, such as mousedown, mousemove, mouseup, and click, +// and the plugin will take care of registering the correct listeners +// behind the scenes to invoke the listener at the fastest possible time +// for that device, while still retaining the order of event firing in +// the traditional mouse environment, should multiple handlers be registered +// on the same element for different events. +// +// The current version exposes the following virtual events to jQuery bind methods: +// "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel" + +(function( $, window, document, undefined ) { + +var dataPropertyName = "virtualMouseBindings", + touchTargetPropertyName = "virtualTouchID", + virtualEventNames = "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split( " " ), + touchEventProps = "clientX clientY pageX pageY screenX screenY".split( " " ), + activeDocHandlers = {}, + resetTimerID = 0, + startX = 0, + startY = 0, + didScroll = false, + clickBlockList = [], + blockMouseTriggers = false, + blockTouchTriggers = false, + eventCaptureSupported = $.support.eventCapture, + $document = $( document ), + nextTouchID = 1, + lastTouchID = 0; + +$.vmouse = { + moveDistanceThreshold: 10, + clickDistanceThreshold: 10, + resetTimerDuration: 1500 +}; + +function getNativeEvent( event ) { + + while ( event && typeof event.originalEvent !== "undefined" ) { + event = event.originalEvent; + } + return event; +} + +function createVirtualEvent( event, eventType ) { + + var t = event.type, + oe, props, ne, prop, ct, touch, i, j; + + event = $.Event(event); + event.type = eventType; + + oe = event.originalEvent; + props = $.event.props; + + // copy original event properties over to the new event + // this would happen if we could call $.event.fix instead of $.Event + // but we don't have a way to force an event to be fixed multiple times + if ( oe ) { + for ( i = props.length, prop; i; ) { + prop = props[ --i ]; + event[ prop ] = oe[ prop ]; + } + } + + if ( t.search(/^touch/) !== -1 ) { + ne = getNativeEvent( oe ); + t = ne.touches; + ct = ne.changedTouches; + touch = ( t && t.length ) ? t[0] : ( (ct && ct.length) ? ct[ 0 ] : undefined ); + + if ( touch ) { + for ( j = 0, len = touchEventProps.length; j < len; j++){ + prop = touchEventProps[ j ]; + event[ prop ] = touch[ prop ]; + } + } + } + + return event; +} + +function getVirtualBindingFlags( element ) { + + var flags = {}, + b, k; + + while ( element ) { + + b = $.data( element, dataPropertyName ); + + for ( k in b ) { + if ( b[ k ] ) { + flags[ k ] = flags.hasVirtualBinding = true; + } + } + element = element.parentNode; + } + return flags; +} + +function getClosestElementWithVirtualBinding( element, eventType ) { + var b; + while ( element ) { + + b = $.data( element, dataPropertyName ); + + if ( b && ( !eventType || b[ eventType ] ) ) { + return element; + } + element = element.parentNode; + } + return null; +} + +function enableTouchBindings() { + blockTouchTriggers = false; +} + +function disableTouchBindings() { + blockTouchTriggers = true; +} + +function enableMouseBindings() { + lastTouchID = 0; + clickBlockList.length = 0; + blockMouseTriggers = false; + + // When mouse bindings are enabled, our + // touch bindings are disabled. + disableTouchBindings(); +} + +function disableMouseBindings() { + // When mouse bindings are disabled, our + // touch bindings are enabled. + enableTouchBindings(); +} + +function startResetTimer() { + clearResetTimer(); + resetTimerID = setTimeout(function(){ + resetTimerID = 0; + enableMouseBindings(); + }, $.vmouse.resetTimerDuration ); +} + +function clearResetTimer() { + if ( resetTimerID ){ + clearTimeout( resetTimerID ); + resetTimerID = 0; + } +} + +function triggerVirtualEvent( eventType, event, flags ) { + var defaultPrevented = false, + ve; + + if ( ( flags && flags[ eventType ] ) || + ( !flags && getClosestElementWithVirtualBinding( event.target, eventType ) ) ) { + + ve = createVirtualEvent( event, eventType ); + + $( event.target).trigger( ve ); + + defaultPrevented = ve.isDefaultPrevented(); + } + + return defaultPrevented; +} + +function mouseEventCallback( event ) { + var touchID = $.data(event.target, touchTargetPropertyName); + + if ( !blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID ) ){ + triggerVirtualEvent( "v" + event.type, event ); + } +} + +function handleTouchStart( event ) { + + var touches = getNativeEvent( event ).touches, + target, flags; + + if ( touches && touches.length === 1 ) { + + target = event.target; + flags = getVirtualBindingFlags( target ); + + if ( flags.hasVirtualBinding ) { + + lastTouchID = nextTouchID++; + $.data( target, touchTargetPropertyName, lastTouchID ); + + clearResetTimer(); + + disableMouseBindings(); + didScroll = false; + + var t = getNativeEvent( event ).touches[ 0 ]; + startX = t.pageX; + startY = t.pageY; + + triggerVirtualEvent( "vmouseover", event, flags ); + triggerVirtualEvent( "vmousedown", event, flags ); + } + } +} + +function handleScroll( event ) { + if ( blockTouchTriggers ) { + return; + } + + if ( !didScroll ) { + triggerVirtualEvent( "vmousecancel", event, getVirtualBindingFlags( event.target ) ); + } + + didScroll = true; + startResetTimer(); +} + +function handleTouchMove( event ) { + if ( blockTouchTriggers ) { + return; + } + + var t = getNativeEvent( event ).touches[ 0 ], + didCancel = didScroll, + moveThreshold = $.vmouse.moveDistanceThreshold; + didScroll = didScroll || + ( Math.abs(t.pageX - startX) > moveThreshold || + Math.abs(t.pageY - startY) > moveThreshold ), + flags = getVirtualBindingFlags( event.target ); + + if ( didScroll && !didCancel ) { + triggerVirtualEvent( "vmousecancel", event, flags ); + } + + triggerVirtualEvent( "vmousemove", event, flags ); + startResetTimer(); +} + +function handleTouchEnd( event ) { + if ( blockTouchTriggers ) { + return; + } + + disableTouchBindings(); + + var flags = getVirtualBindingFlags( event.target ), + t; + triggerVirtualEvent( "vmouseup", event, flags ); + + if ( !didScroll ) { + if ( triggerVirtualEvent( "vclick", event, flags ) ) { + // The target of the mouse events that follow the touchend + // event don't necessarily match the target used during the + // touch. This means we need to rely on coordinates for blocking + // any click that is generated. + t = getNativeEvent( event ).changedTouches[ 0 ]; + clickBlockList.push({ + touchID: lastTouchID, + x: t.clientX, + y: t.clientY + }); + + // Prevent any mouse events that follow from triggering + // virtual event notifications. + blockMouseTriggers = true; + } + } + triggerVirtualEvent( "vmouseout", event, flags); + didScroll = false; + + startResetTimer(); +} + +function hasVirtualBindings( ele ) { + var bindings = $.data( ele, dataPropertyName ), + k; + + if ( bindings ) { + for ( k in bindings ) { + if ( bindings[ k ] ) { + return true; + } + } + } + return false; +} + +function dummyMouseHandler(){} + +function getSpecialEventObject( eventType ) { + var realType = eventType.substr( 1 ); + + return { + setup: function( data, namespace ) { + // If this is the first virtual mouse binding for this element, + // add a bindings object to its data. + + if ( !hasVirtualBindings( this ) ) { + $.data( this, dataPropertyName, {}); + } + + // If setup is called, we know it is the first binding for this + // eventType, so initialize the count for the eventType to zero. + var bindings = $.data( this, dataPropertyName ); + bindings[ eventType ] = true; + + // If this is the first virtual mouse event for this type, + // register a global handler on the document. + + activeDocHandlers[ eventType ] = ( activeDocHandlers[ eventType ] || 0 ) + 1; + + if ( activeDocHandlers[ eventType ] === 1 ) { + $document.bind( realType, mouseEventCallback ); + } + + // Some browsers, like Opera Mini, won't dispatch mouse/click events + // for elements unless they actually have handlers registered on them. + // To get around this, we register dummy handlers on the elements. + + $( this ).bind( realType, dummyMouseHandler ); + + // For now, if event capture is not supported, we rely on mouse handlers. + if ( eventCaptureSupported ) { + // If this is the first virtual mouse binding for the document, + // register our touchstart handler on the document. + + activeDocHandlers[ "touchstart" ] = ( activeDocHandlers[ "touchstart" ] || 0) + 1; + + if (activeDocHandlers[ "touchstart" ] === 1) { + $document.bind( "touchstart", handleTouchStart ) + .bind( "touchend", handleTouchEnd ) + + // On touch platforms, touching the screen and then dragging your finger + // causes the window content to scroll after some distance threshold is + // exceeded. On these platforms, a scroll prevents a click event from being + // dispatched, and on some platforms, even the touchend is suppressed. To + // mimic the suppression of the click event, we need to watch for a scroll + // event. Unfortunately, some platforms like iOS don't dispatch scroll + // events until *AFTER* the user lifts their finger (touchend). This means + // we need to watch both scroll and touchmove events to figure out whether + // or not a scroll happenens before the touchend event is fired. + + .bind( "touchmove", handleTouchMove ) + .bind( "scroll", handleScroll ); + } + } + }, + + teardown: function( data, namespace ) { + // If this is the last virtual binding for this eventType, + // remove its global handler from the document. + + --activeDocHandlers[ eventType ]; + + if ( !activeDocHandlers[ eventType ] ) { + $document.unbind( realType, mouseEventCallback ); + } + + if ( eventCaptureSupported ) { + // If this is the last virtual mouse binding in existence, + // remove our document touchstart listener. + + --activeDocHandlers[ "touchstart" ]; + + if ( !activeDocHandlers[ "touchstart" ] ) { + $document.unbind( "touchstart", handleTouchStart ) + .unbind( "touchmove", handleTouchMove ) + .unbind( "touchend", handleTouchEnd ) + .unbind( "scroll", handleScroll ); + } + } + + var $this = $( this ), + bindings = $.data( this, dataPropertyName ); + + // teardown may be called when an element was + // removed from the DOM. If this is the case, + // jQuery core may have already stripped the element + // of any data bindings so we need to check it before + // using it. + if ( bindings ) { + bindings[ eventType ] = false; + } + + // Unregister the dummy event handler. + + $this.unbind( realType, dummyMouseHandler ); + + // If this is the last virtual mouse binding on the + // element, remove the binding data from the element. + + if ( !hasVirtualBindings( this ) ) { + $this.removeData( dataPropertyName ); + } + } + }; +} + +// Expose our custom events to the jQuery bind/unbind mechanism. + +for ( var i = 0; i < virtualEventNames.length; i++ ){ + $.event.special[ virtualEventNames[ i ] ] = getSpecialEventObject( virtualEventNames[ i ] ); +} + +// Add a capture click handler to block clicks. +// Note that we require event capture support for this so if the device +// doesn't support it, we punt for now and rely solely on mouse events. +if ( eventCaptureSupported ) { + document.addEventListener( "click", function( e ){ + var cnt = clickBlockList.length, + target = e.target, + x, y, ele, i, o, touchID; + + if ( cnt ) { + x = e.clientX; + y = e.clientY; + threshold = $.vmouse.clickDistanceThreshold; + + // The idea here is to run through the clickBlockList to see if + // the current click event is in the proximity of one of our + // vclick events that had preventDefault() called on it. If we find + // one, then we block the click. + // + // Why do we have to rely on proximity? + // + // Because the target of the touch event that triggered the vclick + // can be different from the target of the click event synthesized + // by the browser. The target of a mouse/click event that is syntehsized + // from a touch event seems to be implementation specific. For example, + // some browsers will fire mouse/click events for a link that is near + // a touch event, even though the target of the touchstart/touchend event + // says the user touched outside the link. Also, it seems that with most + // browsers, the target of the mouse/click event is not calculated until the + // time it is dispatched, so if you replace an element that you touched + // with another element, the target of the mouse/click will be the new + // element underneath that point. + // + // Aside from proximity, we also check to see if the target and any + // of its ancestors were the ones that blocked a click. This is necessary + // because of the strange mouse/click target calculation done in the + // Android 2.1 browser, where if you click on an element, and there is a + // mouse/click handler on one of its ancestors, the target will be the + // innermost child of the touched element, even if that child is no where + // near the point of touch. + + ele = target; + + while ( ele ) { + for ( i = 0; i < cnt; i++ ) { + o = clickBlockList[ i ]; + touchID = 0; + + if ( ( ele === target && Math.abs( o.x - x ) < threshold && Math.abs( o.y - y ) < threshold ) || + $.data( ele, touchTargetPropertyName ) === o.touchID ) { + // XXX: We may want to consider removing matches from the block list + // instead of waiting for the reset timer to fire. + e.preventDefault(); + e.stopPropagation(); + return; + } + } + ele = ele.parentNode; + } + } + }, true); +} +})( jQuery, window, document ); +/* +* jQuery Mobile Framework : events +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ +(function( $, window, undefined ) { + +// add new event shortcuts +$.each( ( "touchstart touchmove touchend orientationchange throttledresize " + + "tap taphold swipe swipeleft swiperight scrollstart scrollstop" ).split( " " ), function( i, name ) { + + $.fn[ name ] = function( fn ) { + return fn ? this.bind( name, fn ) : this.trigger( name ); + }; + + $.attrFn[ name ] = true; +}); + +var supportTouch = $.support.touch, + scrollEvent = "touchmove scroll", + touchStartEvent = supportTouch ? "touchstart" : "mousedown", + touchStopEvent = supportTouch ? "touchend" : "mouseup", + touchMoveEvent = supportTouch ? "touchmove" : "mousemove"; + +function triggerCustomEvent( obj, eventType, event ) { + var originalType = event.type; + event.type = eventType; + $.event.handle.call( obj, event ); + event.type = originalType; +} + +// also handles scrollstop +$.event.special.scrollstart = { + + enabled: true, + + setup: function() { + + var thisObject = this, + $this = $( thisObject ), + scrolling, + timer; + + function trigger( event, state ) { + scrolling = state; + triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event ); + } + + // iPhone triggers scroll after a small delay; use touchmove instead + $this.bind( scrollEvent, function( event ) { + + if ( !$.event.special.scrollstart.enabled ) { + return; + } + + if ( !scrolling ) { + trigger( event, true ); + } + + clearTimeout( timer ); + timer = setTimeout(function() { + trigger( event, false ); + }, 50 ); + }); + } +}; + +// also handles taphold +$.event.special.tap = { + setup: function() { + var thisObject = this, + $this = $( thisObject ); + + $this.bind( "vmousedown", function( event ) { + + if ( event.which && event.which !== 1 ) { + return false; + } + + var touching = true, + origTarget = event.target, + origEvent = event.originalEvent, + timer; + + function clearTapHandlers() { + touching = false; + clearTimeout(timer); + + $this.unbind( "vclick", clickHandler ) + .unbind( "vmousecancel", clearTapHandlers ); + } + + function clickHandler(event) { + clearTapHandlers(); + + // ONLY trigger a 'tap' event if the start target is + // the same as the stop target. + if ( origTarget == event.target ) { + triggerCustomEvent( thisObject, "tap", event ); + } + } + + $this.bind( "vmousecancel", clearTapHandlers ) + .bind( "vclick", clickHandler ); + + timer = setTimeout(function() { + if ( touching ) { + triggerCustomEvent( thisObject, "taphold", event ); + } + }, 750 ); + }); + } +}; + +// also handles swipeleft, swiperight +$.event.special.swipe = { + scrollSupressionThreshold: 10, // More than this horizontal displacement, and we will suppress scrolling. + + durationThreshold: 1000, // More time than this, and it isn't a swipe. + + horizontalDistanceThreshold: 30, // Swipe horizontal displacement must be more than this. + + verticalDistanceThreshold: 75, // Swipe vertical displacement must be less than this. + + setup: function() { + var thisObject = this, + $this = $( thisObject ); + + $this.bind( touchStartEvent, function( event ) { + var data = event.originalEvent.touches ? + event.originalEvent.touches[ 0 ] : event, + start = { + time: ( new Date() ).getTime(), + coords: [ data.pageX, data.pageY ], + origin: $( event.target ) + }, + stop; + + function moveHandler( event ) { + + if ( !start ) { + return; + } + + var data = event.originalEvent.touches ? + event.originalEvent.touches[ 0 ] : event; + + stop = { + time: ( new Date() ).getTime(), + coords: [ data.pageX, data.pageY ] + }; + + // prevent scrolling + if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) { + event.preventDefault(); + } + } + + $this.bind( touchMoveEvent, moveHandler ) + .one( touchStopEvent, function( event ) { + $this.unbind( touchMoveEvent, moveHandler ); + + if ( start && stop ) { + if ( stop.time - start.time < $.event.special.swipe.durationThreshold && + Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold && + Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) { + + start.origin.trigger( "swipe" ) + .trigger( start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight" ); + } + } + start = stop = undefined; + }); + }); + } +}; + +(function( $, window ) { + // "Cowboy" Ben Alman + + var win = $( window ), + special_event, + get_orientation, + last_orientation; + + $.event.special.orientationchange = special_event = { + setup: function() { + // If the event is supported natively, return false so that jQuery + // will bind to the event using DOM methods. + if ( $.support.orientation ) { + return false; + } + + // Get the current orientation to avoid initial double-triggering. + last_orientation = get_orientation(); + + // Because the orientationchange event doesn't exist, simulate the + // event by testing window dimensions on resize. + win.bind( "throttledresize", handler ); + }, + teardown: function(){ + // If the event is not supported natively, return false so that + // jQuery will unbind the event using DOM methods. + if ( $.support.orientation ) { + return false; + } + + // Because the orientationchange event doesn't exist, unbind the + // resize event handler. + win.unbind( "throttledresize", handler ); + }, + add: function( handleObj ) { + // Save a reference to the bound event handler. + var old_handler = handleObj.handler; + + handleObj.handler = function( event ) { + // Modify event object, adding the .orientation property. + event.orientation = get_orientation(); + + // Call the originally-bound event handler and return its result. + return old_handler.apply( this, arguments ); + }; + } + }; + + // If the event is not supported natively, this handler will be bound to + // the window resize event to simulate the orientationchange event. + function handler() { + // Get the current orientation. + var orientation = get_orientation(); + + if ( orientation !== last_orientation ) { + // The orientation has changed, so trigger the orientationchange event. + last_orientation = orientation; + win.trigger( "orientationchange" ); + } + }; + + // Get the current page orientation. This method is exposed publicly, should it + // be needed, as jQuery.event.special.orientationchange.orientation() + $.event.special.orientationchange.orientation = get_orientation = function() { + var elem = document.documentElement; + return elem && elem.clientWidth / elem.clientHeight < 1.1 ? "portrait" : "landscape"; + }; + +})( jQuery, window ); + + +// throttled resize event +(function() { + + $.event.special.throttledresize = { + setup: function() { + $( this ).bind( "resize", handler ); + }, + teardown: function(){ + $( this ).unbind( "resize", handler ); + } + }; + + var throttle = 250, + handler = function() { + curr = ( new Date() ).getTime(); + diff = curr - lastCall; + + if ( diff >= throttle ) { + + lastCall = curr; + $( this ).trigger( "throttledresize" ); + + } else { + + if ( heldCall ) { + clearTimeout( heldCall ); + } + + // Promise a held call will still execute + heldCall = setTimeout( handler, throttle - diff ); + } + }, + lastCall = 0, + heldCall, + curr, + diff; +})(); + + +$.each({ + scrollstop: "scrollstart", + taphold: "tap", + swipeleft: "swipe", + swiperight: "swipe" +}, function( event, sourceEvent ) { + + $.event.special[ event ] = { + setup: function() { + $( this ).bind( sourceEvent, $.noop ); + } + }; +}); + +})( jQuery, this ); +/*! + * jQuery hashchange event - v1.3 - 7/21/2010 + * http://benalman.com/projects/jquery-hashchange-plugin/ + * + * Copyright (c) 2010 "Cowboy" Ben Alman + * Dual licensed under the MIT and GPL licenses. + * http://benalman.com/about/license/ + */ + +// Script: jQuery hashchange event +// +// *Version: 1.3, Last updated: 7/21/2010* +// +// Project Home - http://benalman.com/projects/jquery-hashchange-plugin/ +// GitHub - http://github.com/cowboy/jquery-hashchange/ +// Source - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js +// (Minified) - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped) +// +// About: License +// +// Copyright (c) 2010 "Cowboy" Ben Alman, +// Dual licensed under the MIT and GPL licenses. +// http://benalman.com/about/license/ +// +// About: Examples +// +// These working examples, complete with fully commented code, illustrate a few +// ways in which this plugin can be used. +// +// hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/ +// document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/ +// +// About: Support and Testing +// +// Information about what version or versions of jQuery this plugin has been +// tested with, what browsers it has been tested in, and where the unit tests +// reside (so you can test it yourself). +// +// jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2 +// Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5, +// Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5. +// Unit Tests - http://benalman.com/code/projects/jquery-hashchange/unit/ +// +// About: Known issues +// +// While this jQuery hashchange event implementation is quite stable and +// robust, there are a few unfortunate browser bugs surrounding expected +// hashchange event-based behaviors, independent of any JavaScript +// window.onhashchange abstraction. See the following examples for more +// information: +// +// Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/ +// Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/ +// WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/ +// Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/ +// +// Also note that should a browser natively support the window.onhashchange +// event, but not report that it does, the fallback polling loop will be used. +// +// About: Release History +// +// 1.3 - (7/21/2010) Reorganized IE6/7 Iframe code to make it more +// "removable" for mobile-only development. Added IE6/7 document.title +// support. Attempted to make Iframe as hidden as possible by using +// techniques from http://www.paciellogroup.com/blog/?p=604. Added +// support for the "shortcut" format $(window).hashchange( fn ) and +// $(window).hashchange() like jQuery provides for built-in events. +// Renamed jQuery.hashchangeDelay to and +// lowered its default value to 50. Added +// and properties plus document-domain.html +// file to address access denied issues when setting document.domain in +// IE6/7. +// 1.2 - (2/11/2010) Fixed a bug where coming back to a page using this plugin +// from a page on another domain would cause an error in Safari 4. Also, +// IE6/7 Iframe is now inserted after the body (this actually works), +// which prevents the page from scrolling when the event is first bound. +// Event can also now be bound before DOM ready, but it won't be usable +// before then in IE6/7. +// 1.1 - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug +// where browser version is incorrectly reported as 8.0, despite +// inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag. +// 1.0 - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special +// window.onhashchange functionality into a separate plugin for users +// who want just the basic event & back button support, without all the +// extra awesomeness that BBQ provides. This plugin will be included as +// part of jQuery BBQ, but also be available separately. + +(function($,window,undefined){ + '$:nomunge'; // Used by YUI compressor. + + // Reused string. + var str_hashchange = 'hashchange', + + // Method / object references. + doc = document, + fake_onhashchange, + special = $.event.special, + + // Does the browser support window.onhashchange? Note that IE8 running in + // IE7 compatibility mode reports true for 'onhashchange' in window, even + // though the event isn't supported, so also test document.documentMode. + doc_mode = doc.documentMode, + supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 ); + + // Get location.hash (or what you'd expect location.hash to be) sans any + // leading #. Thanks for making this necessary, Firefox! + function get_fragment( url ) { + url = url || location.href; + return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' ); + }; + + // Method: jQuery.fn.hashchange + // + // Bind a handler to the window.onhashchange event or trigger all bound + // window.onhashchange event handlers. This behavior is consistent with + // jQuery's built-in event handlers. + // + // Usage: + // + // > jQuery(window).hashchange( [ handler ] ); + // + // Arguments: + // + // handler - (Function) Optional handler to be bound to the hashchange + // event. This is a "shortcut" for the more verbose form: + // jQuery(window).bind( 'hashchange', handler ). If handler is omitted, + // all bound window.onhashchange event handlers will be triggered. This + // is a shortcut for the more verbose + // jQuery(window).trigger( 'hashchange' ). These forms are described in + // the section. + // + // Returns: + // + // (jQuery) The initial jQuery collection of elements. + + // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and + // $(elem).hashchange() for triggering, like jQuery does for built-in events. + $.fn[ str_hashchange ] = function( fn ) { + return fn ? this.bind( str_hashchange, fn ) : this.trigger( str_hashchange ); + }; + + // Property: jQuery.fn.hashchange.delay + // + // The numeric interval (in milliseconds) at which the + // polling loop executes. Defaults to 50. + + // Property: jQuery.fn.hashchange.domain + // + // If you're setting document.domain in your JavaScript, and you want hash + // history to work in IE6/7, not only must this property be set, but you must + // also set document.domain BEFORE jQuery is loaded into the page. This + // property is only applicable if you are supporting IE6/7 (or IE8 operating + // in "IE7 compatibility" mode). + // + // In addition, the property must be set to the + // path of the included "document-domain.html" file, which can be renamed or + // modified if necessary (note that the document.domain specified must be the + // same in both your main JavaScript as well as in this file). + // + // Usage: + // + // jQuery.fn.hashchange.domain = document.domain; + + // Property: jQuery.fn.hashchange.src + // + // If, for some reason, you need to specify an Iframe src file (for example, + // when setting document.domain as in ), you can + // do so using this property. Note that when using this property, history + // won't be recorded in IE6/7 until the Iframe src file loads. This property + // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7 + // compatibility" mode). + // + // Usage: + // + // jQuery.fn.hashchange.src = 'path/to/file.html'; + + $.fn[ str_hashchange ].delay = 50; + /* + $.fn[ str_hashchange ].domain = null; + $.fn[ str_hashchange ].src = null; + */ + + // Event: hashchange event + // + // Fired when location.hash changes. In browsers that support it, the native + // HTML5 window.onhashchange event is used, otherwise a polling loop is + // initialized, running every milliseconds to + // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7 + // compatibility" mode), a hidden Iframe is created to allow the back button + // and hash-based history to work. + // + // Usage as described in : + // + // > // Bind an event handler. + // > jQuery(window).hashchange( function(e) { + // > var hash = location.hash; + // > ... + // > }); + // > + // > // Manually trigger the event handler. + // > jQuery(window).hashchange(); + // + // A more verbose usage that allows for event namespacing: + // + // > // Bind an event handler. + // > jQuery(window).bind( 'hashchange', function(e) { + // > var hash = location.hash; + // > ... + // > }); + // > + // > // Manually trigger the event handler. + // > jQuery(window).trigger( 'hashchange' ); + // + // Additional Notes: + // + // * The polling loop and Iframe are not created until at least one handler + // is actually bound to the 'hashchange' event. + // * If you need the bound handler(s) to execute immediately, in cases where + // a location.hash exists on page load, via bookmark or page refresh for + // example, use jQuery(window).hashchange() or the more verbose + // jQuery(window).trigger( 'hashchange' ). + // * The event can be bound before DOM ready, but since it won't be usable + // before then in IE6/7 (due to the necessary Iframe), recommended usage is + // to bind it inside a DOM ready handler. + + // Override existing $.event.special.hashchange methods (allowing this plugin + // to be defined after jQuery BBQ in BBQ's source code). + special[ str_hashchange ] = $.extend( special[ str_hashchange ], { + + // Called only when the first 'hashchange' event is bound to window. + setup: function() { + // If window.onhashchange is supported natively, there's nothing to do.. + if ( supports_onhashchange ) { return false; } + + // Otherwise, we need to create our own. And we don't want to call this + // until the user binds to the event, just in case they never do, since it + // will create a polling loop and possibly even a hidden Iframe. + $( fake_onhashchange.start ); + }, + + // Called only when the last 'hashchange' event is unbound from window. + teardown: function() { + // If window.onhashchange is supported natively, there's nothing to do.. + if ( supports_onhashchange ) { return false; } + + // Otherwise, we need to stop ours (if possible). + $( fake_onhashchange.stop ); + } + + }); + + // fake_onhashchange does all the work of triggering the window.onhashchange + // event for browsers that don't natively support it, including creating a + // polling loop to watch for hash changes and in IE 6/7 creating a hidden + // Iframe to enable back and forward. + fake_onhashchange = (function(){ + var self = {}, + timeout_id, + + // Remember the initial hash so it doesn't get triggered immediately. + last_hash = get_fragment(), + + fn_retval = function(val){ return val; }, + history_set = fn_retval, + history_get = fn_retval; + + // Start the polling loop. + self.start = function() { + timeout_id || poll(); + }; + + // Stop the polling loop. + self.stop = function() { + timeout_id && clearTimeout( timeout_id ); + timeout_id = undefined; + }; + + // This polling loop checks every $.fn.hashchange.delay milliseconds to see + // if location.hash has changed, and triggers the 'hashchange' event on + // window when necessary. + function poll() { + var hash = get_fragment(), + history_hash = history_get( last_hash ); + + if ( hash !== last_hash ) { + history_set( last_hash = hash, history_hash ); + + $(window).trigger( str_hashchange ); + + } else if ( history_hash !== last_hash ) { + location.href = location.href.replace( /#.*/, '' ) + history_hash; + } + + timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay ); + }; + + // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + // vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv + // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + $.browser.msie && !supports_onhashchange && (function(){ + // Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8 + // when running in "IE7 compatibility" mode. + + var iframe, + iframe_src; + + // When the event is bound and polling starts in IE 6/7, create a hidden + // Iframe for history handling. + self.start = function(){ + if ( !iframe ) { + iframe_src = $.fn[ str_hashchange ].src; + iframe_src = iframe_src && iframe_src + get_fragment(); + + // Create hidden Iframe. Attempt to make Iframe as hidden as possible + // by using techniques from http://www.paciellogroup.com/blog/?p=604. + iframe = $('"); + + $(connection).trigger("onSending"); + + // Build the url + if (connection.data) { + url += "?connectionData=" + connection.data + "&transport=foreverFrame&clientId=" + window.escape(connection.clientId); + } else { + url += "?transport=foreverFrame&clientId=" + window.escape(connection.clientId); + } + + url += "&frameId=" + frameId; + + frame.prop("src", url); + //frame.data("signalr-connection-id", id); + //frame.data("signalr-connection", connection); + transportLogic.foreverFrame.connections[frameId] = connection; + + frame.bind("load", function () { + // The frame has finished loading (timeout or error), we need to refresh it + // var src = this.src.replace("/connect", "") + "&messageId=" + connection.messageId; + // this.src = src; + console.log("SignalR: forever frame iframe load event fired"); + }) + + $("body").append(frame); + connection.frame = frame; + connection.frameId = frameId; + if (onSuccess) { + onSuccess(); + } + }, + + send: function (connection, data) { + transportLogic.ajaxSend(connection, data, "foreverFrame"); + }, + + receive: function (connection, data) { + if (data) { + if (data.Messages) { + $.each(data.Messages, function () { + try { + $(connection).trigger("onReceived", [this]); + } + catch (e) { + if (console && console.log) { + console.log('Error raising received ' + e); + } + } + }); + } + connection.messageId = data.MessageId; + connection.groups = data.TransportData.Groups; + } + }, + + stop: function (connection) { + if (connection.frame) { + connection.frame.remove(); + delete transportLogic.foreverFrame.connections[connection.frameId]; + } + }, + + getConnection: function (id) { + return transportLogic.foreverFrame.connections[id]; + } + }, + longPolling: { start: function (connection, onSuccess, onFailed) { /// Starts the long polling connection @@ -300,7 +403,9 @@ instance.pollXhr = $.ajax(url, { global: false, + type: "POST", + data: { clientId: instance.clientId, messageId: messageId, @@ -308,7 +413,9 @@ transport: "longPolling", groups: (instance.groups || []).toString() }, + dataType: "json", + success: function (data) { var delay = 0; if (data) { @@ -338,6 +445,7 @@ poll(instance); } }, + error: function (data, textStatus) { if (textStatus === "abort") { return; @@ -362,31 +470,7 @@ }, send: function (connection, data) { - /// Sends data over this connection - /// The SignalR connection to send data over - /// The data to send - /// A callback to be invoked when the send has completed - $.ajax(connection.url + '/send', { - global: false, - type: "POST", - dataType: "json", - data: { - data: data, - transport: "longPolling", - clientId: connection.clientId - }, - success: function (result) { - if (result) { - $(connection).trigger("onReceived", [result]); - } - }, - error: function (data, textStatus) { - if (textStatus === "abort") { - return; - } - $(connection).trigger("onError", [data]); - } - }); + transportLogic.ajaxSend(connection, data, "longPolling"); }, stop: function (connection) { diff --git a/SignalR.Samples/Scripts/jquery.signalR.min.js b/SignalR.Samples/Scripts/jquery.signalR.min.js index e42482850..da018aa14 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.min.js +++ b/SignalR.Samples/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";var i,r;i=function(n){return new i.fn.init(n)},i.fn=i.prototype={init:function(n){this.url=n},start:function(r,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(r)==="function"?u=r:n.type(r)==="object"&&(n.extend(e,r),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,r){r=r||0;if(r>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[r],e=n.type(u)==="object"?u:i.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,r+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.clientId=t.ClientId,n(f).trigger("onStarting");var u=[],r=[];n.each(i.transports,function(n){r.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,r)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,r)<0?u=r:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this;return n(i).bind("onStarting",function(){t.call(i)}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),n}},i.fn.init.prototype=i.fn,i.transports={webSockets:{send:function(n,t){n.socket.send(t)},start:function(i,r,u){var f,o=!1,e;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){u();return}i.socket||(f=document.location.host+i.appRelativeUrl,n(i).trigger("onSending"),f+=i.data?"?connectionData="+i.data+"&transport=webSockets&clientId="+i.clientId:"?transport=webSockets&clientId="+i.clientId,e=document.location.protocol==="https:"?"wss://":"ws://",i.socket=new t.WebSocket(e+f),i.socket.onopen=function(){o=!0,r&&r()},i.socket.onclose=function(t){o?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):u&&u(),i.socket=null},i.socket.onmessage=function(r){var u=t.JSON.parse(r.data);u&&(u.Messages?n.each(u.Messages,function(){n(i).trigger("onReceived",[this])}):n(i).trigger("onReceived",[u]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},longPolling:{start:function(i,r){i.pollXhr&&i.stop(),i.messageId=null,t.setTimeout(function(){(function u(i){n(i).trigger("onSending");var r=i.messageId,e=r===null,f=i.url+(e?"/connect":"");i.pollXhr=n.ajax(f,{global:!1,type:"POST",data:{clientId:i.clientId,messageId:r,connectionData:i.data,transport:"longPolling",groups:(i.groups||[]).toString()},dataType:"json",success:function(r){var f=0;r&&(r.Messages&&n.each(r.Messages,function(){try{n(i).trigger("onReceived",[this])}catch(t){console&&console.log&&console.log("Error raising received "+t)}}),i.messageId=r.MessageId,n.type(r.TransportData.LongPollDelay)==="number"&&(f=r.TransportData.LongPollDelay),i.groups=r.TransportData.Groups),f>0?t.setTimeout(function(){u(i)},f):u(i)},error:function(r,f){if(f==="abort")return;n(i).trigger("onError",[r]),t.setTimeout(function(){u(i)},2e3)}})})(i),setTimeout(r,150)},250)},send:function(t,i){n.ajax(t.url+"/send",{global:!1,type:"POST",dataType:"json",data:{data:i,transport:"longPolling",clientId:t.clientId},success:function(i){i&&n(t).trigger("onReceived",[i])},error:function(i,r){if(r==="abort")return;n(t).trigger("onError",[i])}})},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},i.noConflict=function(){return n.connection===i&&(n.connection=r),i},n.connection&&(r=n.connection),n.connection=n.signalR=i})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var i,u,r;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";i=function(n){return new i.fn.init(n)},i.fn=i.prototype={init:function(n){this.url=n},start:function(r,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(r)==="function"?u=r:n.type(r)==="object"&&(n.extend(e,r),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,r){r=r||0;if(r>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[r],e=n.type(u)==="object"?u:i.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,r+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.clientId=t.ClientId,n(f).trigger("onStarting");var u=[],r=[];n.each(i.transports,function(n){r.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,r)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,r)<0?u=r:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this;return n(i).bind("onStarting",function(){t.call(i)}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),n}},i.fn.init.prototype=i.fn,r={ajaxSend:function(t,i,r){n.ajax(t.url+"/send",{global:!1,type:"POST",dataType:"json",data:{data:i,transport:r,clientId:t.clientId},success:function(i){i&&n(t).trigger("onReceived",[i])},error:function(i,r){if(r==="abort")return;n(t).trigger("onError",[i])}})},foreverFrame:{count:0,connections:{}}},i.transports={webSockets:{send:function(n,t){n.socket.send(t)},start:function(i,r,u){var f,o=!1,e;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){u();return}i.socket||(f=document.location.host+i.appRelativeUrl,n(i).trigger("onSending"),f+=i.data?"?connectionData="+i.data+"&transport=webSockets&clientId="+i.clientId:"?transport=webSockets&clientId="+i.clientId,e=document.location.protocol==="https:"?"wss://":"ws://",i.socket=new t.WebSocket(e+f),i.socket.onopen=function(){o=!0,r&&r()},i.socket.onclose=function(t){o?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):u&&u(),i.socket=null},i.socket.onmessage=function(r){var u=t.JSON.parse(r.data);u&&(u.Messages?n.each(u.Messages,function(){n(i).trigger("onReceived",[this])}):n(i).trigger("onReceived",[u]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},foreverFrame:{start:function(i,u){var s=r.foreverFrame.count+=1,o=i.url+"/connect",e=n("");n(i).trigger("onSending"),o+=i.data?"?connectionData="+i.data+"&transport=foreverFrame&clientId="+t.escape(i.clientId):"?transport=foreverFrame&clientId="+t.escape(i.clientId),o+="&frameId="+s,e.prop("src",o),r.foreverFrame.connections[s]=i,e.bind("load",function(){console.log("SignalR: forever frame iframe load event fired")}),n("body").append(e),i.frame=e,i.frameId=s,u&&u()},send:function(n,t){r.ajaxSend(n,t,"foreverFrame")},receive:function(t,i){i&&(i.Messages&&n.each(i.Messages,function(){try{n(t).trigger("onReceived",[this])}catch(i){console&&console.log&&console.log("Error raising received "+i)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups)},stop:function(n){n.frame&&(n.frame.remove(),delete r.foreverFrame.connections[n.frameId])},getConnection:function(n){return r.foreverFrame.connections[n]}},longPolling:{start:function(i,r){i.pollXhr&&i.stop(),i.messageId=null,t.setTimeout(function(){(function u(i){n(i).trigger("onSending");var r=i.messageId,e=r===null,f=i.url+(e?"/connect":"");i.pollXhr=n.ajax(f,{global:!1,type:"POST",data:{clientId:i.clientId,messageId:r,connectionData:i.data,transport:"longPolling",groups:(i.groups||[]).toString()},dataType:"json",success:function(r){var f=0;r&&(r.Messages&&n.each(r.Messages,function(){try{n(i).trigger("onReceived",[this])}catch(t){console&&console.log&&console.log("Error raising received "+t)}}),i.messageId=r.MessageId,n.type(r.TransportData.LongPollDelay)==="number"&&(f=r.TransportData.LongPollDelay),i.groups=r.TransportData.Groups),f>0?t.setTimeout(function(){u(i)},f):u(i)},error:function(r,f){if(f==="abort")return;n(i).trigger("onError",[r]),t.setTimeout(function(){u(i)},2e3)}})})(i),setTimeout(r,150)},250)},send:function(n,t){r.ajaxSend(n,t,"longPolling")},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},i.noConflict=function(){return n.connection===i&&(n.connection=u),i},n.connection&&(u=n.connection),n.connection=n.signalR=i})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR/PreApplicationStart.cs b/SignalR/PreApplicationStart.cs index 88164f7fe..ef4d44166 100644 --- a/SignalR/PreApplicationStart.cs +++ b/SignalR/PreApplicationStart.cs @@ -15,6 +15,7 @@ public static void Start() { DynamicModuleUtility.RegisterModule(typeof(HubModule)); + TransportManager.Register("foreverFrame", context => new ForeverFrameTransport(context, DependencyResolver.Resolve())); TransportManager.Register("longPolling", context => new LongPollingTransport(context, DependencyResolver.Resolve())); TransportManager.Register("forever", context => new ForeverTransport(context, DependencyResolver.Resolve())); } diff --git a/SignalR/Scripts/jquery.signalR.js b/SignalR/Scripts/jquery.signalR.js index f61c7de38..4e007068f 100644 --- a/SignalR/Scripts/jquery.signalR.js +++ b/SignalR/Scripts/jquery.signalR.js @@ -202,6 +202,37 @@ signalR.fn.init.prototype = signalR.fn; // Transports + var transportLogic = { + ajaxSend: function (connection, data, transport) { + $.ajax(connection.url + '/send', { + global: false, + type: "POST", + dataType: "json", + data: { + data: data, + transport: transport, + clientId: connection.clientId + }, + success: function (result) { + if (result) { + $(connection).trigger("onReceived", [result]); + } + }, + error: function (errData, textStatus) { + if (textStatus === "abort") { + return; + } + $(connection).trigger("onError", [errData]); + } + }); + }, + + foreverFrame: { + count: 0, + connections: {} + } + }; + signalR.transports = { webSockets: { @@ -248,7 +279,7 @@ if (!opened) { if (onFailed) { onFailed(); - } + } } else if (typeof event.wasClean != 'undefined' && event.wasClean === false) { // Ideally this would use the websocket.onerror handler (rather than checking wasClean in onclose) but // I found in some circumstances Chrome won't call onerror. This implementation seems to work on all browsers. @@ -280,6 +311,76 @@ } }, + foreverFrame: { + start: function (connection, onSuccess, onFailed) { + var frameId = (transportLogic.foreverFrame.count += 1), + url = connection.url + "/connect", + frame = $(""); + + $(connection).trigger("onSending"); + + // Build the url + if (connection.data) { + url += "?connectionData=" + connection.data + "&transport=foreverFrame&clientId=" + window.escape(connection.clientId); + } else { + url += "?transport=foreverFrame&clientId=" + window.escape(connection.clientId); + } + + url += "&frameId=" + frameId; + + frame.prop("src", url); + transportLogic.foreverFrame.connections[frameId] = connection; + + frame.bind("load", function () { + // TODO: The frame has finished loading (timeout or error), we need to refresh it + //var src = this.src.replace("/connect", "") + "&messageId=" + connection.messageId; + //this.src = src; + console.log("SignalR: forever frame iframe load event fired"); + }) + + $("body").append(frame); + connection.frame = frame; + connection.frameId = frameId; + if (onSuccess) { + onSuccess(); + } + }, + + send: function (connection, data) { + transportLogic.ajaxSend(connection, data, "foreverFrame"); + }, + + receive: function (connection, data) { + if (data) { + if (data.Messages) { + $.each(data.Messages, function () { + try { + $(connection).trigger("onReceived", [this]); + } + catch (e) { + if (console && console.log) { + console.log('Error raising received ' + e); + } + } + }); + } + connection.messageId = data.MessageId; + connection.groups = data.TransportData.Groups; + } + }, + + stop: function (connection) { + if (connection.frame) { + connection.frame.remove(); + delete transportLogic.foreverFrame.connections[connection.frameId]; + } + }, + + getConnection: function (id) { + return transportLogic.foreverFrame.connections[id]; + } + }, + longPolling: { start: function (connection, onSuccess, onFailed) { /// Starts the long polling connection @@ -300,7 +401,9 @@ instance.pollXhr = $.ajax(url, { global: false, + type: "POST", + data: { clientId: instance.clientId, messageId: messageId, @@ -308,7 +411,9 @@ transport: "longPolling", groups: (instance.groups || []).toString() }, + dataType: "json", + success: function (data) { var delay = 0; if (data) { @@ -338,6 +443,7 @@ poll(instance); } }, + error: function (data, textStatus) { if (textStatus === "abort") { return; @@ -362,31 +468,7 @@ }, send: function (connection, data) { - /// Sends data over this connection - /// The SignalR connection to send data over - /// The data to send - /// A callback to be invoked when the send has completed - $.ajax(connection.url + '/send', { - global: false, - type: "POST", - dataType: "json", - data: { - data: data, - transport: "longPolling", - clientId: connection.clientId - }, - success: function (result) { - if (result) { - $(connection).trigger("onReceived", [result]); - } - }, - error: function (data, textStatus) { - if (textStatus === "abort") { - return; - } - $(connection).trigger("onError", [data]); - } - }); + transportLogic.ajaxSend(connection, data, "longPolling"); }, stop: function (connection) { diff --git a/SignalR/Scripts/jquery.signalR.min.js b/SignalR/Scripts/jquery.signalR.min.js index e42482850..da018aa14 100644 --- a/SignalR/Scripts/jquery.signalR.min.js +++ b/SignalR/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";var i,r;i=function(n){return new i.fn.init(n)},i.fn=i.prototype={init:function(n){this.url=n},start:function(r,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(r)==="function"?u=r:n.type(r)==="object"&&(n.extend(e,r),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,r){r=r||0;if(r>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[r],e=n.type(u)==="object"?u:i.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,r+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.clientId=t.ClientId,n(f).trigger("onStarting");var u=[],r=[];n.each(i.transports,function(n){r.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,r)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,r)<0?u=r:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this;return n(i).bind("onStarting",function(){t.call(i)}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),n}},i.fn.init.prototype=i.fn,i.transports={webSockets:{send:function(n,t){n.socket.send(t)},start:function(i,r,u){var f,o=!1,e;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){u();return}i.socket||(f=document.location.host+i.appRelativeUrl,n(i).trigger("onSending"),f+=i.data?"?connectionData="+i.data+"&transport=webSockets&clientId="+i.clientId:"?transport=webSockets&clientId="+i.clientId,e=document.location.protocol==="https:"?"wss://":"ws://",i.socket=new t.WebSocket(e+f),i.socket.onopen=function(){o=!0,r&&r()},i.socket.onclose=function(t){o?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):u&&u(),i.socket=null},i.socket.onmessage=function(r){var u=t.JSON.parse(r.data);u&&(u.Messages?n.each(u.Messages,function(){n(i).trigger("onReceived",[this])}):n(i).trigger("onReceived",[u]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},longPolling:{start:function(i,r){i.pollXhr&&i.stop(),i.messageId=null,t.setTimeout(function(){(function u(i){n(i).trigger("onSending");var r=i.messageId,e=r===null,f=i.url+(e?"/connect":"");i.pollXhr=n.ajax(f,{global:!1,type:"POST",data:{clientId:i.clientId,messageId:r,connectionData:i.data,transport:"longPolling",groups:(i.groups||[]).toString()},dataType:"json",success:function(r){var f=0;r&&(r.Messages&&n.each(r.Messages,function(){try{n(i).trigger("onReceived",[this])}catch(t){console&&console.log&&console.log("Error raising received "+t)}}),i.messageId=r.MessageId,n.type(r.TransportData.LongPollDelay)==="number"&&(f=r.TransportData.LongPollDelay),i.groups=r.TransportData.Groups),f>0?t.setTimeout(function(){u(i)},f):u(i)},error:function(r,f){if(f==="abort")return;n(i).trigger("onError",[r]),t.setTimeout(function(){u(i)},2e3)}})})(i),setTimeout(r,150)},250)},send:function(t,i){n.ajax(t.url+"/send",{global:!1,type:"POST",dataType:"json",data:{data:i,transport:"longPolling",clientId:t.clientId},success:function(i){i&&n(t).trigger("onReceived",[i])},error:function(i,r){if(r==="abort")return;n(t).trigger("onError",[i])}})},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},i.noConflict=function(){return n.connection===i&&(n.connection=r),i},n.connection&&(r=n.connection),n.connection=n.signalR=i})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var i,u,r;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";i=function(n){return new i.fn.init(n)},i.fn=i.prototype={init:function(n){this.url=n},start:function(r,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(r)==="function"?u=r:n.type(r)==="object"&&(n.extend(e,r),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,r){r=r||0;if(r>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[r],e=n.type(u)==="object"?u:i.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,r+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.clientId=t.ClientId,n(f).trigger("onStarting");var u=[],r=[];n.each(i.transports,function(n){r.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,r)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,r)<0?u=r:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this;return n(i).bind("onStarting",function(){t.call(i)}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),n}},i.fn.init.prototype=i.fn,r={ajaxSend:function(t,i,r){n.ajax(t.url+"/send",{global:!1,type:"POST",dataType:"json",data:{data:i,transport:r,clientId:t.clientId},success:function(i){i&&n(t).trigger("onReceived",[i])},error:function(i,r){if(r==="abort")return;n(t).trigger("onError",[i])}})},foreverFrame:{count:0,connections:{}}},i.transports={webSockets:{send:function(n,t){n.socket.send(t)},start:function(i,r,u){var f,o=!1,e;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){u();return}i.socket||(f=document.location.host+i.appRelativeUrl,n(i).trigger("onSending"),f+=i.data?"?connectionData="+i.data+"&transport=webSockets&clientId="+i.clientId:"?transport=webSockets&clientId="+i.clientId,e=document.location.protocol==="https:"?"wss://":"ws://",i.socket=new t.WebSocket(e+f),i.socket.onopen=function(){o=!0,r&&r()},i.socket.onclose=function(t){o?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):u&&u(),i.socket=null},i.socket.onmessage=function(r){var u=t.JSON.parse(r.data);u&&(u.Messages?n.each(u.Messages,function(){n(i).trigger("onReceived",[this])}):n(i).trigger("onReceived",[u]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},foreverFrame:{start:function(i,u){var s=r.foreverFrame.count+=1,o=i.url+"/connect",e=n("");n(i).trigger("onSending"),o+=i.data?"?connectionData="+i.data+"&transport=foreverFrame&clientId="+t.escape(i.clientId):"?transport=foreverFrame&clientId="+t.escape(i.clientId),o+="&frameId="+s,e.prop("src",o),r.foreverFrame.connections[s]=i,e.bind("load",function(){console.log("SignalR: forever frame iframe load event fired")}),n("body").append(e),i.frame=e,i.frameId=s,u&&u()},send:function(n,t){r.ajaxSend(n,t,"foreverFrame")},receive:function(t,i){i&&(i.Messages&&n.each(i.Messages,function(){try{n(t).trigger("onReceived",[this])}catch(i){console&&console.log&&console.log("Error raising received "+i)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups)},stop:function(n){n.frame&&(n.frame.remove(),delete r.foreverFrame.connections[n.frameId])},getConnection:function(n){return r.foreverFrame.connections[n]}},longPolling:{start:function(i,r){i.pollXhr&&i.stop(),i.messageId=null,t.setTimeout(function(){(function u(i){n(i).trigger("onSending");var r=i.messageId,e=r===null,f=i.url+(e?"/connect":"");i.pollXhr=n.ajax(f,{global:!1,type:"POST",data:{clientId:i.clientId,messageId:r,connectionData:i.data,transport:"longPolling",groups:(i.groups||[]).toString()},dataType:"json",success:function(r){var f=0;r&&(r.Messages&&n.each(r.Messages,function(){try{n(i).trigger("onReceived",[this])}catch(t){console&&console.log&&console.log("Error raising received "+t)}}),i.messageId=r.MessageId,n.type(r.TransportData.LongPollDelay)==="number"&&(f=r.TransportData.LongPollDelay),i.groups=r.TransportData.Groups),f>0?t.setTimeout(function(){u(i)},f):u(i)},error:function(r,f){if(f==="abort")return;n(i).trigger("onError",[r]),t.setTimeout(function(){u(i)},2e3)}})})(i),setTimeout(r,150)},250)},send:function(n,t){r.ajaxSend(n,t,"longPolling")},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},i.noConflict=function(){return n.connection===i&&(n.connection=u),i},n.connection&&(u=n.connection),n.connection=n.signalR=i})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR/SignalR.csproj b/SignalR/SignalR.csproj index 58cb43b68..c2e02b797 100644 --- a/SignalR/SignalR.csproj +++ b/SignalR/SignalR.csproj @@ -95,6 +95,7 @@ + diff --git a/SignalR/Transports/ForeverFrameTransport.cs b/SignalR/Transports/ForeverFrameTransport.cs new file mode 100644 index 000000000..7096c2d37 --- /dev/null +++ b/SignalR/Transports/ForeverFrameTransport.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web; + +namespace SignalR.Transports +{ + public class ForeverFrameTransport : ForeverTransport + { + // TODO: Add support for echoing out data if no parent window found + private const string _initTemplate = "SignalR Forever Frame Transport Stream\r\n" + + "\r\n" + + "\r\n"; + private const string _sendTemplate = "\r\n"; + + public ForeverFrameTransport(HttpContextBase context, IJsonSerializer jsonSerializer) + : base(context, jsonSerializer) + { + + } + + protected string ClientID + { + get + { + return Context.Request.QueryString["clientID"]; + } + } + + protected override bool IsConnectRequest + { + get + { + return Context.Request.Path.EndsWith("/connect", StringComparison.OrdinalIgnoreCase); + } + } + + protected override void InitializeResponse(IConnection connection) + { + base.InitializeResponse(connection); + Context.Response.Write(String.Format(_initTemplate, Context.Request.QueryString["frameId"])); + } + + public override void Send(object value) + { + var payload = JsonSerializer.Stringify(value); + OnSending(payload); + + var script = String.Format(_sendTemplate, payload); + Context.Response.Write(script); + } + } +} \ No newline at end of file diff --git a/SignalR/Transports/ForeverTransport.cs b/SignalR/Transports/ForeverTransport.cs index 262d4382e..f553d2b8e 100644 --- a/SignalR/Transports/ForeverTransport.cs +++ b/SignalR/Transports/ForeverTransport.cs @@ -15,6 +15,24 @@ public ForeverTransport(HttpContextBase context, IJsonSerializer jsonSerializer) _jsonSerializer = jsonSerializer; } + protected IJsonSerializer JsonSerializer + { + get { return _jsonSerializer; } + } + + protected HttpContextBase Context + { + get { return _context; } + } + + protected virtual void OnSending(string payload) + { + if (Sending != null) + { + Sending(payload); + } + } + // Static events intended for use when measuring performance public static event Action Sending; public static event Action Receiving; @@ -46,21 +64,32 @@ public Func ProcessRequest(IConnection connection) } else { - if (Connected != null) + if (IsConnectRequest && Connected != null) { Connected(); } - // Don't timeout and never buffer any output - connection.ReceiveTimeout = TimeSpan.FromTicks(Int32.MaxValue - 1); - _context.Response.BufferOutput = false; - _context.Response.Buffer = false; + InitializeResponse(connection); + return () => ProcessMessages(connection); } return null; } + protected virtual bool IsConnectRequest + { + get { return true; } + } + + protected virtual void InitializeResponse(IConnection connection) + { + // Don't timeout and never buffer any output + connection.ReceiveTimeout = TimeSpan.FromTicks(Int32.MaxValue - 1); + Context.Response.BufferOutput = false; + Context.Response.Buffer = false; + } + private Task ProcessMessages(IConnection connection) { if (_context.Response.IsClientConnected) @@ -79,13 +108,15 @@ private Task ProcessMessages(IConnection connection) return TaskAsyncHelper.Empty; } - public void Send(object value) + public virtual void Send(PersistentResponse response) + { + Send((object)response); + } + + public virtual void Send(object value) { var payload = _jsonSerializer.Stringify(value); - if (Sending != null) - { - Sending(payload); - } + OnSending(payload); _context.Response.Write(payload); } } From a5a1bfe25a5c82b5364bdb953ecd7a32896e0a2a Mon Sep 17 00:00:00 2001 From: DamianEdwards Date: Tue, 29 Nov 2011 15:19:17 -0800 Subject: [PATCH 0235/1128] Fixed IIS dynamic compression module buffering issue for forever transports and derivations like ForeverFrame. --- SignalR.Samples/Scripts/jquery.signalR.js | 159 +++++++++--------- SignalR.Samples/Scripts/jquery.signalR.min.js | 2 +- SignalR.Samples/Web.config | 2 +- SignalR/PersistentConnection.cs | 2 +- SignalR/Scripts/jquery.signalR.js | 157 +++++++++-------- SignalR/Scripts/jquery.signalR.min.js | 2 +- SignalR/Transports/ForeverFrameTransport.cs | 30 +++- SignalR/Transports/ForeverTransport.cs | 36 ++-- 8 files changed, 213 insertions(+), 177 deletions(-) diff --git a/SignalR.Samples/Scripts/jquery.signalR.js b/SignalR.Samples/Scripts/jquery.signalR.js index 97ec1ca10..0b4f32dfe 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.js +++ b/SignalR.Samples/Scripts/jquery.signalR.js @@ -13,7 +13,10 @@ throw "SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8."; } - var signalR, _connection; + var signalR, _connection, + log = (typeof (console) !== "undefined" && console && console.log) + ? console.log + : $.noop; signalR = function (url) { /// Creates a new SignalR connection for the given url @@ -311,78 +314,6 @@ } }, - foreverFrame: { - start: function (connection, onSuccess, onFailed) { - var frameId = (transportLogic.foreverFrame.count += 1), - url = connection.url + "/connect", - frame = $(""); - - $(connection).trigger("onSending"); - - // Build the url - if (connection.data) { - url += "?connectionData=" + connection.data + "&transport=foreverFrame&clientId=" + window.escape(connection.clientId); - } else { - url += "?transport=foreverFrame&clientId=" + window.escape(connection.clientId); - } - - url += "&frameId=" + frameId; - - frame.prop("src", url); - //frame.data("signalr-connection-id", id); - //frame.data("signalr-connection", connection); - transportLogic.foreverFrame.connections[frameId] = connection; - - frame.bind("load", function () { - // The frame has finished loading (timeout or error), we need to refresh it - // var src = this.src.replace("/connect", "") + "&messageId=" + connection.messageId; - // this.src = src; - console.log("SignalR: forever frame iframe load event fired"); - }) - - $("body").append(frame); - connection.frame = frame; - connection.frameId = frameId; - if (onSuccess) { - onSuccess(); - } - }, - - send: function (connection, data) { - transportLogic.ajaxSend(connection, data, "foreverFrame"); - }, - - receive: function (connection, data) { - if (data) { - if (data.Messages) { - $.each(data.Messages, function () { - try { - $(connection).trigger("onReceived", [this]); - } - catch (e) { - if (console && console.log) { - console.log('Error raising received ' + e); - } - } - }); - } - connection.messageId = data.MessageId; - connection.groups = data.TransportData.Groups; - } - }, - - stop: function (connection) { - if (connection.frame) { - connection.frame.remove(); - delete transportLogic.foreverFrame.connections[connection.frameId]; - } - }, - - getConnection: function (id) { - return transportLogic.foreverFrame.connections[id]; - } - }, - longPolling: { start: function (connection, onSuccess, onFailed) { /// Starts the long polling connection @@ -425,9 +356,7 @@ $(instance).trigger("onReceived", [this]); } catch (e) { - if (console && console.log) { - console.log('Error raising received ' + e); - } + log('Error raising received ' + e); } }); } @@ -481,6 +410,84 @@ connection.pollXhr = null; } } + }, + + foreverFrame: { + start: function (connection, onSuccess, onFailed) { + var frameId = (transportLogic.foreverFrame.count += 1), + url = connection.url + "/connect", + frame = $(""); + + $(connection).trigger("onSending"); + + // Build the url + if (connection.data) { + url += "?connectionData=" + connection.data + "&transport=foreverFrame&clientId=" + window.escape(connection.clientId); + } else { + url += "?transport=foreverFrame&clientId=" + window.escape(connection.clientId); + } + + url += "&frameId=" + frameId; + + frame.prop("src", url); + transportLogic.foreverFrame.connections[frameId] = connection; + + frame.bind("load", function () { + // TODO: The frame has finished loading (timeout or error), we need to refresh it + // TODO: Detect if an error was written to the iframe html and delay + //var src = this.src.replace("/connect", "") + "&messageId=" + connection.messageId; + //this.src = src; + log("SignalR: forever frame iframe load event fired"); + }) + + connection.frame = frame; + connection.frameId = frameId; + + if (onSuccess) { + connection.onSuccess = onSuccess; + } + + $("body").append(frame); + }, + + send: function (connection, data) { + transportLogic.ajaxSend(connection, data, "foreverFrame"); + }, + + receive: function (connection, data) { + // TODO: Factor this out and reuse for all transports + if (data) { + if (data.Messages) { + $.each(data.Messages, function () { + try { + $(connection).trigger("onReceived", [this]); + } + catch (e) { + log('Error raising received ' + e); + } + }); + } + connection.messageId = data.MessageId; + connection.groups = data.TransportData.Groups; + } + }, + + stop: function (connection) { + if (connection.frame) { + connection.frame.remove(); + delete transportLogic.foreverFrame.connections[connection.frameId]; + } + }, + + getConnection: function (id) { + return transportLogic.foreverFrame.connections[id]; + }, + + started: function (connection) { + if (connection.onSuccess) { + connection.onSuccess(); + } + } } }; diff --git a/SignalR.Samples/Scripts/jquery.signalR.min.js b/SignalR.Samples/Scripts/jquery.signalR.min.js index da018aa14..a78d962da 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.min.js +++ b/SignalR.Samples/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var i,u,r;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";i=function(n){return new i.fn.init(n)},i.fn=i.prototype={init:function(n){this.url=n},start:function(r,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(r)==="function"?u=r:n.type(r)==="object"&&(n.extend(e,r),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,r){r=r||0;if(r>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[r],e=n.type(u)==="object"?u:i.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,r+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.clientId=t.ClientId,n(f).trigger("onStarting");var u=[],r=[];n.each(i.transports,function(n){r.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,r)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,r)<0?u=r:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this;return n(i).bind("onStarting",function(){t.call(i)}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),n}},i.fn.init.prototype=i.fn,r={ajaxSend:function(t,i,r){n.ajax(t.url+"/send",{global:!1,type:"POST",dataType:"json",data:{data:i,transport:r,clientId:t.clientId},success:function(i){i&&n(t).trigger("onReceived",[i])},error:function(i,r){if(r==="abort")return;n(t).trigger("onError",[i])}})},foreverFrame:{count:0,connections:{}}},i.transports={webSockets:{send:function(n,t){n.socket.send(t)},start:function(i,r,u){var f,o=!1,e;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){u();return}i.socket||(f=document.location.host+i.appRelativeUrl,n(i).trigger("onSending"),f+=i.data?"?connectionData="+i.data+"&transport=webSockets&clientId="+i.clientId:"?transport=webSockets&clientId="+i.clientId,e=document.location.protocol==="https:"?"wss://":"ws://",i.socket=new t.WebSocket(e+f),i.socket.onopen=function(){o=!0,r&&r()},i.socket.onclose=function(t){o?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):u&&u(),i.socket=null},i.socket.onmessage=function(r){var u=t.JSON.parse(r.data);u&&(u.Messages?n.each(u.Messages,function(){n(i).trigger("onReceived",[this])}):n(i).trigger("onReceived",[u]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},foreverFrame:{start:function(i,u){var s=r.foreverFrame.count+=1,o=i.url+"/connect",e=n("");n(i).trigger("onSending"),o+=i.data?"?connectionData="+i.data+"&transport=foreverFrame&clientId="+t.escape(i.clientId):"?transport=foreverFrame&clientId="+t.escape(i.clientId),o+="&frameId="+s,e.prop("src",o),r.foreverFrame.connections[s]=i,e.bind("load",function(){console.log("SignalR: forever frame iframe load event fired")}),n("body").append(e),i.frame=e,i.frameId=s,u&&u()},send:function(n,t){r.ajaxSend(n,t,"foreverFrame")},receive:function(t,i){i&&(i.Messages&&n.each(i.Messages,function(){try{n(t).trigger("onReceived",[this])}catch(i){console&&console.log&&console.log("Error raising received "+i)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups)},stop:function(n){n.frame&&(n.frame.remove(),delete r.foreverFrame.connections[n.frameId])},getConnection:function(n){return r.foreverFrame.connections[n]}},longPolling:{start:function(i,r){i.pollXhr&&i.stop(),i.messageId=null,t.setTimeout(function(){(function u(i){n(i).trigger("onSending");var r=i.messageId,e=r===null,f=i.url+(e?"/connect":"");i.pollXhr=n.ajax(f,{global:!1,type:"POST",data:{clientId:i.clientId,messageId:r,connectionData:i.data,transport:"longPolling",groups:(i.groups||[]).toString()},dataType:"json",success:function(r){var f=0;r&&(r.Messages&&n.each(r.Messages,function(){try{n(i).trigger("onReceived",[this])}catch(t){console&&console.log&&console.log("Error raising received "+t)}}),i.messageId=r.MessageId,n.type(r.TransportData.LongPollDelay)==="number"&&(f=r.TransportData.LongPollDelay),i.groups=r.TransportData.Groups),f>0?t.setTimeout(function(){u(i)},f):u(i)},error:function(r,f){if(f==="abort")return;n(i).trigger("onError",[r]),t.setTimeout(function(){u(i)},2e3)}})})(i),setTimeout(r,150)},250)},send:function(n,t){r.ajaxSend(n,t,"longPolling")},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},i.noConflict=function(){return n.connection===i&&(n.connection=u),i},n.connection&&(u=n.connection),n.connection=n.signalR=i})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var i,f,u,r;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.log?console.log:n.noop,i=function(n){return new i.fn.init(n)},i.fn=i.prototype={init:function(n){this.url=n},start:function(r,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(r)==="function"?u=r:n.type(r)==="object"&&(n.extend(e,r),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,r){r=r||0;if(r>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[r],e=n.type(u)==="object"?u:i.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,r+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.clientId=t.ClientId,n(f).trigger("onStarting");var u=[],r=[];n.each(i.transports,function(n){r.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,r)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,r)<0?u=r:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this;return n(i).bind("onStarting",function(){t.call(i)}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),n}},i.fn.init.prototype=i.fn,r={ajaxSend:function(t,i,r){n.ajax(t.url+"/send",{global:!1,type:"POST",dataType:"json",data:{data:i,transport:r,clientId:t.clientId},success:function(i){i&&n(t).trigger("onReceived",[i])},error:function(i,r){if(r==="abort")return;n(t).trigger("onError",[i])}})},foreverFrame:{count:0,connections:{}}},i.transports={webSockets:{send:function(n,t){n.socket.send(t)},start:function(i,r,u){var f,o=!1,e;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){u();return}i.socket||(f=document.location.host+i.appRelativeUrl,n(i).trigger("onSending"),f+=i.data?"?connectionData="+i.data+"&transport=webSockets&clientId="+i.clientId:"?transport=webSockets&clientId="+i.clientId,e=document.location.protocol==="https:"?"wss://":"ws://",i.socket=new t.WebSocket(e+f),i.socket.onopen=function(){o=!0,r&&r()},i.socket.onclose=function(t){o?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):u&&u(),i.socket=null},i.socket.onmessage=function(r){var u=t.JSON.parse(r.data);u&&(u.Messages?n.each(u.Messages,function(){n(i).trigger("onReceived",[this])}):n(i).trigger("onReceived",[u]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},longPolling:{start:function(i,r){i.pollXhr&&i.stop(),i.messageId=null,t.setTimeout(function(){(function f(i){n(i).trigger("onSending");var r=i.messageId,o=r===null,e=i.url+(o?"/connect":"");i.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{clientId:i.clientId,messageId:r,connectionData:i.data,transport:"longPolling",groups:(i.groups||[]).toString()},dataType:"json",success:function(r){var e=0;r&&(r.Messages&&n.each(r.Messages,function(){try{n(i).trigger("onReceived",[this])}catch(t){u("Error raising received "+t)}}),i.messageId=r.MessageId,n.type(r.TransportData.LongPollDelay)==="number"&&(e=r.TransportData.LongPollDelay),i.groups=r.TransportData.Groups),e>0?t.setTimeout(function(){f(i)},e):f(i)},error:function(r,u){if(u==="abort")return;n(i).trigger("onError",[r]),t.setTimeout(function(){f(i)},2e3)}})})(i),setTimeout(r,150)},250)},send:function(n,t){r.ajaxSend(n,t,"longPolling")},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}},foreverFrame:{start:function(i,f){var h=r.foreverFrame.count+=1,s=i.url+"/connect",o=n("");n(i).trigger("onSending"),s+=i.data?"?connectionData="+i.data+"&transport=foreverFrame&clientId="+t.escape(i.clientId):"?transport=foreverFrame&clientId="+t.escape(i.clientId),s+="&frameId="+h,o.prop("src",s),r.foreverFrame.connections[h]=i,o.bind("load",function(){u("SignalR: forever frame iframe load event fired")}),i.frame=o,i.frameId=h,f&&(i.onSuccess=f),n("body").append(o)},send:function(n,t){r.ajaxSend(n,t,"foreverFrame")},receive:function(t,i){i&&(i.Messages&&n.each(i.Messages,function(){try{n(t).trigger("onReceived",[this])}catch(i){u("Error raising received "+i)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups)},stop:function(n){n.frame&&(n.frame.remove(),delete r.foreverFrame.connections[n.frameId])},getConnection:function(n){return r.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}}},i.noConflict=function(){return n.connection===i&&(n.connection=f),i},n.connection&&(f=n.connection),n.connection=n.signalR=i})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR.Samples/Web.config b/SignalR.Samples/Web.config index 956eeb6ca..c2786c93e 100644 --- a/SignalR.Samples/Web.config +++ b/SignalR.Samples/Web.config @@ -1,4 +1,4 @@ - + + \ No newline at end of file diff --git a/SignalR.AspNet/packages.config b/SignalR.AspNet/packages.config new file mode 100644 index 000000000..f143a04fb --- /dev/null +++ b/SignalR.AspNet/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SignalR.Samples/App_Start/Startup.cs b/SignalR.Samples/App_Start/Startup.cs index 4b1d2a286..a971c5243 100644 --- a/SignalR.Samples/App_Start/Startup.cs +++ b/SignalR.Samples/App_Start/Startup.cs @@ -2,8 +2,8 @@ using System.Diagnostics; using System.Threading; using System.Web.Routing; +using SignalR.AspNet.Routing; using SignalR.Hubs; -using SignalR.Routing; using SignalR.Samples.App_Start; using SignalR.Samples.Hubs.DemoHub; @@ -15,7 +15,6 @@ public class Startup { public static void Start() { - // Uncomment this for web farm support //var cs = ConfigurationManager.ConnectionStrings["SignalR"].ConnectionString; //var store = new PeerToPeerSQLSignalBusMessageStore(cs); diff --git a/SignalR.Samples/SignalR.Samples.csproj b/SignalR.Samples/SignalR.Samples.csproj index 4a0867238..e39d0b20d 100644 --- a/SignalR.Samples/SignalR.Samples.csproj +++ b/SignalR.Samples/SignalR.Samples.csproj @@ -209,6 +209,10 @@ + + {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2} + SignalR.AspNet + {32D16B36-970E-4CF2-B954-78CB1833CBC1} SignalR.ScaleOut diff --git a/SignalR.sln b/SignalR.sln index 2693d0fd2..eb5ce7e0c 100644 --- a/SignalR.sln +++ b/SignalR.sln @@ -17,6 +17,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{1E4BA77C EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.Tests", "SignalR.Tests\SignalR.Tests.csproj", "{FBA09237-84CC-4383-BD12-CDF58E4020E8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.AspNet", "SignalR.AspNet\SignalR.AspNet.csproj", "{0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -43,6 +45,10 @@ Global {FBA09237-84CC-4383-BD12-CDF58E4020E8}.Debug|Any CPU.Build.0 = Debug|Any CPU {FBA09237-84CC-4383-BD12-CDF58E4020E8}.Release|Any CPU.ActiveCfg = Release|Any CPU {FBA09237-84CC-4383-BD12-CDF58E4020E8}.Release|Any CPU.Build.0 = Release|Any CPU + {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SignalR/AspNet/PreApplicationStart.cs b/SignalR/AspNet/PreApplicationStart.cs deleted file mode 100644 index e2d3f8fb3..000000000 --- a/SignalR/AspNet/PreApplicationStart.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Web; -using Microsoft.Web.Infrastructure.DynamicModuleHelper; -using SignalR.AspNet; -using SignalR.Transports; - -[assembly: PreApplicationStartMethod(typeof(PreApplicationStart), "Start")] - -namespace SignalR.AspNet -{ - public static class PreApplicationStart - { - public static void Start() - { - DynamicModuleUtility.RegisterModule(typeof(HubModule)); - - TransportManager.InitializeDefaultTransports(); - } - } -} \ No newline at end of file diff --git a/SignalR/DefaultPersistentConnectionFactory.cs b/SignalR/DefaultPersistentConnectionFactory.cs index 8bc34b3ca..0b8e39d69 100644 --- a/SignalR/DefaultPersistentConnectionFactory.cs +++ b/SignalR/DefaultPersistentConnectionFactory.cs @@ -1,12 +1,11 @@ using System; -using System.Web.Routing; using SignalR.Infrastructure; namespace SignalR { public class DefaultPersistentConnectionFactory : IPersistentConnectionFactory { - public PersistentConnection CreateInstance(RequestContext requestContext, Type connectionType) + public PersistentConnection CreateInstance(Type connectionType) { if (connectionType == null) { diff --git a/SignalR/Hubs/DefaultHubLocator.cs b/SignalR/Hubs/DefaultHubLocator.cs index 720e5daf4..dea77eab3 100644 --- a/SignalR/Hubs/DefaultHubLocator.cs +++ b/SignalR/Hubs/DefaultHubLocator.cs @@ -17,7 +17,8 @@ public IEnumerable GetHubs() public static IEnumerable GetAllHubs() { - return (from Assembly a in BuildManager.GetReferencedAssemblies() + // This only happens once and is cached for the lifetime of the appdomain + return (from Assembly a in AppDomain.CurrentDomain.GetAssemblies() where !a.GlobalAssemblyCache && !a.IsDynamic from type in GetTypesSafe(a) where typeof(IHub).IsAssignableFrom(type) && !type.IsAbstract diff --git a/SignalR/Hubs/DefaultHubTypeResolver.cs b/SignalR/Hubs/DefaultHubTypeResolver.cs index e0febb706..e826b5d6f 100644 --- a/SignalR/Hubs/DefaultHubTypeResolver.cs +++ b/SignalR/Hubs/DefaultHubTypeResolver.cs @@ -66,7 +66,7 @@ private void AddCacheKey(string key, Type hubType) } } - public Type ResolveType(string hubName) + public virtual Type ResolveType(string hubName) { Type type; if (_hubCache.TryGetValue(hubName, out type)) @@ -75,7 +75,7 @@ public Type ResolveType(string hubName) } // Fallback to the build manager - return BuildManager.GetType(hubName, throwOnError: true); + return null; } } } diff --git a/SignalR/IPersistentConnectionFactory.cs b/SignalR/IPersistentConnectionFactory.cs index 675da4d66..02083017e 100644 --- a/SignalR/IPersistentConnectionFactory.cs +++ b/SignalR/IPersistentConnectionFactory.cs @@ -1,10 +1,9 @@ using System; -using System.Web.Routing; namespace SignalR { public interface IPersistentConnectionFactory { - PersistentConnection CreateInstance(RequestContext requestContext, Type handlerType); + PersistentConnection CreateInstance(Type handlerType); } } diff --git a/SignalR/Infrastructure/DependencyResolver.cs b/SignalR/Infrastructure/DependencyResolver.cs index 78eb81c72..75870f720 100644 --- a/SignalR/Infrastructure/DependencyResolver.cs +++ b/SignalR/Infrastructure/DependencyResolver.cs @@ -11,7 +11,7 @@ public static class DependencyResolver private static readonly IDependencyResolver _defaultResolver = new DefaultDependencyResolver(); private static IDependencyResolver _resolver; - internal static IDependencyResolver Current + public static IDependencyResolver Current { get { return _resolver ?? _defaultResolver; } } diff --git a/SignalR/Properties/AssemblyInfo.cs b/SignalR/Properties/AssemblyInfo.cs index 4d915d5c1..725ded0b0 100644 --- a/SignalR/Properties/AssemblyInfo.cs +++ b/SignalR/Properties/AssemblyInfo.cs @@ -2,7 +2,7 @@ using System.Runtime.CompilerServices; [assembly: AssemblyTitle("SignalR.Server")] -[assembly: AssemblyDescription("An ASP.NET library for authoring SignalR services.")] +[assembly: AssemblyDescription("Async signaling library for .NET to help build real-time, multi-user interactive web applications.")] [assembly: InternalsVisibleTo("SignalR.ScaleOut")] [assembly: InternalsVisibleTo("SignalR.Tests")] [assembly: AssemblyVersion("0.3.6.0")] \ No newline at end of file diff --git a/SignalR/SignalR.csproj b/SignalR/SignalR.csproj index 6fef0261f..fba8dfb73 100644 --- a/SignalR/SignalR.csproj +++ b/SignalR/SignalR.csproj @@ -33,17 +33,12 @@ 4 - - True - ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - ..\packages\Newtonsoft.Json.4.0.4\lib\net40\Newtonsoft.Json.dll - @@ -59,8 +54,6 @@ - - @@ -88,7 +81,6 @@ - @@ -102,12 +94,7 @@ - - - - - @@ -131,10 +118,8 @@ - - @@ -149,6 +134,7 @@ + diff --git a/SignalR/packages.config b/SignalR/packages.config index dec59445d..613a4738d 100644 --- a/SignalR/packages.config +++ b/SignalR/packages.config @@ -1,5 +1,4 @@  - \ No newline at end of file From 2e891e020711d58cdf1fc88131cce1f35542c07f Mon Sep 17 00:00:00 2001 From: David Fowler Date: Wed, 28 Dec 2011 21:16:21 -0800 Subject: [PATCH 0296/1128] Removed SupportsWebSockets from the interface and used Items instead. - Removed LocalPath from IRequest. --- SignalR.AspNet/AspNetHost.cs | 14 ++++++++++++-- SignalR.AspNet/AspNetRequest.cs | 19 ------------------- SignalR.AspNet/HttpTaskAsyncHandler.cs | 2 +- SignalR/Abstractions/HostContextExtensions.cs | 5 +++++ SignalR/Abstractions/IRequest.cs | 3 --- SignalR/PersistentConnection.cs | 4 ++-- 6 files changed, 20 insertions(+), 27 deletions(-) diff --git a/SignalR.AspNet/AspNetHost.cs b/SignalR.AspNet/AspNetHost.cs index 816fbe4a3..df988f07a 100644 --- a/SignalR.AspNet/AspNetHost.cs +++ b/SignalR.AspNet/AspNetHost.cs @@ -1,7 +1,8 @@ -using System.Threading.Tasks; +using System; +using System.Linq; +using System.Threading.Tasks; using System.Web; using SignalR.Abstractions; -using SignalR.Web; namespace SignalR.AspNet { @@ -9,6 +10,12 @@ public class AspNetHost : HttpTaskAsyncHandler { private readonly PersistentConnection _connection; + private static readonly Lazy _hasAcceptWebSocketRequest = + new Lazy(() => + { + return typeof(HttpContextBase).GetMethods().Any(m => m.Name.Equals("AcceptWebSocketRequest", StringComparison.OrdinalIgnoreCase)); + }); + public AspNetHost(PersistentConnection connection) { _connection = connection; @@ -20,6 +27,9 @@ public override Task ProcessRequestAsync(HttpContextBase context) var response = new AspNetResponse(context.Request, context.Response); var hostContext = new HostContext(request, response, context.User); + // Determine if the client should bother to try a websocket request + hostContext.Items["supportsWebSockets"] = _hasAcceptWebSocketRequest.Value; + // Set the debugging flag hostContext.Items["debugMode"] = context.IsDebuggingEnabled; diff --git a/SignalR.AspNet/AspNetRequest.cs b/SignalR.AspNet/AspNetRequest.cs index 40a5a1f9d..d8f50694e 100644 --- a/SignalR.AspNet/AspNetRequest.cs +++ b/SignalR.AspNet/AspNetRequest.cs @@ -7,9 +7,6 @@ namespace SignalR.AspNet { public class AspNetRequest : IRequest { - private static readonly Lazy _hasAcceptWebSocketRequest = - new Lazy(() => typeof(HttpContextBase).GetMethod("AcceptWebSocketRequest") != null); - private readonly HttpRequestBase _request; public AspNetRequest(HttpRequestBase request) @@ -30,22 +27,6 @@ public string Path } } - public string LocalPath - { - get - { - return _request.AppRelativeCurrentExecutionFilePath; - } - } - - public bool SupportsWebSockets - { - get - { - return _hasAcceptWebSocketRequest.Value; - } - } - public NameValueCollection QueryString { get diff --git a/SignalR.AspNet/HttpTaskAsyncHandler.cs b/SignalR.AspNet/HttpTaskAsyncHandler.cs index 9619693f0..c9a68ef99 100644 --- a/SignalR.AspNet/HttpTaskAsyncHandler.cs +++ b/SignalR.AspNet/HttpTaskAsyncHandler.cs @@ -2,7 +2,7 @@ using System.Threading.Tasks; using System.Web; -namespace SignalR.Web +namespace SignalR.AspNet { public abstract class HttpTaskAsyncHandler : IHttpAsyncHandler { diff --git a/SignalR/Abstractions/HostContextExtensions.cs b/SignalR/Abstractions/HostContextExtensions.cs index d2ae526dd..00fa621b4 100644 --- a/SignalR/Abstractions/HostContextExtensions.cs +++ b/SignalR/Abstractions/HostContextExtensions.cs @@ -16,5 +16,10 @@ public static bool IsDebuggingEnabled(this HostContext context) { return context.GetValue("debugMode"); } + + public static bool SupportsWebSockets(this HostContext context) + { + return context.GetValue("supportsWebSockets"); + } } } diff --git a/SignalR/Abstractions/IRequest.cs b/SignalR/Abstractions/IRequest.cs index d1be16c08..20e6af4bc 100644 --- a/SignalR/Abstractions/IRequest.cs +++ b/SignalR/Abstractions/IRequest.cs @@ -5,9 +5,6 @@ namespace SignalR.Abstractions public interface IRequest { string Path { get; } - string LocalPath { get; } - bool SupportsWebSockets { get; } - NameValueCollection QueryString { get; } NameValueCollection Headers { get; } NameValueCollection Form { get; } diff --git a/SignalR/PersistentConnection.cs b/SignalR/PersistentConnection.cs index c4fd69c19..a87aa4616 100644 --- a/SignalR/PersistentConnection.cs +++ b/SignalR/PersistentConnection.cs @@ -206,9 +206,9 @@ private Task ProcessNegotiationRequest(HostContext context) context.Response.ContentType = Json.MimeType; return context.Response.WriteAsync(_jsonSerializer.Stringify(new { - Url = context.Request.LocalPath.Replace("/negotiate", ""), + Url = context.Request.Path.Replace("/negotiate", ""), ConnectionId = _connectionIdFactory.CreateConnectionId(context.Request), - TryWebSockets = context.Request.SupportsWebSockets + TryWebSockets = context.SupportsWebSockets() })); } From f94799db410eea7abd3549a91fb46da9f817776d Mon Sep 17 00:00:00 2001 From: David Fowler Date: Wed, 28 Dec 2011 21:33:49 -0800 Subject: [PATCH 0297/1128] Remove all methods from PersistentConnection that don't return Task. --- SignalR.Samples/Raw/Raw.cs | 4 +++- SignalR/Hubs/HubDispatcher.cs | 4 +++- SignalR/PersistentConnection.cs | 12 ------------ 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/SignalR.Samples/Raw/Raw.cs b/SignalR.Samples/Raw/Raw.cs index 156c5e519..5692e799f 100644 --- a/SignalR.Samples/Raw/Raw.cs +++ b/SignalR.Samples/Raw/Raw.cs @@ -32,7 +32,7 @@ protected override Task OnDisconnectAsync(string connectionId) return Connection.Broadcast(DateTime.Now + ": " + GetUser(connectionId) + " disconnected"); } - protected override void OnReceived(string connectionId, string data) + protected override Task OnReceivedAsync(string connectionId, string data) { var serializer = new JavaScriptSerializer(); var message = serializer.Deserialize(data); @@ -91,6 +91,8 @@ protected override void OnReceived(string connectionId, string data) default: break; } + + return base.OnReceivedAsync(connectionId, data); } private string GetUser(string connectionId) diff --git a/SignalR/Hubs/HubDispatcher.cs b/SignalR/Hubs/HubDispatcher.cs index cf547eae5..5eec34f3d 100644 --- a/SignalR/Hubs/HubDispatcher.cs +++ b/SignalR/Hubs/HubDispatcher.cs @@ -171,7 +171,7 @@ public override Task ProcessRequestAsync(HostContext context) return base.ProcessRequestAsync(context); } - protected override void OnDisconnect(string connectionId) + protected override Task OnDisconnectAsync(string connectionId) { // Loop over each hub and call disconnect (if the hub supports it) foreach (Type type in GetDisconnectTypes()) @@ -194,6 +194,8 @@ protected override void OnDisconnect(string connectionId) disconnect.Disconnect(); } } + + return TaskAsyncHelper.Empty; } private IEnumerable GetDisconnectTypes() diff --git a/SignalR/PersistentConnection.cs b/SignalR/PersistentConnection.cs index a87aa4616..752aa5036 100644 --- a/SignalR/PersistentConnection.cs +++ b/SignalR/PersistentConnection.cs @@ -128,38 +128,26 @@ protected virtual IConnection CreateConnection(string connectionId, IEnumerable< return new Connection(_store, _jsonSerializer, _signaler, DefaultSignal, connectionId, signals, groups); } - protected virtual void OnConnected(IRequest request, string connectionId) { } - protected virtual Task OnConnectedAsync(IRequest request, string connectionId) { OnClientConnected(connectionId); - OnConnected(request, connectionId); return TaskAsyncHelper.Empty; } - protected virtual void OnReceived(string connectionId, string data) { } - protected virtual Task OnReceivedAsync(string connectionId, string data) { OnReceiving(); - OnReceived(connectionId, data); return TaskAsyncHelper.Empty; } - protected virtual void OnDisconnect(string connectionId) { } - protected virtual Task OnDisconnectAsync(string connectionId) { OnClientDisconnected(connectionId); - OnDisconnect(connectionId); return TaskAsyncHelper.Empty; } - protected virtual void OnError(Exception e) { } - protected virtual Task OnErrorAsync(Exception e) { - OnError(e); return TaskAsyncHelper.Empty; } From a5e9e60c392d10b1fad0ebce87d33a8682777f83 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Thu, 29 Dec 2011 03:57:51 -0800 Subject: [PATCH 0298/1128] Made BuildManagerTypeLocator derive from DefaultHubLocator. --- SignalR.AspNet/BuildManagerTypeLocator.cs | 32 +++-------------------- SignalR/Hubs/DefaultHubLocator.cs | 19 ++++++++++---- 2 files changed, 18 insertions(+), 33 deletions(-) diff --git a/SignalR.AspNet/BuildManagerTypeLocator.cs b/SignalR.AspNet/BuildManagerTypeLocator.cs index f81abb9c7..fe3f40fc2 100644 --- a/SignalR.AspNet/BuildManagerTypeLocator.cs +++ b/SignalR.AspNet/BuildManagerTypeLocator.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Web.Compilation; @@ -7,34 +6,11 @@ namespace SignalR.AspNet { - public class BuildManagerTypeLocator : IHubLocator + public class BuildManagerTypeLocator : DefaultHubLocator { - private readonly Lazy> _hubs = new Lazy>(GetAllHubs); - - public IEnumerable GetHubs() - { - return _hubs.Value; - } - - public static IEnumerable GetAllHubs() - { - return (from Assembly a in BuildManager.GetReferencedAssemblies() - where !a.GlobalAssemblyCache && !a.IsDynamic - from type in GetTypesSafe(a) - where typeof(IHub).IsAssignableFrom(type) && !type.IsAbstract - select type).ToList(); - } - - private static IEnumerable GetTypesSafe(Assembly a) + protected override IEnumerable GetAssemblies() { - try - { - return a.GetTypes(); - } - catch - { - return Enumerable.Empty(); - } + return BuildManager.GetReferencedAssemblies().Cast(); } } } diff --git a/SignalR/Hubs/DefaultHubLocator.cs b/SignalR/Hubs/DefaultHubLocator.cs index dea77eab3..9e7fc6601 100644 --- a/SignalR/Hubs/DefaultHubLocator.cs +++ b/SignalR/Hubs/DefaultHubLocator.cs @@ -2,29 +2,38 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Web.Compilation; namespace SignalR.Hubs { public class DefaultHubLocator : IHubLocator { - private readonly Lazy> _hubs = new Lazy>(GetAllHubs); + private readonly Lazy> _hubs; + + public DefaultHubLocator() + { + _hubs = new Lazy>(GetAllHubs); + } public IEnumerable GetHubs() { return _hubs.Value; } - public static IEnumerable GetAllHubs() + public IEnumerable GetAllHubs() { - // This only happens once and is cached for the lifetime of the appdomain - return (from Assembly a in AppDomain.CurrentDomain.GetAssemblies() + return (from a in GetAssemblies() where !a.GlobalAssemblyCache && !a.IsDynamic from type in GetTypesSafe(a) where typeof(IHub).IsAssignableFrom(type) && !type.IsAbstract select type).ToList(); } + protected virtual IEnumerable GetAssemblies() + { + // TODO: Look for a better default, chances are the hubs aren't even loaded yet + return AppDomain.CurrentDomain.GetAssemblies(); + } + private static IEnumerable GetTypesSafe(Assembly a) { try From 4e397900e4fe7fc770949b8d31bb91ff0d86a2ce Mon Sep 17 00:00:00 2001 From: David Fowler Date: Thu, 29 Dec 2011 11:25:13 -0800 Subject: [PATCH 0299/1128] Change Path to Url on IRequest. --- SignalR.AspNet/AspNetRequest.cs | 4 ++-- SignalR/Abstractions/IRequest.cs | 3 ++- SignalR/Hubs/HubDispatcher.cs | 2 +- SignalR/PersistentConnection.cs | 4 ++-- SignalR/Transports/ForeverFrameTransport.cs | 2 +- SignalR/Transports/ForeverTransport.cs | 2 +- SignalR/Transports/LongPollingTransport.cs | 4 ++-- 7 files changed, 11 insertions(+), 10 deletions(-) diff --git a/SignalR.AspNet/AspNetRequest.cs b/SignalR.AspNet/AspNetRequest.cs index d8f50694e..9b9d709b7 100644 --- a/SignalR.AspNet/AspNetRequest.cs +++ b/SignalR.AspNet/AspNetRequest.cs @@ -19,11 +19,11 @@ public AspNetRequest(HttpRequestBase request) } } - public string Path + public Uri Url { get { - return _request.Path; + return _request.Url; } } diff --git a/SignalR/Abstractions/IRequest.cs b/SignalR/Abstractions/IRequest.cs index 20e6af4bc..52241a969 100644 --- a/SignalR/Abstractions/IRequest.cs +++ b/SignalR/Abstractions/IRequest.cs @@ -1,10 +1,11 @@ using System.Collections.Specialized; +using System; namespace SignalR.Abstractions { public interface IRequest { - string Path { get; } + Uri Url { get; } NameValueCollection QueryString { get; } NameValueCollection Headers { get; } NameValueCollection Form { get; } diff --git a/SignalR/Hubs/HubDispatcher.cs b/SignalR/Hubs/HubDispatcher.cs index 5eec34f3d..6c47a0ebd 100644 --- a/SignalR/Hubs/HubDispatcher.cs +++ b/SignalR/Hubs/HubDispatcher.cs @@ -159,7 +159,7 @@ protected override Task OnReceivedAsync(string connectionId, string data) public override Task ProcessRequestAsync(HostContext context) { // Generate the proxy - if (context.Request.Path.EndsWith("/hubs", StringComparison.OrdinalIgnoreCase)) + if (context.Request.Url.LocalPath.EndsWith("/hubs", StringComparison.OrdinalIgnoreCase)) { context.Response.ContentType = "application/x-javascript"; return context.Response.WriteAsync(_proxyGenerator.GenerateProxy(_url)); diff --git a/SignalR/PersistentConnection.cs b/SignalR/PersistentConnection.cs index 752aa5036..2febedff2 100644 --- a/SignalR/PersistentConnection.cs +++ b/SignalR/PersistentConnection.cs @@ -194,7 +194,7 @@ private Task ProcessNegotiationRequest(HostContext context) context.Response.ContentType = Json.MimeType; return context.Response.WriteAsync(_jsonSerializer.Stringify(new { - Url = context.Request.Path.Replace("/negotiate", ""), + Url = context.Request.Url.LocalPath.Replace("/negotiate", ""), ConnectionId = _connectionIdFactory.CreateConnectionId(context.Request), TryWebSockets = context.SupportsWebSockets() })); @@ -207,7 +207,7 @@ private string CreateQualifiedName(string groupName) private bool IsNegotiationRequest(IRequest request) { - return request.Path.EndsWith("/negotiate", StringComparison.OrdinalIgnoreCase); + return request.Url.LocalPath.EndsWith("/negotiate", StringComparison.OrdinalIgnoreCase); } private ITransport GetTransport(HostContext context) diff --git a/SignalR/Transports/ForeverFrameTransport.cs b/SignalR/Transports/ForeverFrameTransport.cs index a313f92ee..393c2a4c6 100644 --- a/SignalR/Transports/ForeverFrameTransport.cs +++ b/SignalR/Transports/ForeverFrameTransport.cs @@ -34,7 +34,7 @@ protected override bool IsConnectRequest { get { - return Context.Request.Path.EndsWith("/connect", StringComparison.OrdinalIgnoreCase); + return Context.Request.Url.LocalPath.EndsWith("/connect", StringComparison.OrdinalIgnoreCase); } } diff --git a/SignalR/Transports/ForeverTransport.cs b/SignalR/Transports/ForeverTransport.cs index ae8a2c858..f2e1b401b 100644 --- a/SignalR/Transports/ForeverTransport.cs +++ b/SignalR/Transports/ForeverTransport.cs @@ -101,7 +101,7 @@ public Func ProcessRequest(IReceivingConnection connection) { _connection = connection; - if (_context.Request.Path.EndsWith("/send")) + if (_context.Request.Url.LocalPath.EndsWith("/send")) { ProcessSendRequest(); } diff --git a/SignalR/Transports/LongPollingTransport.cs b/SignalR/Transports/LongPollingTransport.cs index 3bf803395..4d1d6c9f3 100644 --- a/SignalR/Transports/LongPollingTransport.cs +++ b/SignalR/Transports/LongPollingTransport.cs @@ -65,7 +65,7 @@ private bool IsConnectRequest { get { - return _context.Request.Path.EndsWith("/connect", StringComparison.OrdinalIgnoreCase); + return _context.Request.Url.LocalPath.EndsWith("/connect", StringComparison.OrdinalIgnoreCase); } } @@ -73,7 +73,7 @@ private bool IsSendRequest { get { - return _context.Request.Path.EndsWith("/send", StringComparison.OrdinalIgnoreCase); + return _context.Request.Url.LocalPath.EndsWith("/send", StringComparison.OrdinalIgnoreCase); } } From 4d52e5a4064356d2d5f5d626017c79e88219f196 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Fri, 30 Dec 2011 12:19:34 -0800 Subject: [PATCH 0300/1128] Change HttpContext variable to aspnet.HttpContext. --- SignalR.AspNet/AspNetHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SignalR.AspNet/AspNetHost.cs b/SignalR.AspNet/AspNetHost.cs index df988f07a..dadb5ef78 100644 --- a/SignalR.AspNet/AspNetHost.cs +++ b/SignalR.AspNet/AspNetHost.cs @@ -35,7 +35,7 @@ public override Task ProcessRequestAsync(HttpContextBase context) // Stick the context in here so transports or other asp.net specific logic can // grab at it. - hostContext.Items["aspnet.context"] = context; + hostContext.Items["aspnet.HttpContext"] = context; return _connection.ProcessRequestAsync(hostContext); } From 8c519b6ec08bcb55374e2b19977433a2ae5a8312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mats=20N=C3=A4sstr=C3=B6m?= Date: Fri, 30 Dec 2011 20:20:36 +0100 Subject: [PATCH 0301/1128] Bypass ASP.NET request validation On ASP.NET hosts SignalR will now bypass request validation by default. --- SignalR.AspNet/AspNetRequest.cs | 22 ++++++++++++++++++++-- SignalR.Samples/Web.config | 2 +- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/SignalR.AspNet/AspNetRequest.cs b/SignalR.AspNet/AspNetRequest.cs index 9b9d709b7..0349ad642 100644 --- a/SignalR.AspNet/AspNetRequest.cs +++ b/SignalR.AspNet/AspNetRequest.cs @@ -2,12 +2,15 @@ using System.Collections.Specialized; using System.Web; using SignalR.Abstractions; +using Microsoft.Web.Infrastructure.DynamicValidationHelper; namespace SignalR.AspNet { public class AspNetRequest : IRequest { private readonly HttpRequestBase _request; + private NameValueCollection _form; + private NameValueCollection _queryString; public AspNetRequest(HttpRequestBase request) { @@ -17,6 +20,21 @@ public AspNetRequest(HttpRequestBase request) { Cookies.Add(key, request.Cookies[key].Value); } + + // Since the ValidationUtility has a dependency on HttpContext (not HttpContextBase) we + // need to check if we're out of HttpContext to preserve testability. + if (HttpContext.Current == null) + { + _form = _request.Form; + _queryString = _request.QueryString; + } + else + { + Func formGetter, queryGetter; + ValidationUtility.GetUnvalidatedCollections(HttpContext.Current, out formGetter, out queryGetter); + _form = formGetter(); + _queryString = queryGetter(); + } } public Uri Url @@ -31,7 +49,7 @@ public NameValueCollection QueryString { get { - return _request.QueryString; + return _queryString; } } @@ -47,7 +65,7 @@ public NameValueCollection Form { get { - return _request.Form; + return _form; } } diff --git a/SignalR.Samples/Web.config b/SignalR.Samples/Web.config index c2786c93e..21ef9f0e5 100644 --- a/SignalR.Samples/Web.config +++ b/SignalR.Samples/Web.config @@ -20,7 +20,7 @@ - + From 3b6d766f8ec5935643c917967ea8bde9f283565f Mon Sep 17 00:00:00 2001 From: David Fowler Date: Fri, 30 Dec 2011 14:23:15 -0800 Subject: [PATCH 0302/1128] Make variables readonly. --- SignalR.AspNet/AspNetRequest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SignalR.AspNet/AspNetRequest.cs b/SignalR.AspNet/AspNetRequest.cs index 0349ad642..b8c1347e0 100644 --- a/SignalR.AspNet/AspNetRequest.cs +++ b/SignalR.AspNet/AspNetRequest.cs @@ -1,16 +1,16 @@ using System; using System.Collections.Specialized; using System.Web; -using SignalR.Abstractions; using Microsoft.Web.Infrastructure.DynamicValidationHelper; +using SignalR.Abstractions; namespace SignalR.AspNet { public class AspNetRequest : IRequest { private readonly HttpRequestBase _request; - private NameValueCollection _form; - private NameValueCollection _queryString; + private readonly NameValueCollection _form; + private readonly NameValueCollection _queryString; public AspNetRequest(HttpRequestBase request) { From add21f171ad86345278fac43d18663d1a2b96767 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Fri, 30 Dec 2011 21:04:44 -0800 Subject: [PATCH 0303/1128] Added HttpListener based signalr host as SignalR.SelfHost. - Ensure to only initialize default transports once. --- .../HttpListenerRequestWrapper.cs | 107 ++++++++++++++++ .../HttpListenerResponseWrapper.cs | 64 ++++++++++ .../Infrastructure/ResponseExtensions.cs | 53 ++++++++ SignalR.SelfHost/Properties/AssemblyInfo.cs | 6 + SignalR.SelfHost/Server.cs | 119 ++++++++++++++++++ SignalR.SelfHost/SignalR.SelfHost.csproj | 69 ++++++++++ SignalR.sln | 6 + SignalR/TaskAsyncHelper.cs | 2 +- SignalR/Transports/TransportManager.cs | 14 ++- 9 files changed, 435 insertions(+), 5 deletions(-) create mode 100644 SignalR.SelfHost/HttpListenerRequestWrapper.cs create mode 100644 SignalR.SelfHost/HttpListenerResponseWrapper.cs create mode 100644 SignalR.SelfHost/Infrastructure/ResponseExtensions.cs create mode 100644 SignalR.SelfHost/Properties/AssemblyInfo.cs create mode 100644 SignalR.SelfHost/Server.cs create mode 100644 SignalR.SelfHost/SignalR.SelfHost.csproj diff --git a/SignalR.SelfHost/HttpListenerRequestWrapper.cs b/SignalR.SelfHost/HttpListenerRequestWrapper.cs new file mode 100644 index 000000000..7097297a8 --- /dev/null +++ b/SignalR.SelfHost/HttpListenerRequestWrapper.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Specialized; +using System.IO; +using System.Net; +using SignalR.Abstractions; + +namespace SignalR.SelfHost +{ + public class HttpListenerRequestWrapper : IRequest + { + private readonly HttpListenerRequest _httpListenerRequest; + private readonly NameValueCollection _qs; + private NameValueCollection _form; + private readonly NameValueCollection _headers; + private readonly NameValueCollection _cookies; + + public HttpListenerRequestWrapper(HttpListenerRequest httpListenerRequest) + { + _httpListenerRequest = httpListenerRequest; + _qs = new NameValueCollection(httpListenerRequest.QueryString); + _headers = new NameValueCollection(httpListenerRequest.Headers); + _cookies = new NameValueCollection(); + + foreach (Cookie cookie in httpListenerRequest.Cookies) + { + _cookies[cookie.Name] = cookie.Value; + } + } + + public NameValueCollection Cookies + { + get + { + return _cookies; + } + } + + public NameValueCollection Form + { + get + { + EnsureForm(); + return _form; + } + } + + public NameValueCollection Headers + { + get + { + return _headers; + } + } + + public Uri Url + { + get + { + return _httpListenerRequest.Url; + } + } + + public NameValueCollection QueryString + { + get + { + return _qs; + } + } + + private void EnsureForm() + { + if (_form == null) + { + _form = new NameValueCollection(); + + // Do nothing if there's no body + if (!_httpListenerRequest.HasEntityBody) + { + return; + } + + using (var sw = new StreamReader(_httpListenerRequest.InputStream)) + { + var body = sw.ReadToEnd(); + string[] pairs = body.Split(new[] { "&" }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var pair in pairs) + { + string[] entry = pair.Split('='); + if (entry.Length > 1) + { + _form.Add(entry[0], UrlDecode(entry[1])); + } + } + } + } + } + + private static string UrlDecode(string url) + { + // HACK: Uri.UnescapeDataString doesn't seem to handle + + // TODO: Copy impl from System.Web.HttpUtility.UrlDecode + return Uri.UnescapeDataString(url).Replace("+", " "); + } + } +} diff --git a/SignalR.SelfHost/HttpListenerResponseWrapper.cs b/SignalR.SelfHost/HttpListenerResponseWrapper.cs new file mode 100644 index 000000000..1f6c4d98c --- /dev/null +++ b/SignalR.SelfHost/HttpListenerResponseWrapper.cs @@ -0,0 +1,64 @@ +using System.Net; +using System.Threading.Tasks; +using SignalR.Abstractions; +using SignalR.SelfHost.Infrastructure; + +namespace SignalR.SelfHost +{ + public class HttpListenerResponseWrapper : IResponse + { + private readonly HttpListenerResponse _httpListenerResponse; + + public HttpListenerResponseWrapper(HttpListenerResponse httpListenerResponse) + { + _httpListenerResponse = httpListenerResponse; + Buffer = true; + IsClientConnected = true; + } + + public bool Buffer + { + get; + set; + } + + public string ContentType + { + get + { + return _httpListenerResponse.ContentType; + } + set + { + _httpListenerResponse.ContentType = value; + } + } + + public bool IsClientConnected + { + get; + private set; + } + + public Task WriteAsync(string data) + { + return _httpListenerResponse.WriteAsync(data).ContinueWith(task => + { + if (task.IsFaulted) + { + var ex = task.Exception.GetBaseException() as HttpListenerException; + if (ex != null && ex.ErrorCode == 1229) + { + // Non existent connection or connection disposed + IsClientConnected = false; + } + } + else if (!Buffer) + { + // Flush the response if we aren't buffering + _httpListenerResponse.OutputStream.Flush(); + } + }); + } + } +} diff --git a/SignalR.SelfHost/Infrastructure/ResponseExtensions.cs b/SignalR.SelfHost/Infrastructure/ResponseExtensions.cs new file mode 100644 index 000000000..beaa934c6 --- /dev/null +++ b/SignalR.SelfHost/Infrastructure/ResponseExtensions.cs @@ -0,0 +1,53 @@ +using System; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace SignalR.SelfHost.Infrastructure +{ + public static class ResponseExtensions + { + public static Task NotFound(this HttpListenerResponse response) + { + response.StatusCode = 404; + return TaskAsyncHelper.Empty; + } + + public static Task ServerError(this HttpListenerResponse response, Exception exception) + { + response.StatusCode = 500; + return response.WriteAsync(exception.ToString()); + } + + public static Task WriteAsync(this HttpListenerResponse response, string value) + { + return WriteAsync(response, Encoding.UTF8.GetBytes(value)); + } + + public static Task WriteAsync(this HttpListenerResponse response, byte[] buffer) + { + try + { + return Task.Factory.FromAsync((cb, state) => response.OutputStream.BeginWrite(buffer, 0, buffer.Length, cb, state), + ar => response.OutputStream.EndWrite(ar), + null); + } + catch (Exception ex) + { + return TaskAsyncHelper.FromError(ex); + } + } + + public static void CloseSafe(this HttpListenerResponse response) + { + try + { + response.Close(); + } + catch + { + // Swallow exceptions while closing just in case the connection goes away + } + } + } +} diff --git a/SignalR.SelfHost/Properties/AssemblyInfo.cs b/SignalR.SelfHost/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..734daf201 --- /dev/null +++ b/SignalR.SelfHost/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +using System.Reflection; + + +[assembly: AssemblyTitle("SignalR.SelfHost")] +[assembly: AssemblyDescription("HttpListener host for SignalR")] +[assembly: AssemblyVersion("0.3.6")] diff --git a/SignalR.SelfHost/Server.cs b/SignalR.SelfHost/Server.cs new file mode 100644 index 000000000..eff3cec38 --- /dev/null +++ b/SignalR.SelfHost/Server.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Net; +using System.Threading.Tasks; +using SignalR.Abstractions; +using SignalR.Infrastructure; +using SignalR.SelfHost.Infrastructure; +using SignalR.Transports; + +namespace SignalR.SelfHost +{ + public class Server + { + private readonly HttpListener _listener; + private readonly Dictionary _connectionMapping = new Dictionary(); + + static Server() + { + TransportManager.InitializeDefaultTransports(); + } + + public Server(string url) + { + _listener = new HttpListener(); + _listener.Prefixes.Add(url); + } + + public void Start() + { + _listener.Start(); + + ReceiveLoop(); + } + + public void Stop() + { + _listener.Stop(); + } + + public Server MapConnection(string path) where T : PersistentConnection + { + if (!_connectionMapping.ContainsKey(path)) + { + _connectionMapping.Add(path, typeof(T)); + } + + return this; + } + + private void ReceiveLoop() + { + _listener.BeginGetContext(ar => + { + HttpListenerContext context = _listener.EndGetContext(ar); + ReceiveLoop(); + + // Process the request async + ProcessRequestAsync(context).ContinueWith(task => + { + if (task.IsFaulted) + { + Exception ex = task.Exception.GetBaseException(); + context.Response.ServerError(ex).Catch(); + + Debug.WriteLine(ex.Message); + } + + context.Response.CloseSafe(); + }); + + }, null); + } + + private Task ProcessRequestAsync(HttpListenerContext context) + { + try + { + Debug.WriteLine("Incoming request to {0}.", context.Request.Url); + + PersistentConnection connection; + if (TryGetConnection(context, out connection)) + { + var request = new HttpListenerRequestWrapper(context.Request); + var response = new HttpListenerResponseWrapper(context.Response); + var hostContext = new HostContext(request, response, context.User); + + hostContext.Items["net.HttpListenerContext"] = context; + + return connection.ProcessRequestAsync(hostContext); + } + + return context.Response.NotFound(); + } + catch (Exception ex) + { + return TaskAsyncHelper.FromError(ex); + } + } + + private bool TryGetConnection(HttpListenerContext context, out PersistentConnection connection) + { + connection = null; + + foreach (var pair in _connectionMapping) + { + // If the url matches then create the connection type + if (context.Request.RawUrl.StartsWith(pair.Key, StringComparison.OrdinalIgnoreCase)) + { + var factory = DependencyResolver.Resolve(); + connection = factory.CreateInstance(pair.Value); + return true; + } + } + + return false; + } + } +} diff --git a/SignalR.SelfHost/SignalR.SelfHost.csproj b/SignalR.SelfHost/SignalR.SelfHost.csproj new file mode 100644 index 000000000..69d88d2d4 --- /dev/null +++ b/SignalR.SelfHost/SignalR.SelfHost.csproj @@ -0,0 +1,69 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {A2BD866D-906A-4742-AB3B-DB8740830400} + Library + Properties + SignalR.SelfHost + SignalR.SelfHost + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + Properties\CommonAssemblyInfo.cs + + + Infrastructure\TaskAsyncHelper.cs + + + + + + + + + + {1B9A82C4-BCA1-4834-A33E-226F17BE070B} + SignalR + + + + + \ No newline at end of file diff --git a/SignalR.sln b/SignalR.sln index eb5ce7e0c..8146902bd 100644 --- a/SignalR.sln +++ b/SignalR.sln @@ -19,6 +19,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.Tests", "SignalR.Te EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.AspNet", "SignalR.AspNet\SignalR.AspNet.csproj", "{0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.SelfHost", "SignalR.SelfHost\SignalR.SelfHost.csproj", "{A2BD866D-906A-4742-AB3B-DB8740830400}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -49,6 +51,10 @@ Global {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Debug|Any CPU.Build.0 = Debug|Any CPU {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Release|Any CPU.ActiveCfg = Release|Any CPU {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Release|Any CPU.Build.0 = Release|Any CPU + {A2BD866D-906A-4742-AB3B-DB8740830400}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2BD866D-906A-4742-AB3B-DB8740830400}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2BD866D-906A-4742-AB3B-DB8740830400}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2BD866D-906A-4742-AB3B-DB8740830400}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SignalR/TaskAsyncHelper.cs b/SignalR/TaskAsyncHelper.cs index 1d1b532a9..f7356e6e5 100644 --- a/SignalR/TaskAsyncHelper.cs +++ b/SignalR/TaskAsyncHelper.cs @@ -477,7 +477,7 @@ where parameter.ParameterType.IsGenericType && } - private static Task FromError(Exception e) + internal static Task FromError(Exception e) { var tcs = new TaskCompletionSource(); tcs.SetException(e); diff --git a/SignalR/Transports/TransportManager.cs b/SignalR/Transports/TransportManager.cs index 26f642e35..169d9d2ce 100644 --- a/SignalR/Transports/TransportManager.cs +++ b/SignalR/Transports/TransportManager.cs @@ -8,6 +8,7 @@ namespace SignalR.Transports public static class TransportManager { private static readonly ConcurrentDictionary> _transports = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); + private static bool _initialized; public static void Register(string transportName, Func transportFactory) { @@ -40,10 +41,15 @@ internal static ITransport GetTransport(HostContext context) public static void InitializeDefaultTransports() { - Register("foreverFrame", context => new ForeverFrameTransport(context, DependencyResolver.Resolve())); - Register("serverSentEvents", context => new ServerSentEventsTransport(context, DependencyResolver.Resolve())); - Register("longPolling", context => new LongPollingTransport(context, DependencyResolver.Resolve())); - Register("forever", context => new ForeverTransport(context, DependencyResolver.Resolve())); + if (!_initialized) + { + Register("foreverFrame", context => new ForeverFrameTransport(context, DependencyResolver.Resolve())); + Register("serverSentEvents", context => new ServerSentEventsTransport(context, DependencyResolver.Resolve())); + Register("longPolling", context => new LongPollingTransport(context, DependencyResolver.Resolve())); + Register("forever", context => new ForeverTransport(context, DependencyResolver.Resolve())); + + _initialized = true; + } } } } From 13b258f2f00437f65460400fd52f37149521ddb0 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Fri, 30 Dec 2011 21:29:47 -0800 Subject: [PATCH 0304/1128] Added support for hubs in self hosted scenario. --- SignalR.SelfHost/Server.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/SignalR.SelfHost/Server.cs b/SignalR.SelfHost/Server.cs index eff3cec38..86befdddd 100644 --- a/SignalR.SelfHost/Server.cs +++ b/SignalR.SelfHost/Server.cs @@ -4,6 +4,7 @@ using System.Net; using System.Threading.Tasks; using SignalR.Abstractions; +using SignalR.Hubs; using SignalR.Infrastructure; using SignalR.SelfHost.Infrastructure; using SignalR.Transports; @@ -14,6 +15,7 @@ public class Server { private readonly HttpListener _listener; private readonly Dictionary _connectionMapping = new Dictionary(); + private bool _hubsEnabled; static Server() { @@ -38,14 +40,17 @@ public void Stop() _listener.Stop(); } - public Server MapConnection(string path) where T : PersistentConnection + public void MapConnection(string path) where T : PersistentConnection { if (!_connectionMapping.ContainsKey(path)) { _connectionMapping.Add(path, typeof(T)); } + } - return this; + public void EnableHubs() + { + _hubsEnabled = true; } private void ReceiveLoop() @@ -102,6 +107,12 @@ private bool TryGetConnection(HttpListenerContext context, out PersistentConnect { connection = null; + if (_hubsEnabled && context.Request.RawUrl.StartsWith("/signalr", StringComparison.OrdinalIgnoreCase)) + { + connection = new HubDispatcher("/signalr"); + return true; + } + foreach (var pair in _connectionMapping) { // If the url matches then create the connection type From 9cbd4eb0d8a8f7c1dfe0106a6d49de9f5239b346 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sat, 31 Dec 2011 06:59:38 -0800 Subject: [PATCH 0305/1128] Use the full type name when injecting custom variables into the host. Added HostConstants for well known variables. --- SignalR.AspNet/AspNetHost.cs | 6 +++--- SignalR.SelfHost/Server.cs | 5 ++++- SignalR/Abstractions/HostConstants.cs | 15 +++++++++++++++ SignalR/SignalR.csproj | 1 + 4 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 SignalR/Abstractions/HostConstants.cs diff --git a/SignalR.AspNet/AspNetHost.cs b/SignalR.AspNet/AspNetHost.cs index dadb5ef78..fe8d1a22b 100644 --- a/SignalR.AspNet/AspNetHost.cs +++ b/SignalR.AspNet/AspNetHost.cs @@ -28,14 +28,14 @@ public override Task ProcessRequestAsync(HttpContextBase context) var hostContext = new HostContext(request, response, context.User); // Determine if the client should bother to try a websocket request - hostContext.Items["supportsWebSockets"] = _hasAcceptWebSocketRequest.Value; + hostContext.Items[HostConstants.SupportsWebSockets] = _hasAcceptWebSocketRequest.Value; // Set the debugging flag - hostContext.Items["debugMode"] = context.IsDebuggingEnabled; + hostContext.Items[HostConstants.DebugMode] = context.IsDebuggingEnabled; // Stick the context in here so transports or other asp.net specific logic can // grab at it. - hostContext.Items["aspnet.HttpContext"] = context; + hostContext.Items["System.Web.HttpContext"] = context; return _connection.ProcessRequestAsync(hostContext); } diff --git a/SignalR.SelfHost/Server.cs b/SignalR.SelfHost/Server.cs index 86befdddd..b6166de3c 100644 --- a/SignalR.SelfHost/Server.cs +++ b/SignalR.SelfHost/Server.cs @@ -90,7 +90,10 @@ private Task ProcessRequestAsync(HttpListenerContext context) var response = new HttpListenerResponseWrapper(context.Response); var hostContext = new HostContext(request, response, context.User); - hostContext.Items["net.HttpListenerContext"] = context; +#if DEBUG + hostContext.Items[HostConstants.DebugMode] = true; +#endif + hostContext.Items["System.Net.HttpListenerContext"] = context; return connection.ProcessRequestAsync(hostContext); } diff --git a/SignalR/Abstractions/HostConstants.cs b/SignalR/Abstractions/HostConstants.cs new file mode 100644 index 000000000..e448cd267 --- /dev/null +++ b/SignalR/Abstractions/HostConstants.cs @@ -0,0 +1,15 @@ +namespace SignalR.Abstractions +{ + public static class HostConstants + { + /// + /// The host should set this if they need to enable debug mode + /// + public static readonly string DebugMode = "debugMode"; + + /// + /// The host should set this is websockets can be supported + /// + public static readonly string SupportsWebSockets = "supportsWebSockets"; + } +} diff --git a/SignalR/SignalR.csproj b/SignalR/SignalR.csproj index fba8dfb73..818ef41cc 100644 --- a/SignalR/SignalR.csproj +++ b/SignalR/SignalR.csproj @@ -50,6 +50,7 @@ Properties\CommonAssemblyInfo.cs + From f9fe920fbe42f7fe788b5a56252c99598814e6db Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sat, 31 Dec 2011 07:58:13 -0800 Subject: [PATCH 0306/1128] Fixes #83. Fixes #108. Expose all headers in the HubContext. --- SignalR/Hubs/HubContext.cs | 13 ++++++++++--- SignalR/Hubs/HubDispatcher.cs | 10 ++++------ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/SignalR/Hubs/HubContext.cs b/SignalR/Hubs/HubContext.cs index 910fc3f25..d15af6620 100644 --- a/SignalR/Hubs/HubContext.cs +++ b/SignalR/Hubs/HubContext.cs @@ -1,5 +1,6 @@ using System.Collections.Specialized; using System.Security.Principal; +using SignalR.Abstractions; namespace SignalR.Hubs { @@ -15,13 +16,19 @@ public class HubContext /// public NameValueCollection Cookies { get; private set; } + /// + /// Gets the headers for the request + /// + public NameValueCollection Headers { get; private set; } + public IPrincipal User { get; private set; } - public HubContext(string connectionId, NameValueCollection cookies, IPrincipal user) + public HubContext(HostContext context, string connectionId) { ConnectionId = connectionId; - Cookies = cookies; - User = user; + Cookies = context.Request.Cookies; + Headers = context.Request.Headers; + User = context.User; } } } diff --git a/SignalR/Hubs/HubDispatcher.cs b/SignalR/Hubs/HubDispatcher.cs index 6c47a0ebd..25283fc22 100644 --- a/SignalR/Hubs/HubDispatcher.cs +++ b/SignalR/Hubs/HubDispatcher.cs @@ -33,8 +33,7 @@ public class HubDispatcher : PersistentConnection private readonly IHubTypeResolver _hubTypeResolver; private readonly IJsonSerializer _jsonSerializer; - private NameValueCollection _cookies; - private IPrincipal _user; + private HostContext _context; public HubDispatcher(string url) : this(DependencyResolver.Resolve(), @@ -94,7 +93,7 @@ protected override Task OnReceivedAsync(string connectionId, string data) string hubName = hub.GetType().FullName; var state = new TrackingDictionary(hubRequest.State); - hub.Context = new HubContext(connectionId, _cookies, _user); + hub.Context = new HubContext(_context, connectionId); hub.Caller = new SignalAgent(Connection, connectionId, hubName, state); var agent = new ClientAgent(Connection, hubName); hub.Agent = agent; @@ -165,8 +164,7 @@ public override Task ProcessRequestAsync(HostContext context) return context.Response.WriteAsync(_proxyGenerator.GenerateProxy(_url)); } - _cookies = context.Request.Cookies; - _user = context.User; + _context = context; return base.ProcessRequestAsync(context); } @@ -185,7 +183,7 @@ protected override Task OnDisconnectAsync(string connectionId) // REVIEW: We don't have any client state here since we're calling this from the server. // Will this match user expectations? var state = new TrackingDictionary(); - hub.Context = new HubContext(connectionId, _cookies, _user); + hub.Context = new HubContext(_context, connectionId); hub.Caller = new SignalAgent(Connection, connectionId, hubName, state); var agent = new ClientAgent(Connection, hubName); hub.Agent = agent; From af62e04123ef3c3e5975038ba17f804553424993 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sat, 31 Dec 2011 11:34:11 -0800 Subject: [PATCH 0307/1128] Use HostConstants in the extensions. --- SignalR/Abstractions/HostContextExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SignalR/Abstractions/HostContextExtensions.cs b/SignalR/Abstractions/HostContextExtensions.cs index 00fa621b4..7ef40ab24 100644 --- a/SignalR/Abstractions/HostContextExtensions.cs +++ b/SignalR/Abstractions/HostContextExtensions.cs @@ -14,12 +14,12 @@ public static T GetValue(this HostContext context, string key) public static bool IsDebuggingEnabled(this HostContext context) { - return context.GetValue("debugMode"); + return context.GetValue(HostConstants.DebugMode); } public static bool SupportsWebSockets(this HostContext context) { - return context.GetValue("supportsWebSockets"); + return context.GetValue(HostConstants.SupportsWebSockets); } } } From 60692747d83d024a592bdd7b0bac079e98b85b99 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sat, 31 Dec 2011 11:58:09 -0800 Subject: [PATCH 0308/1128] Resolve paths based on the base url. --- SignalR.SelfHost/Server.cs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/SignalR.SelfHost/Server.cs b/SignalR.SelfHost/Server.cs index b6166de3c..348f483b8 100644 --- a/SignalR.SelfHost/Server.cs +++ b/SignalR.SelfHost/Server.cs @@ -13,6 +13,7 @@ namespace SignalR.SelfHost { public class Server { + private readonly string _url; private readonly HttpListener _listener; private readonly Dictionary _connectionMapping = new Dictionary(); private bool _hubsEnabled; @@ -24,6 +25,7 @@ static Server() public Server(string url) { + _url = url; _listener = new HttpListener(); _listener.Prefixes.Add(url); } @@ -110,7 +112,9 @@ private bool TryGetConnection(HttpListenerContext context, out PersistentConnect { connection = null; - if (_hubsEnabled && context.Request.RawUrl.StartsWith("/signalr", StringComparison.OrdinalIgnoreCase)) + string path = ResolvePath(context.Request.Url); + + if (_hubsEnabled && path.StartsWith("/signalr", StringComparison.OrdinalIgnoreCase)) { connection = new HubDispatcher("/signalr"); return true; @@ -119,7 +123,7 @@ private bool TryGetConnection(HttpListenerContext context, out PersistentConnect foreach (var pair in _connectionMapping) { // If the url matches then create the connection type - if (context.Request.RawUrl.StartsWith(pair.Key, StringComparison.OrdinalIgnoreCase)) + if (path.StartsWith(pair.Key, StringComparison.OrdinalIgnoreCase)) { var factory = DependencyResolver.Resolve(); connection = factory.CreateInstance(pair.Value); @@ -129,5 +133,23 @@ private bool TryGetConnection(HttpListenerContext context, out PersistentConnect return false; } + + private string ResolvePath(Uri url) + { + string baseUrl = url.GetComponents(UriComponents.Scheme | UriComponents.HostAndPort | UriComponents.Path, UriFormat.SafeUnescaped); + + if (!baseUrl.StartsWith(_url, StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidOperationException("Unable to resolve path"); + } + + string path = baseUrl.Substring(_url.Length); + if (!path.StartsWith("/")) + { + return "/" + path; + } + + return path; + } } } From 7187da36f325ae64977670c52a1402504b166acf Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sun, 1 Jan 2012 14:49:08 -0800 Subject: [PATCH 0309/1128] Remove duplicate line (copy paste bug). --- SignalR.AspNet/AspNetResponse.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/SignalR.AspNet/AspNetResponse.cs b/SignalR.AspNet/AspNetResponse.cs index c8c214135..0dd989301 100644 --- a/SignalR.AspNet/AspNetResponse.cs +++ b/SignalR.AspNet/AspNetResponse.cs @@ -23,7 +23,6 @@ public bool Buffer } set { - _response.Buffer = value; _response.Buffer = value; _response.BufferOutput = value; From a57c64767dcec4413fdec8b1ac51b24f3b092415 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sun, 1 Jan 2012 15:46:41 -0800 Subject: [PATCH 0310/1128] Ported performance optimization for ASP.NET host from the perf branch. --- SignalR.AspNet/AspNetHost.cs | 2 +- SignalR.AspNet/AspNetResponse.cs | 70 ++++++++++++++++++++++++-------- 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/SignalR.AspNet/AspNetHost.cs b/SignalR.AspNet/AspNetHost.cs index fe8d1a22b..d4c425a2e 100644 --- a/SignalR.AspNet/AspNetHost.cs +++ b/SignalR.AspNet/AspNetHost.cs @@ -24,7 +24,7 @@ public AspNetHost(PersistentConnection connection) public override Task ProcessRequestAsync(HttpContextBase context) { var request = new AspNetRequest(context.Request); - var response = new AspNetResponse(context.Request, context.Response); + var response = new AspNetResponse(context); var hostContext = new HostContext(request, response, context.User); // Determine if the client should bother to try a websocket request diff --git a/SignalR.AspNet/AspNetResponse.cs b/SignalR.AspNet/AspNetResponse.cs index 0dd989301..1ddfa99d4 100644 --- a/SignalR.AspNet/AspNetResponse.cs +++ b/SignalR.AspNet/AspNetResponse.cs @@ -1,4 +1,6 @@ -using System.Threading.Tasks; +using System.Linq.Expressions; +using System.Reflection; +using System.Threading.Tasks; using System.Web; using SignalR.Abstractions; @@ -6,34 +8,38 @@ namespace SignalR.AspNet { public class AspNetResponse : IResponse { - private readonly HttpRequestBase _request; - private readonly HttpResponseBase _response; + private delegate void RemoveHeaderDel(HttpWorkerRequest workerRequest); - public AspNetResponse(HttpRequestBase request, HttpResponseBase response) + private const string IIS7WorkerRequestTypeName = "System.Web.Hosting.IIS7WorkerRequest"; + private static readonly RemoveHeaderDel IIS7RemoveHeader = GetRemoveHeaderDelegate(); + + private readonly HttpContextBase _context; + + public AspNetResponse(HttpContextBase context) { - _request = request; - _response = response; + _context = context; } public bool Buffer { get { - return _response.Buffer; + return _context.Response.Buffer; } set { - _response.Buffer = value; - _response.BufferOutput = value; + _context.Response.Buffer = value; + _context.Response.BufferOutput = value; if (!value) { // This forces the IIS compression module to leave this response alone. // If we don't do this, it will buffer the response to suit its own compression - // logic, resulting in partial messages being sent to the client. - _request.Headers.Remove("Accept-Encoding"); - _response.CacheControl = "no-cache"; - _response.AddHeader("Connection", "keep-alive"); + // logic, resulting in partial messages being sent to the client. + RemoveAcceptEncoding(); + + _context.Response.CacheControl = "no-cache"; + _context.Response.AddHeader("Connection", "keep-alive"); } } } @@ -42,7 +48,7 @@ public bool IsClientConnected { get { - return _response.IsClientConnected; + return _context.Response.IsClientConnected; } } @@ -50,18 +56,48 @@ public string ContentType { get { - return _response.ContentType; + return _context.Response.ContentType; } set { - _response.ContentType = value; + _context.Response.ContentType = value; } } public Task WriteAsync(string data) { - _response.Write(data); + _context.Response.Write(data); return TaskAsyncHelper.Empty; } + + private void RemoveAcceptEncoding() + { + var workerRequest = (HttpWorkerRequest)_context.GetService(typeof(HttpWorkerRequest)); + if (IsIIS7WorkerRequest(workerRequest)) + { + // Optimized code path for IIS7, accessing Headers causes all headers to be read + IIS7RemoveHeader(workerRequest); + } + else + { + _context.Request.Headers.Remove("Accept-Encoding"); + } + } + + private bool IsIIS7WorkerRequest(HttpWorkerRequest workerRequest) + { + return workerRequest.GetType().FullName == IIS7WorkerRequestTypeName; + } + + private static RemoveHeaderDel GetRemoveHeaderDelegate() + { + var iis7wrType = typeof(HttpContext).Assembly.GetType(IIS7WorkerRequestTypeName); + var methodInfo = iis7wrType.GetMethod("SetKnownRequestHeader", BindingFlags.NonPublic | BindingFlags.Instance); + + var wrParamExpr = Expression.Parameter(typeof(HttpWorkerRequest)); + var iis7wrParamExpr = Expression.Convert(wrParamExpr, iis7wrType); + var callExpr = Expression.Call(iis7wrParamExpr, methodInfo, Expression.Constant(HttpWorkerRequest.HeaderAcceptEncoding), Expression.Constant(null, typeof(string)), Expression.Constant(false)); + return Expression.Lambda(callExpr, wrParamExpr).Compile(); + } } } From 3845510b5e94a8b8ec9b6f067182a5be6129159c Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sun, 1 Jan 2012 22:29:25 -0800 Subject: [PATCH 0311/1128] Allow server to specify the websocket url in during negotiation. - Added callback to self hosted server to allow hooking the request. - Updated the client to allow overriding the websocket url. --- SignalR.Samples/Scripts/jquery.signalR.js | 17 +++++++--- SignalR.Samples/Scripts/jquery.signalR.min.js | 2 +- SignalR.SelfHost/Server.cs | 34 +++++++++++++------ SignalR/Abstractions/HostConstants.cs | 7 +++- SignalR/Abstractions/HostContextExtensions.cs | 5 +++ SignalR/PersistentConnection.cs | 3 +- SignalR/Scripts/jquery.signalR.js | 17 +++++++--- SignalR/Scripts/jquery.signalR.min.js | 2 +- 8 files changed, 63 insertions(+), 24 deletions(-) diff --git a/SignalR.Samples/Scripts/jquery.signalR.js b/SignalR.Samples/Scripts/jquery.signalR.js index 2f1487c46..3d56d2b58 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.js +++ b/SignalR.Samples/Scripts/jquery.signalR.js @@ -92,6 +92,7 @@ success: function (res) { connection.appRelativeUrl = res.Url; connection.id = res.ConnectionId; + connection.webSocketServerUrl = res.WebSocketServerUrl; $(connection).trigger("onStarting"); @@ -303,9 +304,17 @@ } if (!connection.socket) { - // Build the url - url = document.location.host + connection.appRelativeUrl; + if (connection.webSocketServerUrl) { + url = connection.webSocketServerUrl; + } + else { + // Determine the protocol + protocol = document.location.protocol === "https:" ? "wss://" : "ws://"; + + url = protocol + document.location.host + connection.appRelativeUrl; + } + // Build the url $(connection).trigger("onSending"); if (connection.data) { url += "?connectionData=" + connection.data + "&transport=webSockets&connectionId=" + connection.id; @@ -313,9 +322,7 @@ url += "?transport=webSockets&connectionId=" + connection.id; } - protocol = document.location.protocol === "https:" ? "wss://" : "ws://"; - - connection.socket = new window.WebSocket(protocol + url); + connection.socket = new window.WebSocket(url); connection.socket.onopen = function () { opened = true; if (onSuccess) { diff --git a/SignalR.Samples/Scripts/jquery.signalR.min.js b/SignalR.Samples/Scripts/jquery.signalR.min.js index 75bcf6489..a419af66b 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.min.js +++ b/SignalR.Samples/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i){var u=n.url+"/connect",r="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(r="connectionData="+t.escape(n.data)+"&"+r),u+="?"+r},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&n.each(i.Messages,function(){try{n(t).trigger("onReceived",[this])}catch(i){u("Error raising received "+i)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,u){var f,o=!1,e;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){u();return}i.socket||(f=document.location.host+i.appRelativeUrl,n(i).trigger("onSending"),f+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,e=document.location.protocol==="https:"?"wss://":"ws://",i.socket=new t.WebSocket(e+f),i.socket.onopen=function(){o=!0,r&&r()},i.socket.onclose=function(t){o?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):u&&u(),i.socket=null},i.socket.onmessage=function(r){var u=t.JSON.parse(r.data);u&&(u.Messages?n.each(u.Messages,function(){n(i).trigger("onReceived",[this])}):n(i).trigger("onReceived",[u]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var h=this,s=!1,o;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),o=i.getUrl(r,this.name),r.eventSource=new t.EventSource(o),r.eventSource.addEventListener("open",function(){s=!0,f()},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(t){s||e(),t.eventPhase==EventSource.CLOSED?(u("SignalR: EventSource closed"),h.stop()):(u("SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){t.setTimeout(function(){var t=n.frame,i=t.src.replace("/connect","")+"&messageId="+n.messageId+"&groups="+escape(n.groups);t.src=i},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}},longPolling:{name:"longPolling",start:function(r,u){r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var u=r.messageId,o=u===null,e=r.url+(o?"/connect":"")+"?transport=longPolling&connectionId="+escape(r.id)+"&messageId="+u+"&groups="+escape((r.groups||[]).toString());r.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl,n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i){var u=n.url+"/connect",r="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(r="connectionData="+t.escape(n.data)+"&"+r),u+="?"+r},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&n.each(i.Messages,function(){try{n(t).trigger("onReceived",[this])}catch(i){u("Error raising received "+i)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,u){var f,o=!1,e;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){u();return}i.socket||(i.webSocketServerUrl?f=i.webSocketServerUrl:(e=document.location.protocol==="https:"?"wss://":"ws://",f=e+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),f+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(f),i.socket.onopen=function(){o=!0,r&&r()},i.socket.onclose=function(t){o?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):u&&u(),i.socket=null},i.socket.onmessage=function(r){var u=t.JSON.parse(r.data);u&&(u.Messages?n.each(u.Messages,function(){n(i).trigger("onReceived",[this])}):n(i).trigger("onReceived",[u]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var h=this,s=!1,o;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),o=i.getUrl(r,this.name),r.eventSource=new t.EventSource(o),r.eventSource.addEventListener("open",function(){s=!0,f()},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(t){s||e(),t.eventPhase==EventSource.CLOSED?(u("SignalR: EventSource closed"),h.stop()):(u("SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){t.setTimeout(function(){var t=n.frame,i=t.src.replace("/connect","")+"&messageId="+n.messageId+"&groups="+escape(n.groups);t.src=i},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}},longPolling:{name:"longPolling",start:function(r,u){r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var u=r.messageId,o=u===null,e=r.url+(o?"/connect":"")+"?transport=longPolling&connectionId="+escape(r.id)+"&messageId="+u+"&groups="+escape((r.groups||[]).toString());r.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR.SelfHost/Server.cs b/SignalR.SelfHost/Server.cs index 348f483b8..4801301e8 100644 --- a/SignalR.SelfHost/Server.cs +++ b/SignalR.SelfHost/Server.cs @@ -18,6 +18,8 @@ public class Server private readonly Dictionary _connectionMapping = new Dictionary(); private bool _hubsEnabled; + public Action OnProcessRequest { get; set; } + static Server() { TransportManager.InitializeDefaultTransports(); @@ -55,6 +57,19 @@ public void EnableHubs() _hubsEnabled = true; } + public bool TryGetConnection(string path, out PersistentConnection connection) + { + connection = null; + + if (_hubsEnabled && path.StartsWith("/signalr", StringComparison.OrdinalIgnoreCase)) + { + connection = new HubDispatcher("/signalr"); + return true; + } + + return TryGetMappedConnection(path, out connection); + } + private void ReceiveLoop() { _listener.BeginGetContext(ar => @@ -86,12 +101,19 @@ private Task ProcessRequestAsync(HttpListenerContext context) Debug.WriteLine("Incoming request to {0}.", context.Request.Url); PersistentConnection connection; - if (TryGetConnection(context, out connection)) + + string path = ResolvePath(context.Request.Url); + + if (TryGetConnection(path, out connection)) { var request = new HttpListenerRequestWrapper(context.Request); var response = new HttpListenerResponseWrapper(context.Response); var hostContext = new HostContext(request, response, context.User); + if (OnProcessRequest != null) + { + OnProcessRequest(hostContext); + } #if DEBUG hostContext.Items[HostConstants.DebugMode] = true; #endif @@ -108,18 +130,10 @@ private Task ProcessRequestAsync(HttpListenerContext context) } } - private bool TryGetConnection(HttpListenerContext context, out PersistentConnection connection) + private bool TryGetMappedConnection(string path, out PersistentConnection connection) { connection = null; - string path = ResolvePath(context.Request.Url); - - if (_hubsEnabled && path.StartsWith("/signalr", StringComparison.OrdinalIgnoreCase)) - { - connection = new HubDispatcher("/signalr"); - return true; - } - foreach (var pair in _connectionMapping) { // If the url matches then create the connection type diff --git a/SignalR/Abstractions/HostConstants.cs b/SignalR/Abstractions/HostConstants.cs index e448cd267..e52cfcc7a 100644 --- a/SignalR/Abstractions/HostConstants.cs +++ b/SignalR/Abstractions/HostConstants.cs @@ -8,8 +8,13 @@ public static class HostConstants public static readonly string DebugMode = "debugMode"; /// - /// The host should set this is websockets can be supported + /// The host should set this is web sockets can be supported /// public static readonly string SupportsWebSockets = "supportsWebSockets"; + + /// + /// The host should set this if the web socket url is different + /// + public static readonly string WebSocketServerUrl = "webSocketServerUrl"; } } diff --git a/SignalR/Abstractions/HostContextExtensions.cs b/SignalR/Abstractions/HostContextExtensions.cs index 7ef40ab24..96fc1c313 100644 --- a/SignalR/Abstractions/HostContextExtensions.cs +++ b/SignalR/Abstractions/HostContextExtensions.cs @@ -21,5 +21,10 @@ public static bool SupportsWebSockets(this HostContext context) { return context.GetValue(HostConstants.SupportsWebSockets); } + + public static string WebSocketServerUrl(this HostContext context) + { + return context.GetValue(HostConstants.WebSocketServerUrl); + } } } diff --git a/SignalR/PersistentConnection.cs b/SignalR/PersistentConnection.cs index 2febedff2..ca3e9a02f 100644 --- a/SignalR/PersistentConnection.cs +++ b/SignalR/PersistentConnection.cs @@ -196,7 +196,8 @@ private Task ProcessNegotiationRequest(HostContext context) { Url = context.Request.Url.LocalPath.Replace("/negotiate", ""), ConnectionId = _connectionIdFactory.CreateConnectionId(context.Request), - TryWebSockets = context.SupportsWebSockets() + TryWebSockets = context.SupportsWebSockets(), + WebSocketServerUrl = context.WebSocketServerUrl() })); } diff --git a/SignalR/Scripts/jquery.signalR.js b/SignalR/Scripts/jquery.signalR.js index 2f1487c46..3d56d2b58 100644 --- a/SignalR/Scripts/jquery.signalR.js +++ b/SignalR/Scripts/jquery.signalR.js @@ -92,6 +92,7 @@ success: function (res) { connection.appRelativeUrl = res.Url; connection.id = res.ConnectionId; + connection.webSocketServerUrl = res.WebSocketServerUrl; $(connection).trigger("onStarting"); @@ -303,9 +304,17 @@ } if (!connection.socket) { - // Build the url - url = document.location.host + connection.appRelativeUrl; + if (connection.webSocketServerUrl) { + url = connection.webSocketServerUrl; + } + else { + // Determine the protocol + protocol = document.location.protocol === "https:" ? "wss://" : "ws://"; + + url = protocol + document.location.host + connection.appRelativeUrl; + } + // Build the url $(connection).trigger("onSending"); if (connection.data) { url += "?connectionData=" + connection.data + "&transport=webSockets&connectionId=" + connection.id; @@ -313,9 +322,7 @@ url += "?transport=webSockets&connectionId=" + connection.id; } - protocol = document.location.protocol === "https:" ? "wss://" : "ws://"; - - connection.socket = new window.WebSocket(protocol + url); + connection.socket = new window.WebSocket(url); connection.socket.onopen = function () { opened = true; if (onSuccess) { diff --git a/SignalR/Scripts/jquery.signalR.min.js b/SignalR/Scripts/jquery.signalR.min.js index 75bcf6489..a419af66b 100644 --- a/SignalR/Scripts/jquery.signalR.min.js +++ b/SignalR/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i){var u=n.url+"/connect",r="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(r="connectionData="+t.escape(n.data)+"&"+r),u+="?"+r},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&n.each(i.Messages,function(){try{n(t).trigger("onReceived",[this])}catch(i){u("Error raising received "+i)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,u){var f,o=!1,e;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){u();return}i.socket||(f=document.location.host+i.appRelativeUrl,n(i).trigger("onSending"),f+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,e=document.location.protocol==="https:"?"wss://":"ws://",i.socket=new t.WebSocket(e+f),i.socket.onopen=function(){o=!0,r&&r()},i.socket.onclose=function(t){o?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):u&&u(),i.socket=null},i.socket.onmessage=function(r){var u=t.JSON.parse(r.data);u&&(u.Messages?n.each(u.Messages,function(){n(i).trigger("onReceived",[this])}):n(i).trigger("onReceived",[u]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var h=this,s=!1,o;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),o=i.getUrl(r,this.name),r.eventSource=new t.EventSource(o),r.eventSource.addEventListener("open",function(){s=!0,f()},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(t){s||e(),t.eventPhase==EventSource.CLOSED?(u("SignalR: EventSource closed"),h.stop()):(u("SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){t.setTimeout(function(){var t=n.frame,i=t.src.replace("/connect","")+"&messageId="+n.messageId+"&groups="+escape(n.groups);t.src=i},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}},longPolling:{name:"longPolling",start:function(r,u){r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var u=r.messageId,o=u===null,e=r.url+(o?"/connect":"")+"?transport=longPolling&connectionId="+escape(r.id)+"&messageId="+u+"&groups="+escape((r.groups||[]).toString());r.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl,n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i){var u=n.url+"/connect",r="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(r="connectionData="+t.escape(n.data)+"&"+r),u+="?"+r},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&n.each(i.Messages,function(){try{n(t).trigger("onReceived",[this])}catch(i){u("Error raising received "+i)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,u){var f,o=!1,e;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){u();return}i.socket||(i.webSocketServerUrl?f=i.webSocketServerUrl:(e=document.location.protocol==="https:"?"wss://":"ws://",f=e+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),f+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(f),i.socket.onopen=function(){o=!0,r&&r()},i.socket.onclose=function(t){o?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):u&&u(),i.socket=null},i.socket.onmessage=function(r){var u=t.JSON.parse(r.data);u&&(u.Messages?n.each(u.Messages,function(){n(i).trigger("onReceived",[this])}):n(i).trigger("onReceived",[u]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var h=this,s=!1,o;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),o=i.getUrl(r,this.name),r.eventSource=new t.EventSource(o),r.eventSource.addEventListener("open",function(){s=!0,f()},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(t){s||e(),t.eventPhase==EventSource.CLOSED?(u("SignalR: EventSource closed"),h.stop()):(u("SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){t.setTimeout(function(){var t=n.frame,i=t.src.replace("/connect","")+"&messageId="+n.messageId+"&groups="+escape(n.groups);t.src=i},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}},longPolling:{name:"longPolling",start:function(r,u){r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var u=r.messageId,o=u===null,e=r.url+(o?"/connect":"")+"?transport=longPolling&connectionId="+escape(r.id)+"&messageId="+u+"&groups="+escape((r.groups||[]).toString());r.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file From edfd4551a9131eff26cc9e9be568fdc27721ac41 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sun, 1 Jan 2012 22:31:36 -0800 Subject: [PATCH 0312/1128] Made IsIIS7WorkerRequest static. --- SignalR.AspNet/AspNetResponse.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SignalR.AspNet/AspNetResponse.cs b/SignalR.AspNet/AspNetResponse.cs index 1ddfa99d4..4aa57e5ca 100644 --- a/SignalR.AspNet/AspNetResponse.cs +++ b/SignalR.AspNet/AspNetResponse.cs @@ -84,7 +84,7 @@ private void RemoveAcceptEncoding() } } - private bool IsIIS7WorkerRequest(HttpWorkerRequest workerRequest) + private static bool IsIIS7WorkerRequest(HttpWorkerRequest workerRequest) { return workerRequest.GetType().FullName == IIS7WorkerRequestTypeName; } From 4bba9e55e5947e1067e5969dad3f18fa72db142d Mon Sep 17 00:00:00 2001 From: David Fowler Date: Mon, 2 Jan 2012 05:21:52 -0800 Subject: [PATCH 0313/1128] Fix Task continuation logic. --- SignalR/Transports/ForeverTransport.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SignalR/Transports/ForeverTransport.cs b/SignalR/Transports/ForeverTransport.cs index f2e1b401b..d3d1198d0 100644 --- a/SignalR/Transports/ForeverTransport.cs +++ b/SignalR/Transports/ForeverTransport.cs @@ -227,7 +227,7 @@ private void ProcessMessagesImpl(TaskCompletionSource taskCompletetionSo taskCompletetionSource.SetException(t.Exception); } }, - TaskContinuationOptions.ExecuteSynchronously & TaskContinuationOptions.NotOnRanToCompletion); + TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnRanToCompletion); // Stop execution here return; From e6dbddb2bf540ccd0d24f50bc17e3de3aa359e0b Mon Sep 17 00:00:00 2001 From: David Fowler Date: Mon, 2 Jan 2012 05:32:13 -0800 Subject: [PATCH 0314/1128] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ff61229d..44025b9ae 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # SignalR -Async signaling library for ASP.NET to help build real-time, multi-user interactive web applications +Async signaling library for .NET to help build real-time, multi-user interactive web applications ## What can it be used for? Pushing data from the server to the client (not just browser clients) has always been a tough problem. SignalR makes From 33ba2557443aa6ca8893db4d2dabaf42c575a119 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Mon, 2 Jan 2012 11:40:38 -0800 Subject: [PATCH 0315/1128] Catch exceptions and log when there's an error in the websocket transport. --- SignalR.Samples/Scripts/jquery.signalR.js | 7 ++++++- SignalR.Samples/Scripts/jquery.signalR.min.js | 2 +- SignalR/Scripts/jquery.signalR.js | 7 ++++++- SignalR/Scripts/jquery.signalR.min.js | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/SignalR.Samples/Scripts/jquery.signalR.js b/SignalR.Samples/Scripts/jquery.signalR.js index 3d56d2b58..365b4b4bb 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.js +++ b/SignalR.Samples/Scripts/jquery.signalR.js @@ -349,7 +349,12 @@ if (data) { if (data.Messages) { $.each(data.Messages, function () { - $(connection).trigger("onReceived", [this]); + try { + $(connection).trigger("onReceived", [this]); + } + catch (e) { + log('Error raising received ' + e); + } }); } else { $(connection).trigger("onReceived", [data]); diff --git a/SignalR.Samples/Scripts/jquery.signalR.min.js b/SignalR.Samples/Scripts/jquery.signalR.min.js index a419af66b..83cb70211 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.min.js +++ b/SignalR.Samples/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl,n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i){var u=n.url+"/connect",r="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(r="connectionData="+t.escape(n.data)+"&"+r),u+="?"+r},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&n.each(i.Messages,function(){try{n(t).trigger("onReceived",[this])}catch(i){u("Error raising received "+i)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,u){var f,o=!1,e;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){u();return}i.socket||(i.webSocketServerUrl?f=i.webSocketServerUrl:(e=document.location.protocol==="https:"?"wss://":"ws://",f=e+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),f+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(f),i.socket.onopen=function(){o=!0,r&&r()},i.socket.onclose=function(t){o?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):u&&u(),i.socket=null},i.socket.onmessage=function(r){var u=t.JSON.parse(r.data);u&&(u.Messages?n.each(u.Messages,function(){n(i).trigger("onReceived",[this])}):n(i).trigger("onReceived",[u]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var h=this,s=!1,o;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),o=i.getUrl(r,this.name),r.eventSource=new t.EventSource(o),r.eventSource.addEventListener("open",function(){s=!0,f()},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(t){s||e(),t.eventPhase==EventSource.CLOSED?(u("SignalR: EventSource closed"),h.stop()):(u("SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){t.setTimeout(function(){var t=n.frame,i=t.src.replace("/connect","")+"&messageId="+n.messageId+"&groups="+escape(n.groups);t.src=i},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}},longPolling:{name:"longPolling",start:function(r,u){r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var u=r.messageId,o=u===null,e=r.url+(o?"/connect":"")+"?transport=longPolling&connectionId="+escape(r.id)+"&messageId="+u+"&groups="+escape((r.groups||[]).toString());r.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl,n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i){var u=n.url+"/connect",r="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(r="connectionData="+t.escape(n.data)+"&"+r),u+="?"+r},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&n.each(i.Messages,function(){try{n(t).trigger("onReceived",[this])}catch(i){u("Error raising received "+i)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data);f&&(f.Messages?n.each(f.Messages,function(){try{n(i).trigger("onReceived",[this])}catch(t){u("Error raising received "+t)}}):n(i).trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var h=this,s=!1,o;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),o=i.getUrl(r,this.name),r.eventSource=new t.EventSource(o),r.eventSource.addEventListener("open",function(){s=!0,f()},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(t){s||e(),t.eventPhase==EventSource.CLOSED?(u("SignalR: EventSource closed"),h.stop()):(u("SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){t.setTimeout(function(){var t=n.frame,i=t.src.replace("/connect","")+"&messageId="+n.messageId+"&groups="+escape(n.groups);t.src=i},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}},longPolling:{name:"longPolling",start:function(r,u){r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var u=r.messageId,o=u===null,e=r.url+(o?"/connect":"")+"?transport=longPolling&connectionId="+escape(r.id)+"&messageId="+u+"&groups="+escape((r.groups||[]).toString());r.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR/Scripts/jquery.signalR.js b/SignalR/Scripts/jquery.signalR.js index 3d56d2b58..365b4b4bb 100644 --- a/SignalR/Scripts/jquery.signalR.js +++ b/SignalR/Scripts/jquery.signalR.js @@ -349,7 +349,12 @@ if (data) { if (data.Messages) { $.each(data.Messages, function () { - $(connection).trigger("onReceived", [this]); + try { + $(connection).trigger("onReceived", [this]); + } + catch (e) { + log('Error raising received ' + e); + } }); } else { $(connection).trigger("onReceived", [data]); diff --git a/SignalR/Scripts/jquery.signalR.min.js b/SignalR/Scripts/jquery.signalR.min.js index a419af66b..83cb70211 100644 --- a/SignalR/Scripts/jquery.signalR.min.js +++ b/SignalR/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl,n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i){var u=n.url+"/connect",r="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(r="connectionData="+t.escape(n.data)+"&"+r),u+="?"+r},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&n.each(i.Messages,function(){try{n(t).trigger("onReceived",[this])}catch(i){u("Error raising received "+i)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,u){var f,o=!1,e;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){u();return}i.socket||(i.webSocketServerUrl?f=i.webSocketServerUrl:(e=document.location.protocol==="https:"?"wss://":"ws://",f=e+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),f+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(f),i.socket.onopen=function(){o=!0,r&&r()},i.socket.onclose=function(t){o?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):u&&u(),i.socket=null},i.socket.onmessage=function(r){var u=t.JSON.parse(r.data);u&&(u.Messages?n.each(u.Messages,function(){n(i).trigger("onReceived",[this])}):n(i).trigger("onReceived",[u]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var h=this,s=!1,o;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),o=i.getUrl(r,this.name),r.eventSource=new t.EventSource(o),r.eventSource.addEventListener("open",function(){s=!0,f()},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(t){s||e(),t.eventPhase==EventSource.CLOSED?(u("SignalR: EventSource closed"),h.stop()):(u("SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){t.setTimeout(function(){var t=n.frame,i=t.src.replace("/connect","")+"&messageId="+n.messageId+"&groups="+escape(n.groups);t.src=i},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}},longPolling:{name:"longPolling",start:function(r,u){r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var u=r.messageId,o=u===null,e=r.url+(o?"/connect":"")+"?transport=longPolling&connectionId="+escape(r.id)+"&messageId="+u+"&groups="+escape((r.groups||[]).toString());r.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl,n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i){var u=n.url+"/connect",r="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(r="connectionData="+t.escape(n.data)+"&"+r),u+="?"+r},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&n.each(i.Messages,function(){try{n(t).trigger("onReceived",[this])}catch(i){u("Error raising received "+i)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data);f&&(f.Messages?n.each(f.Messages,function(){try{n(i).trigger("onReceived",[this])}catch(t){u("Error raising received "+t)}}):n(i).trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var h=this,s=!1,o;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),o=i.getUrl(r,this.name),r.eventSource=new t.EventSource(o),r.eventSource.addEventListener("open",function(){s=!0,f()},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(t){s||e(),t.eventPhase==EventSource.CLOSED?(u("SignalR: EventSource closed"),h.stop()):(u("SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){t.setTimeout(function(){var t=n.frame,i=t.src.replace("/connect","")+"&messageId="+n.messageId+"&groups="+escape(n.groups);t.src=i},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}},longPolling:{name:"longPolling",start:function(r,u){r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var u=r.messageId,o=u===null,e=r.url+(o?"/connect":"")+"?transport=longPolling&connectionId="+escape(r.id)+"&messageId="+u+"&groups="+escape((r.groups||[]).toString());r.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file From 0af87587d73f4d9e3c435fb367aefa5e9289e36a Mon Sep 17 00:00:00 2001 From: David Fowler Date: Mon, 2 Jan 2012 12:33:42 -0800 Subject: [PATCH 0316/1128] Catch exception when the server is stopped. --- SignalR.SelfHost/Server.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/SignalR.SelfHost/Server.cs b/SignalR.SelfHost/Server.cs index 4801301e8..18cc43276 100644 --- a/SignalR.SelfHost/Server.cs +++ b/SignalR.SelfHost/Server.cs @@ -74,7 +74,16 @@ private void ReceiveLoop() { _listener.BeginGetContext(ar => { - HttpListenerContext context = _listener.EndGetContext(ar); + HttpListenerContext context; + try + { + context = _listener.EndGetContext(ar); + } + catch(Exception ex) + { + return; + } + ReceiveLoop(); // Process the request async From 0c6615374ef279a4314d35600162343a6f1f59e3 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Mon, 2 Jan 2012 12:34:37 -0800 Subject: [PATCH 0317/1128] Remove unused exception variable. --- SignalR.SelfHost/Server.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SignalR.SelfHost/Server.cs b/SignalR.SelfHost/Server.cs index 18cc43276..ab94b74da 100644 --- a/SignalR.SelfHost/Server.cs +++ b/SignalR.SelfHost/Server.cs @@ -79,7 +79,7 @@ private void ReceiveLoop() { context = _listener.EndGetContext(ar); } - catch(Exception ex) + catch(Exception) { return; } From 3be1351676e8223bc817e2ae452ab0d21614c6e1 Mon Sep 17 00:00:00 2001 From: Drew Marsh Date: Mon, 2 Jan 2012 12:49:19 -0800 Subject: [PATCH 0318/1128] Minor perf optimizations in SignalR jQuery lib --- SignalR.Samples/Scripts/jquery.signalR.js | 10 +++++++--- SignalR/Scripts/jquery.signalR.js | 10 +++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/SignalR.Samples/Scripts/jquery.signalR.js b/SignalR.Samples/Scripts/jquery.signalR.js index 365b4b4bb..c8001e037 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.js +++ b/SignalR.Samples/Scripts/jquery.signalR.js @@ -260,9 +260,11 @@ } if (data.Messages) { + var $connection = $(connection); + $.each(data.Messages, function () { try { - $(connection).trigger("onReceived", [this]); + $connection.trigger("onReceived", [this]); } catch (e) { log('Error raising received ' + e); @@ -347,17 +349,19 @@ connection.socket.onmessage = function (event) { var data = window.JSON.parse(event.data); if (data) { + var $connection = $(connection); + if (data.Messages) { $.each(data.Messages, function () { try { - $(connection).trigger("onReceived", [this]); + $connection.trigger("onReceived", [this]); } catch (e) { log('Error raising received ' + e); } }); } else { - $(connection).trigger("onReceived", [data]); + $connection.trigger("onReceived", [data]); } } }; diff --git a/SignalR/Scripts/jquery.signalR.js b/SignalR/Scripts/jquery.signalR.js index 365b4b4bb..58b034a19 100644 --- a/SignalR/Scripts/jquery.signalR.js +++ b/SignalR/Scripts/jquery.signalR.js @@ -260,9 +260,11 @@ } if (data.Messages) { + var $connection = $(connection); + $.each(data.Messages, function () { try { - $(connection).trigger("onReceived", [this]); + $connection.trigger("onReceived", [this]); } catch (e) { log('Error raising received ' + e); @@ -347,17 +349,19 @@ connection.socket.onmessage = function (event) { var data = window.JSON.parse(event.data); if (data) { + var $connection = $(connection); + if (data.Messages) { $.each(data.Messages, function () { try { - $(connection).trigger("onReceived", [this]); + $connection.trigger("onReceived", [this]); } catch (e) { log('Error raising received ' + e); } }); } else { - $(connection).trigger("onReceived", [data]); + $connection.trigger("onReceived", [data]); } } }; From aa1d213226dffcf372c008a4da2cc08d7845f853 Mon Sep 17 00:00:00 2001 From: Drew Marsh Date: Mon, 2 Jan 2012 12:54:01 -0800 Subject: [PATCH 0319/1128] Minor perf optimizations in SignalR jQuery lib (minified scripts) --- SignalR.Samples/Scripts/jquery.signalR.js | 2 +- SignalR.Samples/Scripts/jquery.signalR.min.js | 2 +- SignalR/Scripts/jquery.signalR.min.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SignalR.Samples/Scripts/jquery.signalR.js b/SignalR.Samples/Scripts/jquery.signalR.js index c8001e037..58b034a19 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.js +++ b/SignalR.Samples/Scripts/jquery.signalR.js @@ -261,7 +261,7 @@ if (data.Messages) { var $connection = $(connection); - + $.each(data.Messages, function () { try { $connection.trigger("onReceived", [this]); diff --git a/SignalR.Samples/Scripts/jquery.signalR.min.js b/SignalR.Samples/Scripts/jquery.signalR.min.js index 83cb70211..6bac405f1 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.min.js +++ b/SignalR.Samples/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl,n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i){var u=n.url+"/connect",r="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(r="connectionData="+t.escape(n.data)+"&"+r),u+="?"+r},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&n.each(i.Messages,function(){try{n(t).trigger("onReceived",[this])}catch(i){u("Error raising received "+i)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data);f&&(f.Messages?n.each(f.Messages,function(){try{n(i).trigger("onReceived",[this])}catch(t){u("Error raising received "+t)}}):n(i).trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var h=this,s=!1,o;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),o=i.getUrl(r,this.name),r.eventSource=new t.EventSource(o),r.eventSource.addEventListener("open",function(){s=!0,f()},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(t){s||e(),t.eventPhase==EventSource.CLOSED?(u("SignalR: EventSource closed"),h.stop()):(u("SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){t.setTimeout(function(){var t=n.frame,i=t.src.replace("/connect","")+"&messageId="+n.messageId+"&groups="+escape(n.groups);t.src=i},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}},longPolling:{name:"longPolling",start:function(r,u){r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var u=r.messageId,o=u===null,e=r.url+(o?"/connect":"")+"?transport=longPolling&connectionId="+escape(r.id)+"&messageId="+u+"&groups="+escape((r.groups||[]).toString());r.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl,n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i){var u=n.url+"/connect",r="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(r="connectionData="+t.escape(n.data)+"&"+r),u+="?"+r},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){if(i){if(i.Disconnect){t.stop().start();return}if(i.Messages){var r=n(t);n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}})}t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var h=this,s=!1,o;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),o=i.getUrl(r,this.name),r.eventSource=new t.EventSource(o),r.eventSource.addEventListener("open",function(){s=!0,f()},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(t){s||e(),t.eventPhase==EventSource.CLOSED?(u("SignalR: EventSource closed"),h.stop()):(u("SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){t.setTimeout(function(){var t=n.frame,i=t.src.replace("/connect","")+"&messageId="+n.messageId+"&groups="+escape(n.groups);t.src=i},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}},longPolling:{name:"longPolling",start:function(r,u){r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var u=r.messageId,o=u===null,e=r.url+(o?"/connect":"")+"?transport=longPolling&connectionId="+escape(r.id)+"&messageId="+u+"&groups="+escape((r.groups||[]).toString());r.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR/Scripts/jquery.signalR.min.js b/SignalR/Scripts/jquery.signalR.min.js index 83cb70211..6bac405f1 100644 --- a/SignalR/Scripts/jquery.signalR.min.js +++ b/SignalR/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl,n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i){var u=n.url+"/connect",r="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(r="connectionData="+t.escape(n.data)+"&"+r),u+="?"+r},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&n.each(i.Messages,function(){try{n(t).trigger("onReceived",[this])}catch(i){u("Error raising received "+i)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data);f&&(f.Messages?n.each(f.Messages,function(){try{n(i).trigger("onReceived",[this])}catch(t){u("Error raising received "+t)}}):n(i).trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var h=this,s=!1,o;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),o=i.getUrl(r,this.name),r.eventSource=new t.EventSource(o),r.eventSource.addEventListener("open",function(){s=!0,f()},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(t){s||e(),t.eventPhase==EventSource.CLOSED?(u("SignalR: EventSource closed"),h.stop()):(u("SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){t.setTimeout(function(){var t=n.frame,i=t.src.replace("/connect","")+"&messageId="+n.messageId+"&groups="+escape(n.groups);t.src=i},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}},longPolling:{name:"longPolling",start:function(r,u){r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var u=r.messageId,o=u===null,e=r.url+(o?"/connect":"")+"?transport=longPolling&connectionId="+escape(r.id)+"&messageId="+u+"&groups="+escape((r.groups||[]).toString());r.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl,n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i){var u=n.url+"/connect",r="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(r="connectionData="+t.escape(n.data)+"&"+r),u+="?"+r},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){if(i){if(i.Disconnect){t.stop().start();return}if(i.Messages){var r=n(t);n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}})}t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var h=this,s=!1,o;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),o=i.getUrl(r,this.name),r.eventSource=new t.EventSource(o),r.eventSource.addEventListener("open",function(){s=!0,f()},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(t){s||e(),t.eventPhase==EventSource.CLOSED?(u("SignalR: EventSource closed"),h.stop()):(u("SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){t.setTimeout(function(){var t=n.frame,i=t.src.replace("/connect","")+"&messageId="+n.messageId+"&groups="+escape(n.groups);t.src=i},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}},longPolling:{name:"longPolling",start:function(r,u){r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var u=r.messageId,o=u===null,e=r.url+(o?"/connect":"")+"?transport=longPolling&connectionId="+escape(r.id)+"&messageId="+u+"&groups="+escape((r.groups||[]).toString());r.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file From 999e05a4714ff531c20f659c34fbd8300414f525 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Tue, 3 Jan 2012 21:37:59 -0800 Subject: [PATCH 0320/1128] Added basic signalr client sample and fixed task helpers so that they bubbling exceptions properly. - Don't create unnecessary objects in the HttpHelper. - Update signalr.client to use then overloads. --- SignalR.Client.Samples/Program.cs | 48 +++++++ .../Properties/AssemblyInfo.cs | 36 ++++++ .../SignalR.Client.Samples.csproj | 66 ++++++++++ SignalR.Client/Connection.cs | 10 +- SignalR.Client/Hubs/HubProxy.cs | 16 +-- SignalR.Client/Transports/IClientTransport.cs | 2 +- .../Transports/LongPollingTransport.cs | 24 +++- SignalR.ScaleOut/HttpHelper.cs | 100 +++++++++++---- SignalR.SelfHost/SignalR.SelfHost.csproj | 3 + SignalR.sln | 58 +++++++++ SignalR/TaskAsyncHelper.cs | 121 +++++++++++++++--- 11 files changed, 419 insertions(+), 65 deletions(-) create mode 100644 SignalR.Client.Samples/Program.cs create mode 100644 SignalR.Client.Samples/Properties/AssemblyInfo.cs create mode 100644 SignalR.Client.Samples/SignalR.Client.Samples.csproj diff --git a/SignalR.Client.Samples/Program.cs b/SignalR.Client.Samples/Program.cs new file mode 100644 index 000000000..ef495da5b --- /dev/null +++ b/SignalR.Client.Samples/Program.cs @@ -0,0 +1,48 @@ +using System; +using SignalR.Client.Hubs; + +namespace SignalR.Client.Samples +{ + class Program + { + static void Main(string[] args) + { + RunStreamingSample(); + + var hubConnection = new HubConnection("http://localhost:40476/"); + + RunDemoHub(hubConnection); + + hubConnection.Start().Wait(); + + Console.ReadKey(); + } + + private static void RunDemoHub(HubConnection hubConnection) + { + var demo = hubConnection.CreateProxy("SignalR.Samples.Hubs.DemoHub.DemoHub"); + + demo.On("fromArbitraryCode", value => + { + Console.WriteLine("Sending {0} from arbitrary code without the hub itself!", value); + }); + } + + private static void RunStreamingSample() + { + var connection = new Connection("http://localhost:40476/Streaming/streaming"); + + connection.Received += data => + { + Console.WriteLine(data); + }; + + connection.Error += e => + { + Console.WriteLine(e); + }; + + connection.Start().Wait(); + } + } +} diff --git a/SignalR.Client.Samples/Properties/AssemblyInfo.cs b/SignalR.Client.Samples/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..3fe74bf2c --- /dev/null +++ b/SignalR.Client.Samples/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SignalR.Client.Samples")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("SignalR.Client.Samples")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2012")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("750854c7-3c4c-46e0-b46b-a42773f33ae4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SignalR.Client.Samples/SignalR.Client.Samples.csproj b/SignalR.Client.Samples/SignalR.Client.Samples.csproj new file mode 100644 index 000000000..65f53db73 --- /dev/null +++ b/SignalR.Client.Samples/SignalR.Client.Samples.csproj @@ -0,0 +1,66 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {E0223FDC-0982-4D80-B6C2-BFAA6C6748C5} + Exe + Properties + SignalR.Client.Samples + SignalR.Client.Samples + v4.0 + Client + 512 + ..\..\SignalR\ + true + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + {EB46B9C6-EE37-48F9-835E-E49580E40E0A} + SignalR.Client + + + + + + \ No newline at end of file diff --git a/SignalR.Client/Connection.cs b/SignalR.Client/Connection.cs index 222536724..862642c5f 100644 --- a/SignalR.Client/Connection.cs +++ b/SignalR.Client/Connection.cs @@ -63,9 +63,9 @@ public virtual Task Start() string negotiateUrl = Url + "negotiate"; - return HttpHelper.PostAsync(negotiateUrl, PrepareRequest).Then(task => + return HttpHelper.PostAsync(negotiateUrl, PrepareRequest).Then(response => { - string raw = task.Result.ReadAsString(); + string raw = response.ReadAsString(); if (raw == null) { @@ -76,8 +76,10 @@ public virtual Task Start() ConnectionId = negotiationResponse.ConnectionId; - _transport.Start(this, data); - }); + return _transport.Start(this, data); + + }) + .FastUnwrap(); } public virtual void Stop() diff --git a/SignalR.Client/Hubs/HubProxy.cs b/SignalR.Client/Hubs/HubProxy.cs index f9685fa48..6ed115cdb 100644 --- a/SignalR.Client/Hubs/HubProxy.cs +++ b/SignalR.Client/Hubs/HubProxy.cs @@ -78,26 +78,24 @@ public Task Invoke(string method, params object[] args) var value = JsonConvert.SerializeObject(hubData); - return _connection.Send>(value).Then(task => + return _connection.Send>(value).Then(result => { - if (task.Result != null) + if (result != null) { - if (task.Result.Error != null) + if (result.Error != null) { - throw new InvalidOperationException(task.Result.Error); + throw new InvalidOperationException(result.Error); } - HubResult hubResult = task.Result; - - if (hubResult.State != null) + if (result.State != null) { - foreach (var pair in hubResult.State) + foreach (var pair in result.State) { this[pair.Key] = pair.Value; } } - return hubResult.Result; + return result.Result; } return default(T); }); diff --git a/SignalR.Client/Transports/IClientTransport.cs b/SignalR.Client/Transports/IClientTransport.cs index 9cd35d487..699e31a54 100644 --- a/SignalR.Client/Transports/IClientTransport.cs +++ b/SignalR.Client/Transports/IClientTransport.cs @@ -4,7 +4,7 @@ namespace SignalR.Client.Transports { public interface IClientTransport { - void Start(Connection connection, string data); + Task Start(Connection connection, string data); Task Send(Connection connection, string data); void Stop(Connection connection); } diff --git a/SignalR.Client/Transports/LongPollingTransport.cs b/SignalR.Client/Transports/LongPollingTransport.cs index c2bf5d684..aafe5d745 100644 --- a/SignalR.Client/Transports/LongPollingTransport.cs +++ b/SignalR.Client/Transports/LongPollingTransport.cs @@ -18,7 +18,16 @@ public class LongPollingTransport : IClientTransport private const string _receiveQs = "?transport=longPolling&connectionId={0}&messageId={1}&groups={2}&connectionData={3}"; private const string _sendQs = "?transport=longPolling&connectionId={0}"; - public void Start(Connection connection, string data) + public Task Start(Connection connection, string data) + { + var tcs = new TaskCompletionSource(); + + PollingLoop(tcs, connection, data); + + return tcs.Task; + } + + private void PollingLoop(TaskCompletionSource tcs, Connection connection, string data) { string url = connection.Url; @@ -97,10 +106,17 @@ public void Start(Connection connection, string data) // Only continue if the connection is still active and wasn't aborted if (!requestAborted && connection.IsActive) { - Start(connection, data); + PollingLoop(null, connection, data); } } }); + + if (tcs != null) + { + // Only set this the first time + // TODO: We should delay this until after the http request has been made + tcs.SetResult(null); + } } private static bool IsRequestAborted(Exception exception) @@ -119,9 +135,9 @@ public Task Send(Connection connection, string data) { "data", data } }; - return HttpHelper.PostAsync(url, connection.PrepareRequest, postData).Then(task => + return HttpHelper.PostAsync(url, connection.PrepareRequest, postData).Then(response => { - string raw = task.Result.ReadAsString(); + string raw = response.ReadAsString(); if (String.IsNullOrEmpty(raw)) { diff --git a/SignalR.ScaleOut/HttpHelper.cs b/SignalR.ScaleOut/HttpHelper.cs index 8606a9f97..443025c26 100644 --- a/SignalR.ScaleOut/HttpHelper.cs +++ b/SignalR.ScaleOut/HttpHelper.cs @@ -12,39 +12,56 @@ internal static class HttpHelper { public static Task GetResponseAsync(this HttpWebRequest request) { - return Task.Factory.FromAsync(request.BeginGetResponse, iar => (HttpWebResponse)request.EndGetResponse(iar), null); + try + { + return Task.Factory.FromAsync(request.BeginGetResponse, ar => (HttpWebResponse)request.EndGetResponse(ar), null); + } + catch (Exception ex) + { + return TaskAsyncHelper.FromError(ex); + } } public static Task GetRequestStreamAsync(this HttpWebRequest request) { - return Task.Factory.FromAsync(request.BeginGetRequestStream, request.EndGetRequestStream, null); + try + { + return Task.Factory.FromAsync(request.BeginGetRequestStream, request.EndGetRequestStream, null); + } + catch (Exception ex) + { + return TaskAsyncHelper.FromError(ex); + } } public static Task GetAsync(string url) { - return GetAsync(url, _ => { }); + return GetAsync(url, requestPreparer: null); } public static Task GetAsync(string url, Action requestPreparer) { var request = (HttpWebRequest)HttpWebRequest.Create(url); - requestPreparer(request); + if (requestPreparer != null) + { + requestPreparer(request); + } return request.GetResponseAsync(); } public static Task PostAsync(string url) { - return PostInternal(url, _ => { }, new Dictionary()); + return PostInternal(url, requestPreparer: null, postData: null); } public static Task PostAsync(string url, IDictionary postData) { - return PostInternal(url, _ => { }, postData); + return PostInternal(url, requestPreparer: null, postData: postData); } public static Task PostAsync(string url, Action requestPreparer) { - return PostInternal(url, requestPreparer, new Dictionary()); + return PostInternal(url, requestPreparer, postData: null); } public static Task PostAsync(string url, Action requestPreparer, IDictionary postData) @@ -67,7 +84,7 @@ public static string ReadAsString(this HttpWebResponse response) } } } - catch(Exception ex) + catch (Exception ex) { Debug.WriteLine("Failed to read resonse: {0}", ex); // Swallow exceptions when reading the response stream and just try again. @@ -78,8 +95,41 @@ public static string ReadAsString(this HttpWebResponse response) private static Task PostInternal(string url, Action requestPreparer, IDictionary postData) { var request = (HttpWebRequest)HttpWebRequest.Create(url); - - requestPreparer(request); + + if (requestPreparer != null) + { + requestPreparer(request); + } + + byte[] buffer = ProcessPostData(postData); + + request.Method = "POST"; + request.ContentType = "application/x-www-form-urlencoded"; +#if !WINDOWS_PHONE && !SILVERLIGHT + // Set the content length if the buffer is non-null + request.ContentLength = buffer != null ? buffer.LongLength : 0; +#endif + + if (buffer == null) + { + // If there's nothing to be written to the request then just get the response + return request.GetResponseAsync(); + } + + // Write the post data to the request stream + return request.GetRequestStreamAsync() + .Then(stream => stream.WriteAsync(buffer).Then(() => stream.Close())) + .FastUnwrap() + .Then(() => request.GetResponseAsync()) + .FastUnwrap(); + } + + private static byte[] ProcessPostData(IDictionary postData) + { + if (postData == null || postData.Count == 0) + { + return null; + } var sb = new StringBuilder(); foreach (var pair in postData) @@ -97,25 +147,19 @@ private static Task PostInternal(string url, Action - { - t.Result.Write(buffer, 0, buffer.Length); - t.Result.Close(); - }) - .Then(t => - { - return request.GetResponseAsync(); - }) - .Unwrap(); + private static Task WriteAsync(this Stream stream, byte[] buffer) + { + try + { + return Task.Factory.FromAsync((cb, state) => stream.BeginWrite(buffer, 0, buffer.Length, cb, state), ar => stream.EndWrite(ar), null); + } + catch (Exception ex) + { + return TaskAsyncHelper.FromError(ex); + } } } } \ No newline at end of file diff --git a/SignalR.SelfHost/SignalR.SelfHost.csproj b/SignalR.SelfHost/SignalR.SelfHost.csproj index 69d88d2d4..54d60b8bf 100644 --- a/SignalR.SelfHost/SignalR.SelfHost.csproj +++ b/SignalR.SelfHost/SignalR.SelfHost.csproj @@ -12,6 +12,8 @@ SignalR.SelfHost v4.0 512 + ..\..\SignalR\ + true true @@ -59,6 +61,7 @@ + + \ No newline at end of file diff --git a/SignalR.SelfHost.Samples/app.config b/SignalR.SelfHost.Samples/app.config new file mode 100644 index 000000000..e36560333 --- /dev/null +++ b/SignalR.SelfHost.Samples/app.config @@ -0,0 +1,3 @@ + + + diff --git a/SignalR.sln b/SignalR.sln index c0c3273c8..2ceedab39 100644 --- a/SignalR.sln +++ b/SignalR.sln @@ -23,6 +23,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.SelfHost", "SignalR EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.Client.Samples", "SignalR.Client.Samples\SignalR.Client.Samples.csproj", "{E0223FDC-0982-4D80-B6C2-BFAA6C6748C5}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.SelfHost.Samples", "SignalR.SelfHost.Samples\SignalR.SelfHost.Samples.csproj", "{8FD9E25A-2268-4F6E-85F3-B83DC018ECE9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -113,6 +115,16 @@ Global {E0223FDC-0982-4D80-B6C2-BFAA6C6748C5}.Release|Mixed Platforms.Build.0 = Release|x86 {E0223FDC-0982-4D80-B6C2-BFAA6C6748C5}.Release|x86.ActiveCfg = Release|x86 {E0223FDC-0982-4D80-B6C2-BFAA6C6748C5}.Release|x86.Build.0 = Release|x86 + {8FD9E25A-2268-4F6E-85F3-B83DC018ECE9}.Debug|Any CPU.ActiveCfg = Debug|x86 + {8FD9E25A-2268-4F6E-85F3-B83DC018ECE9}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {8FD9E25A-2268-4F6E-85F3-B83DC018ECE9}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {8FD9E25A-2268-4F6E-85F3-B83DC018ECE9}.Debug|x86.ActiveCfg = Debug|x86 + {8FD9E25A-2268-4F6E-85F3-B83DC018ECE9}.Debug|x86.Build.0 = Debug|x86 + {8FD9E25A-2268-4F6E-85F3-B83DC018ECE9}.Release|Any CPU.ActiveCfg = Release|x86 + {8FD9E25A-2268-4F6E-85F3-B83DC018ECE9}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {8FD9E25A-2268-4F6E-85F3-B83DC018ECE9}.Release|Mixed Platforms.Build.0 = Release|x86 + {8FD9E25A-2268-4F6E-85F3-B83DC018ECE9}.Release|x86.ActiveCfg = Release|x86 + {8FD9E25A-2268-4F6E-85F3-B83DC018ECE9}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 51b207cb185a94cd97d4661872c763e2e2f94d83 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sat, 7 Jan 2012 17:55:41 -0800 Subject: [PATCH 0337/1128] Stop processing the buffer only when the line is null, not empty. --- SignalR.Client/Transports/ServerSentEventsTransport.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SignalR.Client/Transports/ServerSentEventsTransport.cs b/SignalR.Client/Transports/ServerSentEventsTransport.cs index 941392061..84433bd6a 100644 --- a/SignalR.Client/Transports/ServerSentEventsTransport.cs +++ b/SignalR.Client/Transports/ServerSentEventsTransport.cs @@ -185,8 +185,8 @@ private void ProcessChunks() { string line = _buffer.ReadLine(); - // Stop when we read an empty line - if (String.IsNullOrEmpty(line)) + // No new lines in the buffer so stop processing + if (line == null) { break; } From cfb05fc1498c2517a64df279450ac510ab5cfb97 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sat, 7 Jan 2012 21:46:30 -0800 Subject: [PATCH 0338/1128] Make SignalR work on cassini. Fallback to reading the query string and form values from the request if the validation helper fails. Guard Initialize() from being called multiple times. --- SignalR.AspNet/AspNetRequest.cs | 52 ++++++++++++++++++++++---------- SignalR.AspNet/AspNetResponse.cs | 16 +++++++--- SignalR.AspNet/Bootstrapper.cs | 45 +++++++++++++++++++++------ 3 files changed, 84 insertions(+), 29 deletions(-) diff --git a/SignalR.AspNet/AspNetRequest.cs b/SignalR.AspNet/AspNetRequest.cs index b8c1347e0..07e0b5626 100644 --- a/SignalR.AspNet/AspNetRequest.cs +++ b/SignalR.AspNet/AspNetRequest.cs @@ -9,8 +9,8 @@ namespace SignalR.AspNet public class AspNetRequest : IRequest { private readonly HttpRequestBase _request; - private readonly NameValueCollection _form; - private readonly NameValueCollection _queryString; + private NameValueCollection _form; + private NameValueCollection _queryString; public AspNetRequest(HttpRequestBase request) { @@ -21,20 +21,7 @@ public AspNetRequest(HttpRequestBase request) Cookies.Add(key, request.Cookies[key].Value); } - // Since the ValidationUtility has a dependency on HttpContext (not HttpContextBase) we - // need to check if we're out of HttpContext to preserve testability. - if (HttpContext.Current == null) - { - _form = _request.Form; - _queryString = _request.QueryString; - } - else - { - Func formGetter, queryGetter; - ValidationUtility.GetUnvalidatedCollections(HttpContext.Current, out formGetter, out queryGetter); - _form = formGetter(); - _queryString = queryGetter(); - } + ResolveFormAndQueryString(); } public Uri Url @@ -74,5 +61,38 @@ public NameValueCollection Cookies get; private set; } + + private void ResolveFormAndQueryString() + { + // Since the ValidationUtility has a dependency on HttpContext (not HttpContextBase) we + // need to check if we're out of HttpContext to preserve testability. + if (HttpContext.Current == null) + { + _form = _request.Form; + _queryString = _request.QueryString; + } + else + { + try + { + ResolveUnvalidatedCollections(); + } + catch + { + // TODO: Cache this + // Fallback to grabbing values from the request + _form = _request.Form; + _queryString = _request.QueryString; + } + } + } + + private void ResolveUnvalidatedCollections() + { + Func formGetter, queryGetter; + ValidationUtility.GetUnvalidatedCollections(HttpContext.Current, out formGetter, out queryGetter); + _form = formGetter(); + _queryString = queryGetter(); + } } } diff --git a/SignalR.AspNet/AspNetResponse.cs b/SignalR.AspNet/AspNetResponse.cs index 4aa57e5ca..282734e89 100644 --- a/SignalR.AspNet/AspNetResponse.cs +++ b/SignalR.AspNet/AspNetResponse.cs @@ -1,4 +1,5 @@ -using System.Linq.Expressions; +using System; +using System.Linq.Expressions; using System.Reflection; using System.Threading.Tasks; using System.Web; @@ -11,7 +12,7 @@ public class AspNetResponse : IResponse private delegate void RemoveHeaderDel(HttpWorkerRequest workerRequest); private const string IIS7WorkerRequestTypeName = "System.Web.Hosting.IIS7WorkerRequest"; - private static readonly RemoveHeaderDel IIS7RemoveHeader = GetRemoveHeaderDelegate(); + private static readonly RemoveHeaderDel IIS7RemoveHeader = GetRemoveHeaderDelegate(); private readonly HttpContextBase _context; @@ -80,7 +81,14 @@ private void RemoveAcceptEncoding() } else { - _context.Request.Headers.Remove("Accept-Encoding"); + try + { + _context.Request.Headers.Remove("Accept-Encoding"); + } + catch (PlatformNotSupportedException) + { + // Happens on cassini + } } } @@ -88,7 +96,7 @@ private static bool IsIIS7WorkerRequest(HttpWorkerRequest workerRequest) { return workerRequest.GetType().FullName == IIS7WorkerRequestTypeName; } - + private static RemoveHeaderDel GetRemoveHeaderDelegate() { var iis7wrType = typeof(HttpContext).Assembly.GetType(IIS7WorkerRequestTypeName); diff --git a/SignalR.AspNet/Bootstrapper.cs b/SignalR.AspNet/Bootstrapper.cs index c846ffff5..1eec0038e 100644 --- a/SignalR.AspNet/Bootstrapper.cs +++ b/SignalR.AspNet/Bootstrapper.cs @@ -3,24 +3,51 @@ using SignalR.AspNet; using SignalR.Hubs; using SignalR.Infrastructure; -using SignalR.Transports; -[assembly: PreApplicationStartMethod(typeof(Bootstrapper), "Start")] +[assembly: PreApplicationStartMethod(typeof(Bootstrapper), "Initialize")] namespace SignalR.AspNet { public static class Bootstrapper { - public static void Start() + private static bool _initialized; + private static object _lockObject = new object(); + + public static void Initialize() { - DynamicModuleUtility.RegisterModule(typeof(HubModule)); + // Ensure this is only called once + if (!_initialized) + { + lock (_lockObject) + { + if (!_initialized) + { + RegisterHubModule(); + + // Replace defaults with asp.net implementations + var typeResolver = new BuildManagerTypeResolver(); + var hubLocator = new BuildManagerTypeLocator(); - // Replace defaults with asp.net implementations - var typeResolver = new BuildManagerTypeResolver(); - var hubLocator = new BuildManagerTypeLocator(); + DependencyResolver.Register(typeof(IHubTypeResolver), () => typeResolver); + DependencyResolver.Register(typeof(IHubLocator), () => hubLocator); - DependencyResolver.Register(typeof(IHubTypeResolver), () => typeResolver); - DependencyResolver.Register(typeof(IHubLocator), () => hubLocator); + _initialized = true; + } + } + } + } + + private static void RegisterHubModule() + { + try + { + DynamicModuleUtility.RegisterModule(typeof(HubModule)); + } + catch + { + // If we're unable to load MWI then just swallow the exception and don't allow + // the automagic hub registration + } } } } \ No newline at end of file From 8360abc2143c03317268063730bf6492f05e2dc5 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sun, 8 Jan 2012 19:51:33 -0800 Subject: [PATCH 0339/1128] Cleaned up ITransport interface. - Removed Func return. - Events return Task instead of being void. - Make disconnect async. --- SignalR/PersistentConnection.cs | 35 +++------ SignalR/TaskAsyncHelper.cs | 19 +++++ SignalR/Transports/ForeverTransport.cs | 58 ++++++++------- SignalR/Transports/ITrackingDisconnect.cs | 4 +- SignalR/Transports/ITransport.cs | 13 ++-- SignalR/Transports/LongPollingTransport.cs | 86 ++++++++++++---------- SignalR/Transports/TransportHeartBeat.cs | 16 +++- 7 files changed, 129 insertions(+), 102 deletions(-) diff --git a/SignalR/PersistentConnection.cs b/SignalR/PersistentConnection.cs index 9a879e782..20108196d 100644 --- a/SignalR/PersistentConnection.cs +++ b/SignalR/PersistentConnection.cs @@ -61,8 +61,6 @@ private string DefaultSignal public virtual Task ProcessRequestAsync(HostContext context) { - Task transportEventTask = null; - if (IsNegotiationRequest(context.Request)) { return ProcessNegotiationRequest(context); @@ -86,40 +84,25 @@ public virtual Task ProcessRequestAsync(HostContext context) IEnumerable groups = _transport.Groups; Connection = CreateConnection(connectionId, groups, context.Request); - - // Wire up the events we need - _transport.Connected += () => + + _transport.Connected = () => { - transportEventTask = OnConnectedAsync(context.Request, connectionId); + return OnConnectedAsync(context.Request, connectionId); }; - _transport.Received += (data) => + _transport.Received = data => { - transportEventTask = OnReceivedAsync(connectionId, data); + return OnReceivedAsync(connectionId, data); }; - _transport.Error += (e) => - { - transportEventTask = OnErrorAsync(e); - }; + _transport.Error = OnErrorAsync; - _transport.Disconnected += () => + _transport.Disconnected = () => { - transportEventTask = OnDisconnectAsync(connectionId); + return OnDisconnectAsync(connectionId); }; - Func transportProcessRequest = _transport.ProcessRequest(Connection); - - if (transportProcessRequest != null) - { - if (transportEventTask != null) - { - return transportEventTask.Then(transportProcessRequest).FastUnwrap(); - } - return transportProcessRequest(); - } - - return transportEventTask ?? TaskAsyncHelper.Empty; + return _transport.ProcessRequest(Connection) ?? TaskAsyncHelper.Empty; } protected virtual IConnection CreateConnection(string connectionId, IEnumerable groups, IRequest request) diff --git a/SignalR/TaskAsyncHelper.cs b/SignalR/TaskAsyncHelper.cs index 3baa52239..69ad216ff 100644 --- a/SignalR/TaskAsyncHelper.cs +++ b/SignalR/TaskAsyncHelper.cs @@ -97,6 +97,25 @@ public static Task Then(this Task task, Action> #endregion + public static void ContinueWith(this Task task, TaskCompletionSource tcs) + { + task.ContinueWith(t => + { + if (t.IsFaulted) + { + tcs.SetException(t.Exception); + } + else if (t.IsCanceled) + { + tcs.SetCanceled(); + } + else + { + tcs.SetResult(null); + } + }); + } + // Then extesions public static Task Then(this Task task, Action successor) { diff --git a/SignalR/Transports/ForeverTransport.cs b/SignalR/Transports/ForeverTransport.cs index d3d1198d0..4eb93ea10 100644 --- a/SignalR/Transports/ForeverTransport.cs +++ b/SignalR/Transports/ForeverTransport.cs @@ -89,31 +89,29 @@ protected virtual void OnSending(string payload) public static event Action Sending; public static event Action Receiving; - public event Action Received; + public Func Received { get; set; } - public event Action Connected; + public Func Connected { get; set; } - public event Action Disconnected; + public Func Disconnected { get; set; } - public event Action Error; + public Func Error { get; set; } - public Func ProcessRequest(IReceivingConnection connection) + public Task ProcessRequest(IReceivingConnection connection) { _connection = connection; if (_context.Request.Url.LocalPath.EndsWith("/send")) { - ProcessSendRequest(); + return ProcessSendRequest(); } else { if (IsConnectRequest && Connected != null) { - Connected(); + return Connected().Then(() => ProcessReceiveRequest(connection)).FastUnwrap(); } - _heartBeat.AddConnection(this); - return ProcessReceiveRequest(connection); } @@ -133,19 +131,27 @@ public virtual Task Send(object value) return _context.Response.WriteAsync(data); } - public void Disconnect() + public Task Disconnect() { if (!_disconnected && Disconnected != null) { - Disconnected(); + return Disconnected().Then(() => SendDisconnectCommand()).FastUnwrap(); } + + return SendDisconnectCommand(); + } + + private Task SendDisconnectCommand() + { _disconnected = true; - _connection.SendCommand( - new SignalCommand - { - Type = CommandType.Disconnect, - ExpiresAfter = TimeSpan.FromMinutes(30) - }); + + var command = new SignalCommand + { + Type = CommandType.Disconnect, + ExpiresAfter = TimeSpan.FromMinutes(30) + }; + + return _connection.SendCommand(command); } protected virtual bool IsConnectRequest @@ -163,9 +169,10 @@ protected virtual Task InitializeResponse(IReceivingConnection connection) return TaskAsyncHelper.Empty; } - private void ProcessSendRequest() + private Task ProcessSendRequest() { string data = _context.Request.Form["data"]; + if (Receiving != null) { Receiving(data); @@ -173,13 +180,17 @@ private void ProcessSendRequest() if (Received != null) { - Received(data); + return Received(data); } + + return TaskAsyncHelper.Empty; } - private Func ProcessReceiveRequest(IReceivingConnection connection) + private Task ProcessReceiveRequest(IReceivingConnection connection) { - return () => InitializeResponse(connection) + _heartBeat.AddConnection(this); + + return InitializeResponse(connection) .Then((c, id) => ProcessMessages(c, id), connection, LastMessageId) .FastUnwrap(); } @@ -234,10 +245,7 @@ private void ProcessMessagesImpl(TaskCompletionSource taskCompletetionSo } // Client is no longer connected - Disconnect(); - - // We're done - taskCompletetionSource.SetResult(null); + Disconnect().ContinueWith(taskCompletetionSource); return; } } diff --git a/SignalR/Transports/ITrackingDisconnect.cs b/SignalR/Transports/ITrackingDisconnect.cs index 83032672b..a715e19f1 100644 --- a/SignalR/Transports/ITrackingDisconnect.cs +++ b/SignalR/Transports/ITrackingDisconnect.cs @@ -1,4 +1,6 @@ using System; +using System.Threading.Tasks; + namespace SignalR.Transports { public interface ITrackingDisconnect @@ -6,6 +8,6 @@ public interface ITrackingDisconnect string ConnectionId { get; } bool IsAlive { get; } TimeSpan DisconnectThreshold { get; } - void Disconnect(); + Task Disconnect(); } } \ No newline at end of file diff --git a/SignalR/Transports/ITransport.cs b/SignalR/Transports/ITransport.cs index 4a6da983b..00c93dd7c 100644 --- a/SignalR/Transports/ITransport.cs +++ b/SignalR/Transports/ITransport.cs @@ -6,13 +6,14 @@ namespace SignalR.Transports { public interface ITransport { - event Action Received; - event Action Connected; - event Action Disconnected; - event Action Error; - Func ProcessRequest(IReceivingConnection connection); + Func Received { get; set; } + Func Connected { get; set; } + Func Disconnected { get; set; } + Func Error { get; set; } + string ConnectionId { get; } IEnumerable Groups { get; } + + Task ProcessRequest(IReceivingConnection connection); Task Send(object value); - string ConnectionId { get; } } } diff --git a/SignalR/Transports/LongPollingTransport.cs b/SignalR/Transports/LongPollingTransport.cs index 4d1d6c9f3..5d2d2e78a 100644 --- a/SignalR/Transports/LongPollingTransport.cs +++ b/SignalR/Transports/LongPollingTransport.cs @@ -106,21 +106,21 @@ public bool IsAlive } } - public event Action Received; + public Func Received { get; set; } - public event Action Connected; + public Func Connected { get; set; } - public event Action Disconnected; + public Func Disconnected { get; set; } - public event Action Error; + public Func Error { get; set; } - public Func ProcessRequest(IReceivingConnection connection) + public Task ProcessRequest(IReceivingConnection connection) { _connection = connection; if (IsSendRequest) { - ProcessSendRequest(); + return ProcessSendRequest(); } else { @@ -152,70 +152,76 @@ public virtual Task Send(object value) { Sending(payload); } + _context.Response.ContentType = Json.MimeType; return _context.Response.WriteAsync(payload); } - public virtual void Disconnect() + public virtual Task Disconnect() { if (!_disconnected && Disconnected != null) { - Disconnected(); + return Disconnected().Then(() => SendDisconnectCommand()).FastUnwrap(); } + return SendDisconnectCommand(); + } + + private Task SendDisconnectCommand() + { _disconnected = true; - + + var command = new SignalCommand + { + Type = CommandType.Disconnect, + ExpiresAfter = TimeSpan.FromMinutes(30) + }; + // Force connection to close by sending a command signal - _connection.SendCommand( - new SignalCommand - { - Type = CommandType.Disconnect, - ExpiresAfter = TimeSpan.FromMinutes(30) - }); + return _connection.SendCommand(command); } - private void ProcessSendRequest() + private Task ProcessSendRequest() { - if (Received != null || Receiving != null) + string data = _context.Request.Form["data"]; + + if (Receiving != null) { - string data = _context.Request.Form["data"]; - if (Receiving != null) - { - Receiving(data); - } - if (Received != null) - { - Received(data); - } + Receiving(data); + } + + if (Received != null) + { + return Received(data); } + + return TaskAsyncHelper.Empty; } - private Func ProcessConnectRequest(IReceivingConnection connection) + private Task ProcessConnectRequest(IReceivingConnection connection) { // Since this is the first request, there's no data we need to retrieve so just wait // on a message to come through _heartBeat.AddConnection(this); + if (Connected != null) { - Connected(); + return Connected().Then(() => ProcessReceiveRequest(connection)).FastUnwrap(); } - // ReceiveAsync() will async wait until a message arrives then return - return () => connection.ReceiveAsync() - .Then(new Func(response => Send(response))) - .FastUnwrap(); + return ProcessReceiveRequest(connection); } - private Func ProcessReceiveRequest(IReceivingConnection connection) + private Task ProcessReceiveRequest(IReceivingConnection connection) { - _heartBeat.AddConnection(this); - // If there is a message id then we receive with that id, which will either return - // immediately if there are already messages since that id, or wait until new - // messages come in and then return - return () => connection.ReceiveAsync(MessageId.Value) - .Then(new Func(response => Send(response))) - .FastUnwrap(); + // ReceiveAsync() will async wait until a message arrives then return + var receiveTask = IsConnectRequest ? + connection.ReceiveAsync() : + connection.ReceiveAsync(MessageId.Value); + + return receiveTask.Then(new Func(response => Send(response))) + .FastUnwrap(); } private PersistentResponse AddTransportData(PersistentResponse response) diff --git a/SignalR/Transports/TransportHeartBeat.cs b/SignalR/Transports/TransportHeartBeat.cs index 77a2527a6..76076cb7f 100644 --- a/SignalR/Transports/TransportHeartBeat.cs +++ b/SignalR/Transports/TransportHeartBeat.cs @@ -114,12 +114,20 @@ private void Beat(object state) try { - connection.Disconnect(); + connection.Disconnect().ContinueWith(task => + { + if (task.IsFaulted) + { + Trace.TraceError("SignalR exception thrown by Task: {0}", task.Exception); + } + + // Remove the connection from the list + RemoveConnection(connection); + }); } - finally + catch { - // Remove the connection from the list - RemoveConnection(connection); + // Swallow exceptions that might happen during disconnect } } } From 5de255e2bcdee1729c6e218bd38de4703c606cda Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sun, 8 Jan 2012 21:14:05 -0800 Subject: [PATCH 0340/1128] Remove unused using. --- SignalR/Hubs/DefaultHubTypeResolver.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/SignalR/Hubs/DefaultHubTypeResolver.cs b/SignalR/Hubs/DefaultHubTypeResolver.cs index e826b5d6f..1c09dd3a1 100644 --- a/SignalR/Hubs/DefaultHubTypeResolver.cs +++ b/SignalR/Hubs/DefaultHubTypeResolver.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Web.Compilation; using SignalR.Infrastructure; namespace SignalR.Hubs From e93c0bc0096ec45c9c1a36ad6148b815b4be1d32 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Mon, 9 Jan 2012 16:56:58 -0800 Subject: [PATCH 0341/1128] Don't re-raise onSuccess on sse reconnect. --- SignalR.Samples/Scripts/jquery.signalR.js | 9 +++++---- SignalR.Samples/Scripts/jquery.signalR.min.js | 2 +- SignalR/Scripts/jquery.signalR.js | 9 +++++---- SignalR/Scripts/jquery.signalR.min.js | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/SignalR.Samples/Scripts/jquery.signalR.js b/SignalR.Samples/Scripts/jquery.signalR.js index 0fd761be3..c32ecae6e 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.js +++ b/SignalR.Samples/Scripts/jquery.signalR.js @@ -256,7 +256,7 @@ processMessages: function (connection, data) { var $connection; - + if (data) { if (data.Disconnect) { // Disconnected by the server, need to reconnect @@ -407,9 +407,10 @@ connection.eventSource = new window.EventSource(url); connection.eventSource.addEventListener("open", function (e) { - // opened - opened = true; - onSuccess(); + if (opened === false) { + opened = true; + onSuccess(); + } }, false); connection.eventSource.addEventListener("message", function (e) { diff --git a/SignalR.Samples/Scripts/jquery.signalR.min.js b/SignalR.Samples/Scripts/jquery.signalR.min.js index f9c0cb860..9d9bd608a 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.min.js +++ b/SignalR.Samples/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i){var u=n.url+"/connect",r="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(r="connectionData="+t.escape(n.data)+"&"+r),u+="?"+r},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r;if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&(r=n(t),n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}})),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var h=this,s=!1,o;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),o=i.getUrl(r,this.name),r.eventSource=new t.EventSource(o),r.eventSource.addEventListener("open",function(){s=!0,f()},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(t){s||e(),t.eventPhase==EventSource.CLOSED?(u("SignalR: EventSource closed"),h.stop()):(u("SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){t.setTimeout(function(){var t=n.frame,i=t.src.replace("/connect","")+"&messageId="+n.messageId+"&groups="+escape(n.groups);t.src=i},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}},longPolling:{name:"longPolling",start:function(r,u){r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var u=r.messageId,o=u===null,e=r.url+(o?"/connect":"")+"?transport=longPolling&connectionId="+escape(r.id)+"&messageId="+u+"&groups="+escape((r.groups||[]).toString());r.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i){var u=n.url+"/connect",r="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(r="connectionData="+t.escape(n.data)+"&"+r),u+="?"+r},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r;if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&(r=n(t),n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}})),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var h=this,o=!1,s;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),s=i.getUrl(r,this.name),r.eventSource=new t.EventSource(s),r.eventSource.addEventListener("open",function(){o===!1&&(o=!0,f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(t){o||e(),t.eventPhase==EventSource.CLOSED?(u("SignalR: EventSource closed"),h.stop()):(u("SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){t.setTimeout(function(){var t=n.frame,i=t.src.replace("/connect","")+"&messageId="+n.messageId+"&groups="+escape(n.groups);t.src=i},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}},longPolling:{name:"longPolling",start:function(r,u){r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var u=r.messageId,o=u===null,e=r.url+(o?"/connect":"")+"?transport=longPolling&connectionId="+escape(r.id)+"&messageId="+u+"&groups="+escape((r.groups||[]).toString());r.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR/Scripts/jquery.signalR.js b/SignalR/Scripts/jquery.signalR.js index 0fd761be3..c32ecae6e 100644 --- a/SignalR/Scripts/jquery.signalR.js +++ b/SignalR/Scripts/jquery.signalR.js @@ -256,7 +256,7 @@ processMessages: function (connection, data) { var $connection; - + if (data) { if (data.Disconnect) { // Disconnected by the server, need to reconnect @@ -407,9 +407,10 @@ connection.eventSource = new window.EventSource(url); connection.eventSource.addEventListener("open", function (e) { - // opened - opened = true; - onSuccess(); + if (opened === false) { + opened = true; + onSuccess(); + } }, false); connection.eventSource.addEventListener("message", function (e) { diff --git a/SignalR/Scripts/jquery.signalR.min.js b/SignalR/Scripts/jquery.signalR.min.js index f9c0cb860..9d9bd608a 100644 --- a/SignalR/Scripts/jquery.signalR.min.js +++ b/SignalR/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i){var u=n.url+"/connect",r="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(r="connectionData="+t.escape(n.data)+"&"+r),u+="?"+r},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r;if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&(r=n(t),n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}})),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var h=this,s=!1,o;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),o=i.getUrl(r,this.name),r.eventSource=new t.EventSource(o),r.eventSource.addEventListener("open",function(){s=!0,f()},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(t){s||e(),t.eventPhase==EventSource.CLOSED?(u("SignalR: EventSource closed"),h.stop()):(u("SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){t.setTimeout(function(){var t=n.frame,i=t.src.replace("/connect","")+"&messageId="+n.messageId+"&groups="+escape(n.groups);t.src=i},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}},longPolling:{name:"longPolling",start:function(r,u){r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var u=r.messageId,o=u===null,e=r.url+(o?"/connect":"")+"?transport=longPolling&connectionId="+escape(r.id)+"&messageId="+u+"&groups="+escape((r.groups||[]).toString());r.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i){var u=n.url+"/connect",r="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(r="connectionData="+t.escape(n.data)+"&"+r),u+="?"+r},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r;if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&(r=n(t),n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}})),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var h=this,o=!1,s;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),s=i.getUrl(r,this.name),r.eventSource=new t.EventSource(s),r.eventSource.addEventListener("open",function(){o===!1&&(o=!0,f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(t){o||e(),t.eventPhase==EventSource.CLOSED?(u("SignalR: EventSource closed"),h.stop()):(u("SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){t.setTimeout(function(){var t=n.frame,i=t.src.replace("/connect","")+"&messageId="+n.messageId+"&groups="+escape(n.groups);t.src=i},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}},longPolling:{name:"longPolling",start:function(r,u){r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var u=r.messageId,o=u===null,e=r.url+(o?"/connect":"")+"?transport=longPolling&connectionId="+escape(r.id)+"&messageId="+u+"&groups="+escape((r.groups||[]).toString());r.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file From 7dcf92d895250ed17e757f6809c196f9fc231bb3 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Mon, 9 Jan 2012 19:39:40 -0800 Subject: [PATCH 0342/1128] Remove Buffer flag from IResponse and always disable buffering in SignalR hosts. --- SignalR.AspNet/AspNetResponse.cs | 39 +++++++------------ .../HttpListenerResponseWrapper.cs | 15 ++----- SignalR/Abstractions/IResponse.cs | 1 - SignalR/Transports/ForeverTransport.cs | 4 +- .../Transports/ServerSentEventsTransport.cs | 2 - 5 files changed, 19 insertions(+), 42 deletions(-) diff --git a/SignalR.AspNet/AspNetResponse.cs b/SignalR.AspNet/AspNetResponse.cs index 282734e89..52a2d9c43 100644 --- a/SignalR.AspNet/AspNetResponse.cs +++ b/SignalR.AspNet/AspNetResponse.cs @@ -19,30 +19,7 @@ public class AspNetResponse : IResponse public AspNetResponse(HttpContextBase context) { _context = context; - } - - public bool Buffer - { - get - { - return _context.Response.Buffer; - } - set - { - _context.Response.Buffer = value; - _context.Response.BufferOutput = value; - - if (!value) - { - // This forces the IIS compression module to leave this response alone. - // If we don't do this, it will buffer the response to suit its own compression - // logic, resulting in partial messages being sent to the client. - RemoveAcceptEncoding(); - - _context.Response.CacheControl = "no-cache"; - _context.Response.AddHeader("Connection", "keep-alive"); - } - } + DisableResponseBuffering(); } public bool IsClientConnected @@ -71,6 +48,20 @@ public Task WriteAsync(string data) return TaskAsyncHelper.Empty; } + private void DisableResponseBuffering() + { + _context.Response.Buffer = false; + _context.Response.BufferOutput = false; + + // This forces the IIS compression module to leave this response alone. + // If we don't do this, it will buffer the response to suit its own compression + // logic, resulting in partial messages being sent to the client. + RemoveAcceptEncoding(); + + _context.Response.CacheControl = "no-cache"; + _context.Response.AddHeader("Connection", "keep-alive"); + } + private void RemoveAcceptEncoding() { var workerRequest = (HttpWorkerRequest)_context.GetService(typeof(HttpWorkerRequest)); diff --git a/SignalR.SelfHost/HttpListenerResponseWrapper.cs b/SignalR.SelfHost/HttpListenerResponseWrapper.cs index 1f6c4d98c..adaeec03e 100644 --- a/SignalR.SelfHost/HttpListenerResponseWrapper.cs +++ b/SignalR.SelfHost/HttpListenerResponseWrapper.cs @@ -12,16 +12,9 @@ public class HttpListenerResponseWrapper : IResponse public HttpListenerResponseWrapper(HttpListenerResponse httpListenerResponse) { _httpListenerResponse = httpListenerResponse; - Buffer = true; IsClientConnected = true; } - public bool Buffer - { - get; - set; - } - public string ContentType { get @@ -53,11 +46,9 @@ public Task WriteAsync(string data) IsClientConnected = false; } } - else if (!Buffer) - { - // Flush the response if we aren't buffering - _httpListenerResponse.OutputStream.Flush(); - } + + // Always flush the response + _httpListenerResponse.OutputStream.Flush(); }); } } diff --git a/SignalR/Abstractions/IResponse.cs b/SignalR/Abstractions/IResponse.cs index 7e7254bb1..5e3cf4e12 100644 --- a/SignalR/Abstractions/IResponse.cs +++ b/SignalR/Abstractions/IResponse.cs @@ -4,7 +4,6 @@ namespace SignalR.Abstractions { public interface IResponse { - bool Buffer { get; set; } bool IsClientConnected { get; } string ContentType { get; set; } diff --git a/SignalR/Transports/ForeverTransport.cs b/SignalR/Transports/ForeverTransport.cs index 4eb93ea10..0ed581a01 100644 --- a/SignalR/Transports/ForeverTransport.cs +++ b/SignalR/Transports/ForeverTransport.cs @@ -163,9 +163,7 @@ protected virtual Task InitializeResponse(IReceivingConnection connection) { // Don't timeout connection.ReceiveTimeout = TimeSpan.FromDays(1); - - _context.Response.Buffer = false; - + return TaskAsyncHelper.Empty; } diff --git a/SignalR/Transports/ServerSentEventsTransport.cs b/SignalR/Transports/ServerSentEventsTransport.cs index a323151c9..ce4765ada 100644 --- a/SignalR/Transports/ServerSentEventsTransport.cs +++ b/SignalR/Transports/ServerSentEventsTransport.cs @@ -1,7 +1,5 @@ using System; -using System.Web; using System.Threading.Tasks; -using SignalR.Infrastructure; using SignalR.Abstractions; namespace SignalR.Transports From 1feab940491e2d39fe957c92ddddd9a404c12abe Mon Sep 17 00:00:00 2001 From: David Fowler Date: Mon, 9 Jan 2012 21:46:14 -0800 Subject: [PATCH 0343/1128] Fix disconnect logic in ForeverTransport. --- SignalR/Transports/ForeverTransport.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/SignalR/Transports/ForeverTransport.cs b/SignalR/Transports/ForeverTransport.cs index 0ed581a01..9a466cca4 100644 --- a/SignalR/Transports/ForeverTransport.cs +++ b/SignalR/Transports/ForeverTransport.cs @@ -163,7 +163,7 @@ protected virtual Task InitializeResponse(IReceivingConnection connection) { // Don't timeout connection.ReceiveTimeout = TimeSpan.FromDays(1); - + return TaskAsyncHelper.Empty; } @@ -242,8 +242,7 @@ private void ProcessMessagesImpl(TaskCompletionSource taskCompletetionSo return; } - // Client is no longer connected - Disconnect().ContinueWith(taskCompletetionSource); + taskCompletetionSource.SetResult(null); return; } } From 3c56201bd8b04d19bfd22515f2240738d6fadbde Mon Sep 17 00:00:00 2001 From: damianedwards Date: Mon, 9 Jan 2012 23:57:47 -0800 Subject: [PATCH 0344/1128] Fixed reconnect logic in ServerSentEventsTransport and ForeverFrameTransport. Fixes #91 --- SignalR.Samples/Scripts/jquery.signalR.js | 62 +++++++++++++------ SignalR.Samples/Scripts/jquery.signalR.min.js | 2 +- SignalR/Connection.cs | 3 +- SignalR/PersistentResponse.cs | 1 + SignalR/Scripts/jquery.signalR.js | 62 +++++++++++++------ SignalR/Scripts/jquery.signalR.min.js | 2 +- SignalR/Transports/ForeverFrameTransport.cs | 6 ++ SignalR/Transports/ForeverTransport.cs | 6 +- SignalR/Transports/LongPollingTransport.cs | 4 +- .../Transports/ServerSentEventsTransport.cs | 4 +- 10 files changed, 106 insertions(+), 46 deletions(-) diff --git a/SignalR.Samples/Scripts/jquery.signalR.js b/SignalR.Samples/Scripts/jquery.signalR.js index c32ecae6e..4360b0c3f 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.js +++ b/SignalR.Samples/Scripts/jquery.signalR.js @@ -221,12 +221,24 @@ // Transports var transportLogic = { - getUrl: function (connection, transport) { + getUrl: function (connection, transport, reconnecting) { /// Gets the url for making a GET based connect request - var url = connection.url + "/connect", + var url = connection.url, qs = "transport=" + transport + "&connectionId=" + window.escape(connection.id); + if (connection.data) { - qs = "connectionData=" + window.escape(connection.data) + "&" + qs; + qs += "&connectionData=" + window.escape(connection.data); + } + + if (!reconnecting) { + url = url + "/connect"; + } else { + if (connection.messageId) { + qs += "&messageId=" + connection.messageId; + } + if (connection.groups) { + qs += "&groups=" + window.escape(JSON.stringify(connection.groups)); + } } url += "?" + qs; return url; @@ -389,6 +401,7 @@ start: function (connection, onSuccess, onFailed) { var that = this, opened = false, + reconnecting = !onSuccess, url; if (connection.eventSource) { @@ -402,14 +415,16 @@ $(connection).trigger("onSending"); - url = transportLogic.getUrl(connection, this.name); + url = transportLogic.getUrl(connection, this.name, reconnecting); connection.eventSource = new window.EventSource(url); connection.eventSource.addEventListener("open", function (e) { if (opened === false) { opened = true; - onSuccess(); + if (onSuccess) { + onSuccess(); + } } }, false); @@ -426,15 +441,27 @@ connection.eventSource.addEventListener("error", function (e) { if (!opened) { - onFailed(); + if (onFailed) { + onFailed(); + } } - if (e.eventPhase == EventSource.CLOSED) { - // connection closed - log("SignalR: EventSource closed"); - that.stop(); + + log('SignalR: EventSource readyState: ' + connection.eventSource.readyState); + + if (e.eventPhase === EventSource.CLOSED) { + // connection closed + if (connection.eventSource.readyState === window.EventSource.CONNECTING) { + log('[' + new Date().toTimeString() + '] SignalR: EventSource reconnecting'); + that.stop(connection); + that.start(connection); + } + else { + log('[' + new Date().toTimeString() + '] SignalR: EventSource closed'); + that.stop(connection); + } } else { // connection error - log("SignalR: EventSource error"); + log('[' + new Date().toTimeString() + '] SignalR: EventSource error'); $(instance).trigger("onError", [data]); } }, false); @@ -486,9 +513,10 @@ }, reconnect: function (connection) { + var that = this; window.setTimeout(function () { var frame = connection.frame, - src = frame.src.replace("/connect", "") + "&messageId=" + connection.messageId + "&groups=" + escape(connection.groups); + src = transportLogic.getUrl(connection, that.name, true) + "&frameId=" + connection.frameId; frame.src = src; }, 2000); }, @@ -513,6 +541,7 @@ started: function (connection) { if (connection.onSuccess) { connection.onSuccess(); + delete connection.onSuccess; } } }, @@ -523,6 +552,7 @@ start: function (connection, onSuccess, onFailed) { /// Starts the long polling connection /// The SignalR connection to start + var that = this; if (connection.pollXhr) { connection.stop(); } @@ -535,16 +565,12 @@ var messageId = instance.messageId, connect = (messageId === null), - url = instance.url + (connect ? "/connect" : "") + - "?transport=longPolling" + - "&connectionId=" + escape(instance.id) + - "&messageId=" + messageId + - "&groups=" + escape((instance.groups || []).toString()); + url = transportLogic.getUrl(instance, that.name, !connect); instance.pollXhr = $.ajax(url, { global: false, - type: "POST", + type: "GET", data: { connectionData: instance.data diff --git a/SignalR.Samples/Scripts/jquery.signalR.min.js b/SignalR.Samples/Scripts/jquery.signalR.min.js index 9d9bd608a..b450d887d 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.min.js +++ b/SignalR.Samples/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i){var u=n.url+"/connect",r="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(r="connectionData="+t.escape(n.data)+"&"+r),u+="?"+r},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r;if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&(r=n(t),n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}})),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var h=this,o=!1,s;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),s=i.getUrl(r,this.name),r.eventSource=new t.EventSource(s),r.eventSource.addEventListener("open",function(){o===!1&&(o=!0,f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(t){o||e(),t.eventPhase==EventSource.CLOSED?(u("SignalR: EventSource closed"),h.stop()):(u("SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){t.setTimeout(function(){var t=n.frame,i=t.src.replace("/connect","")+"&messageId="+n.messageId+"&groups="+escape(n.groups);t.src=i},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}},longPolling:{name:"longPolling",start:function(r,u){r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var u=r.messageId,o=u===null,e=r.url+(o?"/connect":"")+"?transport=longPolling&connectionId="+escape(r.id)+"&messageId="+u+"&groups="+escape((r.groups||[]).toString());r.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r;if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&(r=n(t),n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}})),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var s=this,o=!1,c=!f,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),h=i.getUrl(r,this.name,c),r.eventSource=new t.EventSource(h),r.eventSource.addEventListener("open",function(){o===!1&&(o=!0,f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){o||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),s.stop(r),s.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),s.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR/Connection.cs b/SignalR/Connection.cs index c81bb3972..f27a43d3a 100644 --- a/SignalR/Connection.cs +++ b/SignalR/Connection.cs @@ -175,7 +175,8 @@ private PersistentResponse GetEmptyResponse(long? messageId) { var response = new PersistentResponse { - MessageId = messageId ?? 0 + MessageId = messageId ?? 0, + TimedOut = true }; PopulateResponseState(response); diff --git a/SignalR/PersistentResponse.cs b/SignalR/PersistentResponse.cs index 06681c585..6bd2e0b42 100644 --- a/SignalR/PersistentResponse.cs +++ b/SignalR/PersistentResponse.cs @@ -9,6 +9,7 @@ public class PersistentResponse public long MessageId { get; set; } public IEnumerable Messages { get; set; } public bool Disconnect { get; set; } + public bool TimedOut { get; set; } // TODO: Don't seralize TransportData to the response if there is none public IDictionary TransportData diff --git a/SignalR/Scripts/jquery.signalR.js b/SignalR/Scripts/jquery.signalR.js index c32ecae6e..4360b0c3f 100644 --- a/SignalR/Scripts/jquery.signalR.js +++ b/SignalR/Scripts/jquery.signalR.js @@ -221,12 +221,24 @@ // Transports var transportLogic = { - getUrl: function (connection, transport) { + getUrl: function (connection, transport, reconnecting) { /// Gets the url for making a GET based connect request - var url = connection.url + "/connect", + var url = connection.url, qs = "transport=" + transport + "&connectionId=" + window.escape(connection.id); + if (connection.data) { - qs = "connectionData=" + window.escape(connection.data) + "&" + qs; + qs += "&connectionData=" + window.escape(connection.data); + } + + if (!reconnecting) { + url = url + "/connect"; + } else { + if (connection.messageId) { + qs += "&messageId=" + connection.messageId; + } + if (connection.groups) { + qs += "&groups=" + window.escape(JSON.stringify(connection.groups)); + } } url += "?" + qs; return url; @@ -389,6 +401,7 @@ start: function (connection, onSuccess, onFailed) { var that = this, opened = false, + reconnecting = !onSuccess, url; if (connection.eventSource) { @@ -402,14 +415,16 @@ $(connection).trigger("onSending"); - url = transportLogic.getUrl(connection, this.name); + url = transportLogic.getUrl(connection, this.name, reconnecting); connection.eventSource = new window.EventSource(url); connection.eventSource.addEventListener("open", function (e) { if (opened === false) { opened = true; - onSuccess(); + if (onSuccess) { + onSuccess(); + } } }, false); @@ -426,15 +441,27 @@ connection.eventSource.addEventListener("error", function (e) { if (!opened) { - onFailed(); + if (onFailed) { + onFailed(); + } } - if (e.eventPhase == EventSource.CLOSED) { - // connection closed - log("SignalR: EventSource closed"); - that.stop(); + + log('SignalR: EventSource readyState: ' + connection.eventSource.readyState); + + if (e.eventPhase === EventSource.CLOSED) { + // connection closed + if (connection.eventSource.readyState === window.EventSource.CONNECTING) { + log('[' + new Date().toTimeString() + '] SignalR: EventSource reconnecting'); + that.stop(connection); + that.start(connection); + } + else { + log('[' + new Date().toTimeString() + '] SignalR: EventSource closed'); + that.stop(connection); + } } else { // connection error - log("SignalR: EventSource error"); + log('[' + new Date().toTimeString() + '] SignalR: EventSource error'); $(instance).trigger("onError", [data]); } }, false); @@ -486,9 +513,10 @@ }, reconnect: function (connection) { + var that = this; window.setTimeout(function () { var frame = connection.frame, - src = frame.src.replace("/connect", "") + "&messageId=" + connection.messageId + "&groups=" + escape(connection.groups); + src = transportLogic.getUrl(connection, that.name, true) + "&frameId=" + connection.frameId; frame.src = src; }, 2000); }, @@ -513,6 +541,7 @@ started: function (connection) { if (connection.onSuccess) { connection.onSuccess(); + delete connection.onSuccess; } } }, @@ -523,6 +552,7 @@ start: function (connection, onSuccess, onFailed) { /// Starts the long polling connection /// The SignalR connection to start + var that = this; if (connection.pollXhr) { connection.stop(); } @@ -535,16 +565,12 @@ var messageId = instance.messageId, connect = (messageId === null), - url = instance.url + (connect ? "/connect" : "") + - "?transport=longPolling" + - "&connectionId=" + escape(instance.id) + - "&messageId=" + messageId + - "&groups=" + escape((instance.groups || []).toString()); + url = transportLogic.getUrl(instance, that.name, !connect); instance.pollXhr = $.ajax(url, { global: false, - type: "POST", + type: "GET", data: { connectionData: instance.data diff --git a/SignalR/Scripts/jquery.signalR.min.js b/SignalR/Scripts/jquery.signalR.min.js index 9d9bd608a..b450d887d 100644 --- a/SignalR/Scripts/jquery.signalR.min.js +++ b/SignalR/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i){var u=n.url+"/connect",r="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(r="connectionData="+t.escape(n.data)+"&"+r),u+="?"+r},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r;if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&(r=n(t),n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}})),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var h=this,o=!1,s;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),s=i.getUrl(r,this.name),r.eventSource=new t.EventSource(s),r.eventSource.addEventListener("open",function(){o===!1&&(o=!0,f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(t){o||e(),t.eventPhase==EventSource.CLOSED?(u("SignalR: EventSource closed"),h.stop()):(u("SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){t.setTimeout(function(){var t=n.frame,i=t.src.replace("/connect","")+"&messageId="+n.messageId+"&groups="+escape(n.groups);t.src=i},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&n.onSuccess()}},longPolling:{name:"longPolling",start:function(r,u){r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var u=r.messageId,o=u===null,e=r.url+(o?"/connect":"")+"?transport=longPolling&connectionId="+escape(r.id)+"&messageId="+u+"&groups="+escape((r.groups||[]).toString());r.pollXhr=n.ajax(e,{global:!1,type:"POST",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r;if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&(r=n(t),n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}})),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var s=this,o=!1,c=!f,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),h=i.getUrl(r,this.name,c),r.eventSource=new t.EventSource(h),r.eventSource.addEventListener("open",function(){o===!1&&(o=!0,f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){o||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),s.stop(r),s.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),s.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR/Transports/ForeverFrameTransport.cs b/SignalR/Transports/ForeverFrameTransport.cs index 393c2a4c6..e1ebdbc3d 100644 --- a/SignalR/Transports/ForeverFrameTransport.cs +++ b/SignalR/Transports/ForeverFrameTransport.cs @@ -58,6 +58,12 @@ public override Task Send(PersistentResponse response) protected override Task InitializeResponse(IReceivingConnection connection) { + long lastMessageId; + if (Int64.TryParse(Context.Request.QueryString["messageId"], out lastMessageId)) + { + LastMessageId = lastMessageId; + } + return base.InitializeResponse(connection) .Then(initScript => Context.Response.WriteAsync(initScript), String.Format(_initTemplate, Context.Request.QueryString["frameId"])) .FastUnwrap(); diff --git a/SignalR/Transports/ForeverTransport.cs b/SignalR/Transports/ForeverTransport.cs index 9a466cca4..022f5d43a 100644 --- a/SignalR/Transports/ForeverTransport.cs +++ b/SignalR/Transports/ForeverTransport.cs @@ -65,14 +65,14 @@ public IEnumerable Groups { get { - string groupValue = _context.Request.QueryStringOrForm("groups"); + string groupValue = _context.Request.QueryString["groups"]; if (String.IsNullOrEmpty(groupValue)) { return Enumerable.Empty(); } - return groupValue.Split(','); + return _jsonSerializer.Parse(groupValue); } } @@ -216,7 +216,7 @@ private void ProcessMessagesImpl(TaskCompletionSource taskCompletetionSo // If the response has the Disconnect flag, just send the response and exit the loop, // the server thinks connection is gone. Otherwse, send the response then re-enter the loop Task sendTask = Send(response); - if (response.Disconnect) + if (response.Disconnect || response.TimedOut) { // Signal the tcs when the task is done return sendTask.Then(tcs => tcs.SetResult(null), taskCompletetionSource); diff --git a/SignalR/Transports/LongPollingTransport.cs b/SignalR/Transports/LongPollingTransport.cs index 5d2d2e78a..7abe64219 100644 --- a/SignalR/Transports/LongPollingTransport.cs +++ b/SignalR/Transports/LongPollingTransport.cs @@ -50,14 +50,14 @@ public IEnumerable Groups { get { - string groupValue = _context.Request.QueryStringOrForm("groups"); + string groupValue = _context.Request.QueryString["groups"]; if (String.IsNullOrEmpty(groupValue)) { return Enumerable.Empty(); } - return groupValue.Split(','); + return _jsonSerializer.Parse(groupValue); } } diff --git a/SignalR/Transports/ServerSentEventsTransport.cs b/SignalR/Transports/ServerSentEventsTransport.cs index ce4765ada..e37b30d6b 100644 --- a/SignalR/Transports/ServerSentEventsTransport.cs +++ b/SignalR/Transports/ServerSentEventsTransport.cs @@ -16,7 +16,7 @@ protected override bool IsConnectRequest { get { - return Context.Request.Headers["Last-Event-ID"] == null; + return Context.Request.Url.LocalPath.EndsWith("/connect", StringComparison.OrdinalIgnoreCase); } } @@ -35,7 +35,7 @@ public override Task Send(PersistentResponse response) protected override Task InitializeResponse(IReceivingConnection connection) { long lastMessageId; - if (Int64.TryParse(Context.Request.Headers["Last-Event-ID"], out lastMessageId)) + if (Int64.TryParse(Context.Request.QueryString["messageId"], out lastMessageId)) { LastMessageId = lastMessageId; } From bdd2f3ab19b05d7d6d8aa163d140a29fa26d2417 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Tue, 10 Jan 2012 09:11:46 -0800 Subject: [PATCH 0345/1128] Fixed issue with LongPollingTransport and disconnect and added new reconnect event. --- SignalR.Samples/Scripts/jquery.signalR.js | 25 +++++++++++++------ SignalR.Samples/Scripts/jquery.signalR.min.js | 2 +- SignalR/Scripts/jquery.signalR.js | 25 +++++++++++++------ SignalR/Scripts/jquery.signalR.min.js | 2 +- SignalR/Transports/LongPollingTransport.cs | 6 ++--- 5 files changed, 40 insertions(+), 20 deletions(-) diff --git a/SignalR.Samples/Scripts/jquery.signalR.js b/SignalR.Samples/Scripts/jquery.signalR.js index 4360b0c3f..3a1ae1809 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.js +++ b/SignalR.Samples/Scripts/jquery.signalR.js @@ -199,6 +199,17 @@ return connection; }, + reconnect: function (callback) { + /// Adds a callback that will be invoked when the client reconnects after being disconnected + /// A callback function to execute when the connection is re-established + /// + var connection = this; + $(connection).bind("onReconnect", function (e, data) { + callback.call(connection); + }); + return connection; + }, + stop: function () { /// Stops listening /// @@ -261,25 +272,25 @@ if (textStatus === "abort") { return; } - $(connection).trigger("onError", [errData]); + $(connection).trigger("onError"); } }); }, processMessages: function (connection, data) { - var $connection; + var $connection = $(connection); if (data) { if (data.Disconnect) { - // Disconnected by the server, need to reconnect - connection.stop() - .start(); + // Disconnected by the server + connection.stop(); + + // Trigger the reconnect event + $connection.trigger("onReconnect"); return; } if (data.Messages) { - $connection = $(connection); - $.each(data.Messages, function () { try { $connection.trigger("onReceived", [this]); diff --git a/SignalR.Samples/Scripts/jquery.signalR.min.js b/SignalR.Samples/Scripts/jquery.signalR.min.js index b450d887d..7d798819d 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.min.js +++ b/SignalR.Samples/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r;if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&(r=n(t),n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}})),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var s=this,o=!1,c=!f,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),h=i.getUrl(r,this.name,c),r.eventSource=new t.EventSource(h),r.eventSource.addEventListener("open",function(){o===!1&&(o=!0,f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){o||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),s.stop(r),s.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),s.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},reconnect:function(t){var i=this;return n(i).bind("onReconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError")}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onReconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var s=this,o=!1,c=!f,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),h=i.getUrl(r,this.name,c),r.eventSource=new t.EventSource(h),r.eventSource.addEventListener("open",function(){o===!1&&(o=!0,f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){o||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),s.stop(r),s.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),s.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR/Scripts/jquery.signalR.js b/SignalR/Scripts/jquery.signalR.js index 4360b0c3f..3a1ae1809 100644 --- a/SignalR/Scripts/jquery.signalR.js +++ b/SignalR/Scripts/jquery.signalR.js @@ -199,6 +199,17 @@ return connection; }, + reconnect: function (callback) { + /// Adds a callback that will be invoked when the client reconnects after being disconnected + /// A callback function to execute when the connection is re-established + /// + var connection = this; + $(connection).bind("onReconnect", function (e, data) { + callback.call(connection); + }); + return connection; + }, + stop: function () { /// Stops listening /// @@ -261,25 +272,25 @@ if (textStatus === "abort") { return; } - $(connection).trigger("onError", [errData]); + $(connection).trigger("onError"); } }); }, processMessages: function (connection, data) { - var $connection; + var $connection = $(connection); if (data) { if (data.Disconnect) { - // Disconnected by the server, need to reconnect - connection.stop() - .start(); + // Disconnected by the server + connection.stop(); + + // Trigger the reconnect event + $connection.trigger("onReconnect"); return; } if (data.Messages) { - $connection = $(connection); - $.each(data.Messages, function () { try { $connection.trigger("onReceived", [this]); diff --git a/SignalR/Scripts/jquery.signalR.min.js b/SignalR/Scripts/jquery.signalR.min.js index b450d887d..7d798819d 100644 --- a/SignalR/Scripts/jquery.signalR.min.js +++ b/SignalR/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r;if(i){if(i.Disconnect){t.stop().start();return}i.Messages&&(r=n(t),n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}})),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var s=this,o=!1,c=!f,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),h=i.getUrl(r,this.name,c),r.eventSource=new t.EventSource(h),r.eventSource.addEventListener("open",function(){o===!1&&(o=!0,f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){o||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),s.stop(r),s.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),s.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},reconnect:function(t){var i=this;return n(i).bind("onReconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError")}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onReconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var s=this,o=!1,c=!f,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),h=i.getUrl(r,this.name,c),r.eventSource=new t.EventSource(h),r.eventSource.addEventListener("open",function(){o===!1&&(o=!0,f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){o||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),s.stop(r),s.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),s.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR/Transports/LongPollingTransport.cs b/SignalR/Transports/LongPollingTransport.cs index 7abe64219..608332788 100644 --- a/SignalR/Transports/LongPollingTransport.cs +++ b/SignalR/Transports/LongPollingTransport.cs @@ -201,10 +201,6 @@ private Task ProcessSendRequest() private Task ProcessConnectRequest(IReceivingConnection connection) { - // Since this is the first request, there's no data we need to retrieve so just wait - // on a message to come through - _heartBeat.AddConnection(this); - if (Connected != null) { return Connected().Then(() => ProcessReceiveRequest(connection)).FastUnwrap(); @@ -215,6 +211,8 @@ private Task ProcessConnectRequest(IReceivingConnection connection) private Task ProcessReceiveRequest(IReceivingConnection connection) { + _heartBeat.AddConnection(this); + // ReceiveAsync() will async wait until a message arrives then return var receiveTask = IsConnectRequest ? connection.ReceiveAsync() : From 65e568d5eaa2d6d9ace75557ad17755738933e62 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Tue, 10 Jan 2012 09:52:03 -0800 Subject: [PATCH 0346/1128] Pass error data to error handler. --- SignalR.Samples/Scripts/jquery.signalR.js | 4 ++-- SignalR.Samples/Scripts/jquery.signalR.min.js | 2 +- SignalR/Scripts/jquery.signalR.js | 4 ++-- SignalR/Scripts/jquery.signalR.min.js | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/SignalR.Samples/Scripts/jquery.signalR.js b/SignalR.Samples/Scripts/jquery.signalR.js index 3a1ae1809..2a124d80d 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.js +++ b/SignalR.Samples/Scripts/jquery.signalR.js @@ -194,7 +194,7 @@ /// var connection = this; $(connection).bind("onError", function (e, data) { - callback.call(connection); + callback.call(connection, data); }); return connection; }, @@ -272,7 +272,7 @@ if (textStatus === "abort") { return; } - $(connection).trigger("onError"); + $(connection).trigger("onError", [errData]); } }); }, diff --git a/SignalR.Samples/Scripts/jquery.signalR.min.js b/SignalR.Samples/Scripts/jquery.signalR.min.js index 7d798819d..e5aca4045 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.min.js +++ b/SignalR.Samples/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},reconnect:function(t){var i=this;return n(i).bind("onReconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError")}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onReconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var s=this,o=!1,c=!f,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),h=i.getUrl(r,this.name,c),r.eventSource=new t.EventSource(h),r.eventSource.addEventListener("open",function(){o===!1&&(o=!0,f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){o||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),s.stop(r),s.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),s.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},reconnect:function(t){var i=this;return n(i).bind("onReconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onReconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var s=this,o=!1,c=!f,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),h=i.getUrl(r,this.name,c),r.eventSource=new t.EventSource(h),r.eventSource.addEventListener("open",function(){o===!1&&(o=!0,f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){o||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),s.stop(r),s.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),s.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR/Scripts/jquery.signalR.js b/SignalR/Scripts/jquery.signalR.js index 3a1ae1809..2a124d80d 100644 --- a/SignalR/Scripts/jquery.signalR.js +++ b/SignalR/Scripts/jquery.signalR.js @@ -194,7 +194,7 @@ /// var connection = this; $(connection).bind("onError", function (e, data) { - callback.call(connection); + callback.call(connection, data); }); return connection; }, @@ -272,7 +272,7 @@ if (textStatus === "abort") { return; } - $(connection).trigger("onError"); + $(connection).trigger("onError", [errData]); } }); }, diff --git a/SignalR/Scripts/jquery.signalR.min.js b/SignalR/Scripts/jquery.signalR.min.js index 7d798819d..e5aca4045 100644 --- a/SignalR/Scripts/jquery.signalR.min.js +++ b/SignalR/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(){t.call(i)}),i},reconnect:function(t){var i=this;return n(i).bind("onReconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError")}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onReconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var s=this,o=!1,c=!f,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),h=i.getUrl(r,this.name,c),r.eventSource=new t.EventSource(h),r.eventSource.addEventListener("open",function(){o===!1&&(o=!0,f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){o||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),s.stop(r),s.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),s.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},reconnect:function(t){var i=this;return n(i).bind("onReconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onReconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var s=this,o=!1,c=!f,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),h=i.getUrl(r,this.name,c),r.eventSource=new t.EventSource(h),r.eventSource.addEventListener("open",function(){o===!1&&(o=!0,f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){o||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),s.stop(r),s.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),s.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file From 9486eb3dbcd70039a0736b7bd72f897b988bb24f Mon Sep 17 00:00:00 2001 From: David Fowler Date: Tue, 10 Jan 2012 10:06:55 -0800 Subject: [PATCH 0347/1128] Fixed issue with setting timeout value on persistent response. --- SignalR/Connection.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SignalR/Connection.cs b/SignalR/Connection.cs index f27a43d3a..c3f4d21f3 100644 --- a/SignalR/Connection.cs +++ b/SignalR/Connection.cs @@ -160,7 +160,7 @@ private Task ProcessSignal(SignalResult result, long? messag { if (result.TimedOut) { - PersistentResponse response = GetEmptyResponse(messageId); + PersistentResponse response = GetEmptyResponse(messageId, result.TimedOut); // Return a task wrapping the result return TaskAsyncHelper.FromResult(response); @@ -171,12 +171,12 @@ private Task ProcessSignal(SignalResult result, long? messag .Then((response, id) => response ?? GetEmptyResponse(id), messageId); } - private PersistentResponse GetEmptyResponse(long? messageId) + private PersistentResponse GetEmptyResponse(long? messageId, bool timedOut = false) { var response = new PersistentResponse { MessageId = messageId ?? 0, - TimedOut = true + TimedOut = timedOut }; PopulateResponseState(response); @@ -239,7 +239,7 @@ private void ProcessCommand(Message message) { return; } - + switch (command.Type) { case CommandType.AddToGroup: From 5f4d40d51d69ecb4072e78181c0486bd62c4bcb1 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Tue, 10 Jan 2012 19:54:00 -0800 Subject: [PATCH 0348/1128] Json serialize groups in receive query string in .net client. --- SignalR.Client/Transports/HttpBasedTransport.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SignalR.Client/Transports/HttpBasedTransport.cs b/SignalR.Client/Transports/HttpBasedTransport.cs index 434f3e045..6c4ab720a 100644 --- a/SignalR.Client/Transports/HttpBasedTransport.cs +++ b/SignalR.Client/Transports/HttpBasedTransport.cs @@ -67,7 +67,7 @@ protected string GetReceiveQueryString(Connection connection, string data) _transport, Uri.EscapeDataString(connection.ConnectionId), Convert.ToString(connection.MessageId), - Uri.EscapeDataString(String.Join(",", connection.Groups.ToArray())), + Uri.EscapeDataString(JsonConvert.SerializeObject(connection.Groups)), data); } From 83d2734cf74ce9c636fa35c66703c07e65f716b1 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Tue, 10 Jan 2012 23:44:04 -0800 Subject: [PATCH 0349/1128] Fixed #122. Added reconnect logic to .net client ServerSentEventsTransport. --- .../Transports/ServerSentEventsTransport.cs | 62 ++++++++++++++----- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/SignalR.Client/Transports/ServerSentEventsTransport.cs b/SignalR.Client/Transports/ServerSentEventsTransport.cs index 84433bd6a..9cf2c2e85 100644 --- a/SignalR.Client/Transports/ServerSentEventsTransport.cs +++ b/SignalR.Client/Transports/ServerSentEventsTransport.cs @@ -2,6 +2,7 @@ using System.IO; using System.Net; using System.Text; +using System.Threading; using SignalR.Client.Infrastructure; namespace SignalR.Client.Transports @@ -9,6 +10,7 @@ namespace SignalR.Client.Transports public class ServerSentEventsTransport : HttpBasedTransport { private const string ReaderKey = "sse.reader"; + private static readonly TimeSpan ReconnectDelay = TimeSpan.FromSeconds(2); public ServerSentEventsTransport() : base("serverSentEvents") @@ -22,7 +24,10 @@ protected override void OnStart(Connection connection, string data, Action initi private void OpenConnection(Connection connection, string data, Action initializeCallback, Action errorCallback) { - var url = connection.Url + GetReceiveQueryString(connection, data); + // If we're reconnecting add /connect to the url + bool reconnect = initializeCallback == null; + + var url = (reconnect ? connection.Url : connection.Url + "connect") + GetReceiveQueryString(connection, data); Action prepareRequest = PrepareRequest(connection); @@ -32,22 +37,35 @@ private void OpenConnection(Connection connection, string data, Action initializ request.Accept = "text/event-stream"; - if (connection.MessageId != null) - { - request.Headers["Last-Event-ID"] = connection.MessageId.ToString(); - } - }).ContinueWith(task => { if (task.IsFaulted) { - errorCallback(task.Exception); + if (errorCallback != null) + { + errorCallback(task.Exception); + } + else + { + // Raise the error event if we failed to reconnect + connection.OnError(task.Exception.GetBaseException()); + } } else { // Get the reseponse stream and read it for messages var stream = task.Result.GetResponseStream(); - var reader = new AsyncStreamReader(stream, connection, initializeCallback, errorCallback); + var reader = new AsyncStreamReader(stream, + connection, + initializeCallback, + () => + { + // Wait for a bit before reconnecting + Thread.Sleep(ReconnectDelay); + + // Now attempt a reconnect + OpenConnection(connection, data, initializeCallback: null, errorCallback: null); + }); reader.StartReading(); // Set the reader for this connection @@ -75,16 +93,16 @@ private class AsyncStreamReader private readonly Stream _stream; private readonly ChunkBuffer _buffer; private readonly Action _initializeCallback; - private readonly Action _errorCallback; + private readonly Action _closeCallback; private readonly Connection _connection; private int _processingQueue; private bool _reading; private bool _processingBuffer; - public AsyncStreamReader(Stream stream, Connection connection, Action initializeCallback, Action errorCallback) + public AsyncStreamReader(Stream stream, Connection connection, Action initializeCallback, Action closeCallback) { _initializeCallback = initializeCallback; - _errorCallback = errorCallback; + _closeCallback = closeCallback; _stream = stream; _connection = connection; _buffer = new ChunkBuffer(); @@ -118,8 +136,6 @@ private void ReadLoop() if (!IsRequestAborted(exception)) { _connection.OnError(exception); - - _errorCallback(exception); } return; } @@ -132,6 +148,19 @@ private void ReadLoop() _buffer.Add(buffer, read); } + if (read == 0) + { + // Stop any reading we're doing + StopReading(); + + // Close the stream + _stream.Close(); + + // Call the close callback + _closeCallback(); + return; + } + // Keep reading the next set of data ReadLoop(); @@ -220,8 +249,11 @@ private void ProcessChunks() case EventType.Data: if (sseEvent.Data.Equals("initialized", StringComparison.OrdinalIgnoreCase)) { - // Mark the connection as started - _initializeCallback(); + if (_initializeCallback != null) + { + // Mark the connection as started + _initializeCallback(); + } } else { From d0056128797778b6f1d14c9b51808ac00c0abd2c Mon Sep 17 00:00:00 2001 From: David Fowler Date: Wed, 11 Jan 2012 00:03:43 -0800 Subject: [PATCH 0350/1128] Bump version to 0.4. --- Build/Build.proj | 4 ++-- SignalR.AspNet/Properties/AssemblyInfo.cs | 2 +- SignalR.AspNet/Properties/SignalR.AspNet.nuspec | 2 +- SignalR.Client.Silverlight/Properties/AssemblyInfo.cs | 2 +- SignalR.Client.WP7/Properties/AssemblyInfo.cs | 2 +- SignalR.Client.WP71/Properties/AssemblyInfo.cs | 2 +- SignalR.Client/Properties/AssemblyInfo.cs | 2 +- SignalR.ScaleOut/Properties/AssemblyInfo.cs | 2 +- SignalR/Properties/AssemblyInfo.cs | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Build/Build.proj b/Build/Build.proj index 85ea1b7dc..92b8bd1fe 100644 --- a/Build/Build.proj +++ b/Build/Build.proj @@ -12,8 +12,8 @@ 0 - 3 - $(MajorVersion).$(MinorVersion).6 + 4 + $(MajorVersion).$(MinorVersion).0 false $description$ - + \ No newline at end of file diff --git a/SignalR.Client.Silverlight/Properties/AssemblyInfo.cs b/SignalR.Client.Silverlight/Properties/AssemblyInfo.cs index 2a315575a..6298f5992 100644 --- a/SignalR.Client.Silverlight/Properties/AssemblyInfo.cs +++ b/SignalR.Client.Silverlight/Properties/AssemblyInfo.cs @@ -3,4 +3,4 @@ [assembly: AssemblyTitle("SignalR.Client.Silverlight")] [assembly: AssemblyDescription("Silverlight client for SignalR")] -[assembly: AssemblyVersion("0.3.7.0")] \ No newline at end of file +[assembly: AssemblyVersion("0.4.0.0")] \ No newline at end of file diff --git a/SignalR.Client.WP7/Properties/AssemblyInfo.cs b/SignalR.Client.WP7/Properties/AssemblyInfo.cs index bc0148fdd..c9d61a0a1 100644 --- a/SignalR.Client.WP7/Properties/AssemblyInfo.cs +++ b/SignalR.Client.WP7/Properties/AssemblyInfo.cs @@ -3,4 +3,4 @@ [assembly: AssemblyTitle("SignalR.Client.WP7")] [assembly: AssemblyDescription("WP7 client for SignalR")] -[assembly: AssemblyVersion("0.3.7.0")] \ No newline at end of file +[assembly: AssemblyVersion("0.4.0.0")] \ No newline at end of file diff --git a/SignalR.Client.WP71/Properties/AssemblyInfo.cs b/SignalR.Client.WP71/Properties/AssemblyInfo.cs index bc0148fdd..c9d61a0a1 100644 --- a/SignalR.Client.WP71/Properties/AssemblyInfo.cs +++ b/SignalR.Client.WP71/Properties/AssemblyInfo.cs @@ -3,4 +3,4 @@ [assembly: AssemblyTitle("SignalR.Client.WP7")] [assembly: AssemblyDescription("WP7 client for SignalR")] -[assembly: AssemblyVersion("0.3.7.0")] \ No newline at end of file +[assembly: AssemblyVersion("0.4.0.0")] \ No newline at end of file diff --git a/SignalR.Client/Properties/AssemblyInfo.cs b/SignalR.Client/Properties/AssemblyInfo.cs index 8cc6e08b9..2ededa818 100644 --- a/SignalR.Client/Properties/AssemblyInfo.cs +++ b/SignalR.Client/Properties/AssemblyInfo.cs @@ -6,4 +6,4 @@ [assembly: AssemblyTitle("SignalR.Client")] [assembly: AssemblyDescription(".NET client for SignalR")] -[assembly: AssemblyVersion("0.3.7.0")] \ No newline at end of file +[assembly: AssemblyVersion("0.4.0.0")] \ No newline at end of file diff --git a/SignalR.ScaleOut/Properties/AssemblyInfo.cs b/SignalR.ScaleOut/Properties/AssemblyInfo.cs index 47f0bc36e..e511d10c8 100644 --- a/SignalR.ScaleOut/Properties/AssemblyInfo.cs +++ b/SignalR.ScaleOut/Properties/AssemblyInfo.cs @@ -5,4 +5,4 @@ // associated with an assembly. [assembly: AssemblyTitle("SignalR.ScaleOut")] [assembly: AssemblyDescription("Scale out signal buses and message stores for SignalR for use in web farms")] -[assembly: AssemblyVersion("0.3.6.0")] \ No newline at end of file +[assembly: AssemblyVersion("0.4.0.0")] \ No newline at end of file diff --git a/SignalR/Properties/AssemblyInfo.cs b/SignalR/Properties/AssemblyInfo.cs index 725ded0b0..116ff7836 100644 --- a/SignalR/Properties/AssemblyInfo.cs +++ b/SignalR/Properties/AssemblyInfo.cs @@ -5,4 +5,4 @@ [assembly: AssemblyDescription("Async signaling library for .NET to help build real-time, multi-user interactive web applications.")] [assembly: InternalsVisibleTo("SignalR.ScaleOut")] [assembly: InternalsVisibleTo("SignalR.Tests")] -[assembly: AssemblyVersion("0.3.6.0")] \ No newline at end of file +[assembly: AssemblyVersion("0.4.0.0")] \ No newline at end of file From 347453bf03bb7cd8c7c2b5922ebe54605e33e53b Mon Sep 17 00:00:00 2001 From: David Fowler Date: Wed, 11 Jan 2012 20:44:57 -0800 Subject: [PATCH 0351/1128] Added EndAsync to IResponse in order to distinguish between responses that are closed after write and streaming writes. - Implemented EndAsync in AspNetResponse and HttpListenerResponseWrapper. --- SignalR.AspNet/AspNetResponse.cs | 5 +++++ .../HttpListenerResponseWrapper.cs | 22 ++++++++++--------- SignalR/Abstractions/IResponse.cs | 1 + SignalR/Hubs/HubDispatcher.cs | 2 +- SignalR/PersistentConnection.cs | 2 +- SignalR/Transports/ForeverTransport.cs | 6 +++-- SignalR/Transports/LongPollingTransport.cs | 2 +- 7 files changed, 25 insertions(+), 15 deletions(-) diff --git a/SignalR.AspNet/AspNetResponse.cs b/SignalR.AspNet/AspNetResponse.cs index 52a2d9c43..b2bed9017 100644 --- a/SignalR.AspNet/AspNetResponse.cs +++ b/SignalR.AspNet/AspNetResponse.cs @@ -48,6 +48,11 @@ public Task WriteAsync(string data) return TaskAsyncHelper.Empty; } + public Task EndAsync(string data) + { + return WriteAsync(data); + } + private void DisableResponseBuffering() { _context.Response.Buffer = false; diff --git a/SignalR.SelfHost/HttpListenerResponseWrapper.cs b/SignalR.SelfHost/HttpListenerResponseWrapper.cs index adaeec03e..b26fca02b 100644 --- a/SignalR.SelfHost/HttpListenerResponseWrapper.cs +++ b/SignalR.SelfHost/HttpListenerResponseWrapper.cs @@ -36,20 +36,22 @@ public bool IsClientConnected public Task WriteAsync(string data) { return _httpListenerResponse.WriteAsync(data).ContinueWith(task => + { + if (task.IsFaulted) { - if (task.IsFaulted) + var ex = task.Exception.GetBaseException() as HttpListenerException; + if (ex != null && ex.ErrorCode == 1229) { - var ex = task.Exception.GetBaseException() as HttpListenerException; - if (ex != null && ex.ErrorCode == 1229) - { - // Non existent connection or connection disposed - IsClientConnected = false; - } + // Non existent connection or connection disposed + IsClientConnected = false; } + } + }).Catch(); + } - // Always flush the response - _httpListenerResponse.OutputStream.Flush(); - }); + public Task EndAsync(string data) + { + return WriteAsync(data).Then(response => response.CloseSafe(), _httpListenerResponse); } } } diff --git a/SignalR/Abstractions/IResponse.cs b/SignalR/Abstractions/IResponse.cs index 5e3cf4e12..020aaa582 100644 --- a/SignalR/Abstractions/IResponse.cs +++ b/SignalR/Abstractions/IResponse.cs @@ -8,5 +8,6 @@ public interface IResponse string ContentType { get; set; } Task WriteAsync(string data); + Task EndAsync(string data); } } diff --git a/SignalR/Hubs/HubDispatcher.cs b/SignalR/Hubs/HubDispatcher.cs index 57235530f..406df0bc9 100644 --- a/SignalR/Hubs/HubDispatcher.cs +++ b/SignalR/Hubs/HubDispatcher.cs @@ -160,7 +160,7 @@ public override Task ProcessRequestAsync(HostContext context) if (context.Request.Url.LocalPath.EndsWith("/hubs", StringComparison.OrdinalIgnoreCase)) { context.Response.ContentType = "application/x-javascript"; - return context.Response.WriteAsync(_proxyGenerator.GenerateProxy(_url)); + return context.Response.EndAsync(_proxyGenerator.GenerateProxy(_url)); } _context = context; diff --git a/SignalR/PersistentConnection.cs b/SignalR/PersistentConnection.cs index 20108196d..193d8b395 100644 --- a/SignalR/PersistentConnection.cs +++ b/SignalR/PersistentConnection.cs @@ -184,7 +184,7 @@ public Task RemoveFromGroup(string connectionId, string groupName) private Task ProcessNegotiationRequest(HostContext context) { context.Response.ContentType = Json.MimeType; - return context.Response.WriteAsync(_jsonSerializer.Stringify(new + return context.Response.EndAsync(_jsonSerializer.Stringify(new { Url = context.Request.Url.LocalPath.Replace("/negotiate", ""), ConnectionId = _connectionIdFactory.CreateConnectionId(context.Request), diff --git a/SignalR/Transports/ForeverTransport.cs b/SignalR/Transports/ForeverTransport.cs index 022f5d43a..7f5f4bc32 100644 --- a/SignalR/Transports/ForeverTransport.cs +++ b/SignalR/Transports/ForeverTransport.cs @@ -121,14 +121,16 @@ public Task ProcessRequest(IReceivingConnection connection) public virtual Task Send(PersistentResponse response) { _heartBeat.MarkConnection(this); - return Send((object)response); + var data = JsonSerializer.Stringify(response); + OnSending(data); + return _context.Response.WriteAsync(data); } public virtual Task Send(object value) { var data = JsonSerializer.Stringify(value); OnSending(data); - return _context.Response.WriteAsync(data); + return _context.Response.EndAsync(data); } public Task Disconnect() diff --git a/SignalR/Transports/LongPollingTransport.cs b/SignalR/Transports/LongPollingTransport.cs index 608332788..5fb515173 100644 --- a/SignalR/Transports/LongPollingTransport.cs +++ b/SignalR/Transports/LongPollingTransport.cs @@ -154,7 +154,7 @@ public virtual Task Send(object value) } _context.Response.ContentType = Json.MimeType; - return _context.Response.WriteAsync(payload); + return _context.Response.EndAsync(payload); } public virtual Task Disconnect() From 8ba37e9ddcfb70a17ac3ae42b5a827841e84de1c Mon Sep 17 00:00:00 2001 From: David Fowler Date: Wed, 11 Jan 2012 21:09:21 -0800 Subject: [PATCH 0352/1128] Fixes #123. Raise onFailed if EventSource ctor throws. Set a configurable timeout for the sse connection so that we move onto the next transport if it doesn't connect. --- SignalR.Samples/Scripts/jquery.signalR.js | 35 ++++++++++++++++--- SignalR.Samples/Scripts/jquery.signalR.min.js | 2 +- SignalR/Scripts/jquery.signalR.js | 35 ++++++++++++++++--- SignalR/Scripts/jquery.signalR.min.js | 2 +- 4 files changed, 64 insertions(+), 10 deletions(-) diff --git a/SignalR.Samples/Scripts/jquery.signalR.js b/SignalR.Samples/Scripts/jquery.signalR.js index 2a124d80d..80f4dd16f 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.js +++ b/SignalR.Samples/Scripts/jquery.signalR.js @@ -409,11 +409,14 @@ serverSentEvents: { name: "serverSentEvents", + timeOut: 3000, + start: function (connection, onSuccess, onFailed) { var that = this, opened = false, reconnecting = !onSuccess, - url; + url, + connectTimeOut; if (connection.eventSource) { connection.stop(); @@ -428,11 +431,37 @@ url = transportLogic.getUrl(connection, this.name, reconnecting); - connection.eventSource = new window.EventSource(url); + try { + connection.eventSource = new window.EventSource(url); + } + catch (e) { + // If the connection failed call the failed callback + if (onFailed) { + onFailed(); + } + return; + } + + // After connecting, if after the specified timeout there's no response stop the connection + // and raise on failed + connectTimeOut = window.setTimeout(function () { + if (opened === false) { + that.stop(connection); + + if (onFailed) { + onFailed(); + } + } + }, + that.timeOut); connection.eventSource.addEventListener("open", function (e) { if (opened === false) { opened = true; + + // Clear the connectTimeOut + clearTimeout(connectTimeOut); + if (onSuccess) { onSuccess(); } @@ -441,13 +470,11 @@ connection.eventSource.addEventListener("message", function (e) { // process messages - //log("SignalR: EventSource message received - " + e.data); if (e.data === "initialized") { return; } var data = window.JSON.parse(e.data); transportLogic.processMessages(connection, data); - // TODO: persist the groups and connection data in a cookie }, false); connection.eventSource.addEventListener("error", function (e) { diff --git a/SignalR.Samples/Scripts/jquery.signalR.min.js b/SignalR.Samples/Scripts/jquery.signalR.min.js index e5aca4045..2abfc0165 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.min.js +++ b/SignalR.Samples/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},reconnect:function(t){var i=this;return n(i).bind("onReconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onReconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var s=this,o=!1,c=!f,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),h=i.getUrl(r,this.name,c),r.eventSource=new t.EventSource(h),r.eventSource.addEventListener("open",function(){o===!1&&(o=!0,f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){o||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),s.stop(r),s.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),s.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},reconnect:function(t){var i=this;return n(i).bind("onReconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onReconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR/Scripts/jquery.signalR.js b/SignalR/Scripts/jquery.signalR.js index 2a124d80d..80f4dd16f 100644 --- a/SignalR/Scripts/jquery.signalR.js +++ b/SignalR/Scripts/jquery.signalR.js @@ -409,11 +409,14 @@ serverSentEvents: { name: "serverSentEvents", + timeOut: 3000, + start: function (connection, onSuccess, onFailed) { var that = this, opened = false, reconnecting = !onSuccess, - url; + url, + connectTimeOut; if (connection.eventSource) { connection.stop(); @@ -428,11 +431,37 @@ url = transportLogic.getUrl(connection, this.name, reconnecting); - connection.eventSource = new window.EventSource(url); + try { + connection.eventSource = new window.EventSource(url); + } + catch (e) { + // If the connection failed call the failed callback + if (onFailed) { + onFailed(); + } + return; + } + + // After connecting, if after the specified timeout there's no response stop the connection + // and raise on failed + connectTimeOut = window.setTimeout(function () { + if (opened === false) { + that.stop(connection); + + if (onFailed) { + onFailed(); + } + } + }, + that.timeOut); connection.eventSource.addEventListener("open", function (e) { if (opened === false) { opened = true; + + // Clear the connectTimeOut + clearTimeout(connectTimeOut); + if (onSuccess) { onSuccess(); } @@ -441,13 +470,11 @@ connection.eventSource.addEventListener("message", function (e) { // process messages - //log("SignalR: EventSource message received - " + e.data); if (e.data === "initialized") { return; } var data = window.JSON.parse(e.data); transportLogic.processMessages(connection, data); - // TODO: persist the groups and connection data in a cookie }, false); connection.eventSource.addEventListener("error", function (e) { diff --git a/SignalR/Scripts/jquery.signalR.min.js b/SignalR/Scripts/jquery.signalR.min.js index e5aca4045..2abfc0165 100644 --- a/SignalR/Scripts/jquery.signalR.min.js +++ b/SignalR/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},reconnect:function(t){var i=this;return n(i).bind("onReconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onReconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",start:function(r,f,e){var s=this,o=!1,c=!f,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),h=i.getUrl(r,this.name,c),r.eventSource=new t.EventSource(h),r.eventSource.addEventListener("open",function(){o===!1&&(o=!0,f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){o||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),s.stop(r),s.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),s.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},reconnect:function(t){var i=this;return n(i).bind("onReconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onReconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file From 5fb8c6b10660332209370860bdd459a00afbcb24 Mon Sep 17 00:00:00 2001 From: DamianEdwards Date: Thu, 12 Jan 2012 09:55:06 -0800 Subject: [PATCH 0353/1128] added timeout failure to Forever Frame --- SignalR.Samples/Scripts/jquery.signalR.js | 16 ++++++++++++++++ SignalR.Samples/Scripts/jquery.signalR.min.js | 2 +- SignalR/Scripts/jquery.signalR.js | 18 +++++++++++++++++- SignalR/Scripts/jquery.signalR.min.js | 2 +- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/SignalR.Samples/Scripts/jquery.signalR.js b/SignalR.Samples/Scripts/jquery.signalR.js index 80f4dd16f..c7d4014b1 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.js +++ b/SignalR.Samples/Scripts/jquery.signalR.js @@ -520,10 +520,13 @@ foreverFrame: { name: "foreverFrame", + timeout: 3000, + start: function (connection, onSuccess, onFailed) { var that = this, frameId = (transportLogic.foreverFrame.count += 1), url, + connectTimeOut, frame = $(""); $(connection).trigger("onSending"); @@ -548,6 +551,19 @@ } $("body").append(frame); + + // After connecting, if after the specified timeout there's no response stop the connection + // and raise on failed + connectTimeOut = window.setTimeout(function () { + if (connection.onSuccess) { + that.stop(connection); + + if (onFailed) { + onFailed(); + } + } + }, + that.timeOut); }, reconnect: function (connection) { diff --git a/SignalR.Samples/Scripts/jquery.signalR.min.js b/SignalR.Samples/Scripts/jquery.signalR.min.js index 2abfc0165..5b005c348 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.min.js +++ b/SignalR.Samples/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},reconnect:function(t){var i=this;return n(i).bind("onReconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onReconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},reconnect:function(t){var i=this;return n(i).bind("onReconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onReconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",timeout:3e3,start:function(r,f,e){var s=this,c=i.foreverFrame.count+=1,h,l,o=n("");n(r).trigger("onSending"),h=i.getUrl(r,this.name),h+="&frameId="+c,o.prop("src",h),i.foreverFrame.connections[c]=r,o.bind("load",function(){u("SignalR: forever frame iframe load event fired"),s.reconnect(r)}),r.frame=o[0],r.frameId=c,f&&(r.onSuccess=f),n("body").append(o),l=t.setTimeout(function(){r.onSuccess&&(s.stop(r),e&&e())},s.timeOut)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR/Scripts/jquery.signalR.js b/SignalR/Scripts/jquery.signalR.js index 80f4dd16f..f23846bfe 100644 --- a/SignalR/Scripts/jquery.signalR.js +++ b/SignalR/Scripts/jquery.signalR.js @@ -486,7 +486,7 @@ log('SignalR: EventSource readyState: ' + connection.eventSource.readyState); - if (e.eventPhase === EventSource.CLOSED) { + if (e.eventPhase === window.EventSource.CLOSED) { // connection closed if (connection.eventSource.readyState === window.EventSource.CONNECTING) { log('[' + new Date().toTimeString() + '] SignalR: EventSource reconnecting'); @@ -520,10 +520,13 @@ foreverFrame: { name: "foreverFrame", + timeout: 3000, + start: function (connection, onSuccess, onFailed) { var that = this, frameId = (transportLogic.foreverFrame.count += 1), url, + connectTimeOut, frame = $(""); $(connection).trigger("onSending"); @@ -548,6 +551,19 @@ } $("body").append(frame); + + // After connecting, if after the specified timeout there's no response stop the connection + // and raise on failed + connectTimeOut = window.setTimeout(function () { + if (connection.onSuccess) { + that.stop(connection); + + if (onFailed) { + onFailed(); + } + } + }, + that.timeOut); }, reconnect: function (connection) { diff --git a/SignalR/Scripts/jquery.signalR.min.js b/SignalR/Scripts/jquery.signalR.min.js index 2abfc0165..5b005c348 100644 --- a/SignalR/Scripts/jquery.signalR.min.js +++ b/SignalR/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},reconnect:function(t){var i=this;return n(i).bind("onReconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onReconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},reconnect:function(t){var i=this;return n(i).bind("onReconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onReconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",timeout:3e3,start:function(r,f,e){var s=this,c=i.foreverFrame.count+=1,h,l,o=n("");n(r).trigger("onSending"),h=i.getUrl(r,this.name),h+="&frameId="+c,o.prop("src",h),i.foreverFrame.connections[c]=r,o.bind("load",function(){u("SignalR: forever frame iframe load event fired"),s.reconnect(r)}),r.frame=o[0],r.frameId=c,f&&(r.onSuccess=f),n("body").append(o),l=t.setTimeout(function(){r.onSuccess&&(s.stop(r),e&&e())},s.timeOut)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file From a1e4d9fb1e73f89460211e3fd46b9eff7c81071e Mon Sep 17 00:00:00 2001 From: David Fowler Date: Thu, 12 Jan 2012 09:56:47 -0800 Subject: [PATCH 0354/1128] Changed the reconnect event to disconnect event. --- SignalR.Samples/Scripts/jquery.signalR.js | 12 ++++++------ SignalR.Samples/Scripts/jquery.signalR.min.js | 2 +- SignalR/Scripts/jquery.signalR.js | 12 ++++++------ SignalR/Scripts/jquery.signalR.min.js | 6 +++++- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/SignalR.Samples/Scripts/jquery.signalR.js b/SignalR.Samples/Scripts/jquery.signalR.js index c7d4014b1..ac39f6af1 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.js +++ b/SignalR.Samples/Scripts/jquery.signalR.js @@ -199,12 +199,12 @@ return connection; }, - reconnect: function (callback) { - /// Adds a callback that will be invoked when the client reconnects after being disconnected - /// A callback function to execute when the connection is re-established + disconnect: function (callback) { + /// Adds a callback that will be invoked when the client disconnects + /// A callback function to execute when the connection is broken /// var connection = this; - $(connection).bind("onReconnect", function (e, data) { + $(connection).bind("onDisconnect", function (e, data) { callback.call(connection); }); return connection; @@ -285,8 +285,8 @@ // Disconnected by the server connection.stop(); - // Trigger the reconnect event - $connection.trigger("onReconnect"); + // Trigger the disconnect event + $connection.trigger("onDisconnect"); return; } diff --git a/SignalR.Samples/Scripts/jquery.signalR.min.js b/SignalR.Samples/Scripts/jquery.signalR.min.js index 5b005c348..429ab121a 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.min.js +++ b/SignalR.Samples/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},reconnect:function(t){var i=this;return n(i).bind("onReconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onReconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",timeout:3e3,start:function(r,f,e){var s=this,c=i.foreverFrame.count+=1,h,l,o=n("");n(r).trigger("onSending"),h=i.getUrl(r,this.name),h+="&frameId="+c,o.prop("src",h),i.foreverFrame.connections[c]=r,o.bind("load",function(){u("SignalR: forever frame iframe load event fired"),s.reconnect(r)}),r.frame=o[0],r.frameId=c,f&&(r.onSuccess=f),n("body").append(o),l=t.setTimeout(function(){r.onSuccess&&(s.stop(r),e&&e())},s.timeOut)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},disconnect:function(t){var i=this;return n(i).bind("onDisconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onDisconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR/Scripts/jquery.signalR.js b/SignalR/Scripts/jquery.signalR.js index f23846bfe..171602da0 100644 --- a/SignalR/Scripts/jquery.signalR.js +++ b/SignalR/Scripts/jquery.signalR.js @@ -199,12 +199,12 @@ return connection; }, - reconnect: function (callback) { - /// Adds a callback that will be invoked when the client reconnects after being disconnected - /// A callback function to execute when the connection is re-established + disconnect: function (callback) { + /// Adds a callback that will be invoked when the client disconnects + /// A callback function to execute when the connection is broken /// var connection = this; - $(connection).bind("onReconnect", function (e, data) { + $(connection).bind("onDisconnect", function (e, data) { callback.call(connection); }); return connection; @@ -285,8 +285,8 @@ // Disconnected by the server connection.stop(); - // Trigger the reconnect event - $connection.trigger("onReconnect"); + // Trigger the disconnect event + $connection.trigger("onDisconnect"); return; } diff --git a/SignalR/Scripts/jquery.signalR.min.js b/SignalR/Scripts/jquery.signalR.min.js index 5b005c348..6a8070442 100644 --- a/SignalR/Scripts/jquery.signalR.min.js +++ b/SignalR/Scripts/jquery.signalR.min.js @@ -1 +1,5 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},reconnect:function(t){var i=this;return n(i).bind("onReconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onReconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",timeout:3e3,start:function(r,f,e){var s=this,c=i.foreverFrame.count+=1,h,l,o=n("");n(r).trigger("onSending"),h=i.getUrl(r,this.name),h+="&frameId="+c,o.prop("src",h),i.foreverFrame.connections[c]=r,o.bind("load",function(){u("SignalR: forever frame iframe load event fired"),s.reconnect(r)}),r.frame=o[0],r.frameId=c,f&&(r.onSuccess=f),n("body").append(o),l=t.setTimeout(function(){r.onSuccess&&(s.stop(r),e&&e())},s.timeOut)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +<<<<<<< HEAD +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},reconnect:function(t){var i=this;return n(i).bind("onReconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onReconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",timeout:3e3,start:function(r,f,e){var s=this,c=i.foreverFrame.count+=1,h,l,o=n("");n(r).trigger("onSending"),h=i.getUrl(r,this.name),h+="&frameId="+c,o.prop("src",h),i.foreverFrame.connections[c]=r,o.bind("load",function(){u("SignalR: forever frame iframe load event fired"),s.reconnect(r)}),r.frame=o[0],r.frameId=c,f&&(r.onSuccess=f),n("body").append(o),l=t.setTimeout(function(){r.onSuccess&&(s.stop(r),e&&e())},s.timeOut)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) +======= +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},disconnect:function(t){var i=this;return n(i).bind("onDisconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onDisconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) +>>>>>>> Changed the reconnect event to disconnect event. From 997a01a67f05bcead78c6f58ee4d1719df331ba5 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Thu, 12 Jan 2012 10:07:39 -0800 Subject: [PATCH 0355/1128] Regenerate the minified js files. --- SignalR.Samples/Scripts/jquery.signalR.js | 2 +- SignalR.Samples/Scripts/jquery.signalR.min.js | 2 +- SignalR/Scripts/jquery.signalR.min.js | 6 +----- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/SignalR.Samples/Scripts/jquery.signalR.js b/SignalR.Samples/Scripts/jquery.signalR.js index ac39f6af1..171602da0 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.js +++ b/SignalR.Samples/Scripts/jquery.signalR.js @@ -486,7 +486,7 @@ log('SignalR: EventSource readyState: ' + connection.eventSource.readyState); - if (e.eventPhase === EventSource.CLOSED) { + if (e.eventPhase === window.EventSource.CLOSED) { // connection closed if (connection.eventSource.readyState === window.EventSource.CONNECTING) { log('[' + new Date().toTimeString() + '] SignalR: EventSource reconnecting'); diff --git a/SignalR.Samples/Scripts/jquery.signalR.min.js b/SignalR.Samples/Scripts/jquery.signalR.min.js index 429ab121a..1ccc05751 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.min.js +++ b/SignalR.Samples/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},disconnect:function(t){var i=this;return n(i).bind("onDisconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onDisconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},disconnect:function(t){var i=this;return n(i).bind("onDisconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onDisconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===t.EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",timeout:3e3,start:function(r,f,e){var s=this,c=i.foreverFrame.count+=1,h,l,o=n("");n(r).trigger("onSending"),h=i.getUrl(r,this.name),h+="&frameId="+c,o.prop("src",h),i.foreverFrame.connections[c]=r,o.bind("load",function(){u("SignalR: forever frame iframe load event fired"),s.reconnect(r)}),r.frame=o[0],r.frameId=c,f&&(r.onSuccess=f),n("body").append(o),l=t.setTimeout(function(){r.onSuccess&&(s.stop(r),e&&e())},s.timeOut)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR/Scripts/jquery.signalR.min.js b/SignalR/Scripts/jquery.signalR.min.js index 6a8070442..1ccc05751 100644 --- a/SignalR/Scripts/jquery.signalR.min.js +++ b/SignalR/Scripts/jquery.signalR.min.js @@ -1,5 +1 @@ -<<<<<<< HEAD -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},reconnect:function(t){var i=this;return n(i).bind("onReconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onReconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",timeout:3e3,start:function(r,f,e){var s=this,c=i.foreverFrame.count+=1,h,l,o=n("");n(r).trigger("onSending"),h=i.getUrl(r,this.name),h+="&frameId="+c,o.prop("src",h),i.foreverFrame.connections[c]=r,o.bind("load",function(){u("SignalR: forever frame iframe load event fired"),s.reconnect(r)}),r.frame=o[0],r.frameId=c,f&&(r.onSuccess=f),n("body").append(o),l=t.setTimeout(function(){r.onSuccess&&(s.stop(r),e&&e())},s.timeOut)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) -======= -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},disconnect:function(t){var i=this;return n(i).bind("onDisconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onDisconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",start:function(t,r){var h=this,s=i.foreverFrame.count+=1,o,e=n("");n(t).trigger("onSending"),o=i.getUrl(t,this.name),o+="&frameId="+s,e.prop("src",o),i.foreverFrame.connections[s]=t,e.bind("load",function(){u("SignalR: forever frame iframe load event fired"),h.reconnect(t)}),t.frame=e[0],t.frameId=s,r&&(t.onSuccess=r),n("body").append(e)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) ->>>>>>> Changed the reconnect event to disconnect event. +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},disconnect:function(t){var i=this;return n(i).bind("onDisconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onDisconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===t.EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",timeout:3e3,start:function(r,f,e){var s=this,c=i.foreverFrame.count+=1,h,l,o=n("");n(r).trigger("onSending"),h=i.getUrl(r,this.name),h+="&frameId="+c,o.prop("src",h),i.foreverFrame.connections[c]=r,o.bind("load",function(){u("SignalR: forever frame iframe load event fired"),s.reconnect(r)}),r.frame=o[0],r.frameId=c,f&&(r.onSuccess=f),n("body").append(o),l=t.setTimeout(function(){r.onSuccess&&(s.stop(r),e&&e())},s.timeOut)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file From 33fae17ebc6aad213e5182e394d8d74db3c395d0 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Thu, 12 Jan 2012 10:19:06 -0800 Subject: [PATCH 0356/1128] Instead of returning an empty Task from the AspNetResponse.WriteAsync method, return a task that represents the sync write operation. --- SignalR.AspNet/AspNetResponse.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/SignalR.AspNet/AspNetResponse.cs b/SignalR.AspNet/AspNetResponse.cs index b2bed9017..b78b7903c 100644 --- a/SignalR.AspNet/AspNetResponse.cs +++ b/SignalR.AspNet/AspNetResponse.cs @@ -44,8 +44,7 @@ public string ContentType public Task WriteAsync(string data) { - _context.Response.Write(data); - return TaskAsyncHelper.Empty; + return TaskAsyncHelper.FromMethod((response, value) => response.Write(value), _context.Response, data); } public Task EndAsync(string data) From 591e74995f9aad5513c082e023114b6de9c8763e Mon Sep 17 00:00:00 2001 From: DamianEdwards Date: Thu, 12 Jan 2012 11:04:21 -0800 Subject: [PATCH 0357/1128] Fixes #121 Added support for custom querystring parameters on connections --- SignalR.Samples/Scripts/jquery.signalR.js | 33 ++++++++++++++++--- SignalR.Samples/Scripts/jquery.signalR.min.js | 2 +- SignalR/Scripts/jquery.signalR.js | 33 ++++++++++++++++--- SignalR/Scripts/jquery.signalR.min.js | 2 +- 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/SignalR.Samples/Scripts/jquery.signalR.js b/SignalR.Samples/Scripts/jquery.signalR.js index 171602da0..764239dcb 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.js +++ b/SignalR.Samples/Scripts/jquery.signalR.js @@ -18,17 +18,23 @@ ? function (msg) { console.debug(msg); } : $.noop; - signalR = function (url) { + signalR = function (url, qs) { /// Creates a new SignalR connection for the given url /// The URL of the long polling endpoint + /// + /// [Optional] Custom querystring parameters to add to the connection URL. + /// If an object, every non-function member will be added to the querystring. + /// If a string, it's added to the QS as specified. + /// /// - return new signalR.fn.init(url); + return new signalR.fn.init(url, qs); }; signalR.fn = signalR.prototype = { - init: function (url) { + init: function (url, qs) { this.url = url; + this.qs = qs; }, start: function (options, callback) { @@ -232,6 +238,22 @@ // Transports var transportLogic = { + addQs: function (url, connection) { + if (!connection.qs) { + return url; + } + + if (typeof (connection.qs) === "object") { + return url + "&" + $.param(connection.qs); + } + + if (typeof (connection.qs) === "string") { + return url + "&" + connection.qs; + } + + return url + "&" + escape(connection.qs.toString()); + }, + getUrl: function (connection, transport, reconnecting) { /// Gets the url for making a GET based connect request var url = connection.url, @@ -252,11 +274,14 @@ } } url += "?" + qs; + url = this.addQs(url, connection); return url; }, ajaxSend: function (connection, data) { - $.ajax(connection.url + "/send" + "?transport=" + connection.transport.name + "&connectionId=" + window.escape(connection.id), { + var url = connection.url + "/send" + "?transport=" + connection.transport.name + "&connectionId=" + window.escape(connection.id); + url = this.addQs(url, connection); + $.ajax(url, { global: false, type: "POST", dataType: "json", diff --git a/SignalR.Samples/Scripts/jquery.signalR.min.js b/SignalR.Samples/Scripts/jquery.signalR.min.js index 1ccc05751..175498eb9 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.min.js +++ b/SignalR.Samples/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},disconnect:function(t){var i=this;return n(i).bind("onDisconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onDisconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===t.EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",timeout:3e3,start:function(r,f,e){var s=this,c=i.foreverFrame.count+=1,h,l,o=n("");n(r).trigger("onSending"),h=i.getUrl(r,this.name),h+="&frameId="+c,o.prop("src",h),i.foreverFrame.connections[c]=r,o.bind("load",function(){u("SignalR: forever frame iframe load event fired"),s.reconnect(r)}),r.frame=o[0],r.frameId=c,f&&(r.onSuccess=f),n("body").append(o),l=t.setTimeout(function(){r.onSuccess&&(s.stop(r),e&&e())},s.timeOut)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n,t){return new r.fn.init(n,t)},r.fn=r.prototype={init:function(n,t){this.url=n,this.qs=t},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},disconnect:function(t){var i=this;return n(i).bind("onDisconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={addQs:function(t,i){return i.qs?typeof i.qs=="object"?t+"&"+n.param(i.qs):typeof i.qs=="string"?t+"&"+i.qs:t+"&"+escape(i.qs.toString()):t},getUrl:function(n,i,r){var u=n.url,f="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(f+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(f+="&messageId="+n.messageId),n.groups&&(f+="&groups="+t.escape(JSON.stringify(n.groups)))):u=u+"/connect",u+="?"+f,u=this.addQs(u,n)},ajaxSend:function(i,r){var u=i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id);u=this.addQs(u,i),n.ajax(u,{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onDisconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===t.EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",timeout:3e3,start:function(r,f,e){var s=this,c=i.foreverFrame.count+=1,h,l,o=n("");n(r).trigger("onSending"),h=i.getUrl(r,this.name),h+="&frameId="+c,o.prop("src",h),i.foreverFrame.connections[c]=r,o.bind("load",function(){u("SignalR: forever frame iframe load event fired"),s.reconnect(r)}),r.frame=o[0],r.frameId=c,f&&(r.onSuccess=f),n("body").append(o),l=t.setTimeout(function(){r.onSuccess&&(s.stop(r),e&&e())},s.timeOut)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR/Scripts/jquery.signalR.js b/SignalR/Scripts/jquery.signalR.js index 171602da0..764239dcb 100644 --- a/SignalR/Scripts/jquery.signalR.js +++ b/SignalR/Scripts/jquery.signalR.js @@ -18,17 +18,23 @@ ? function (msg) { console.debug(msg); } : $.noop; - signalR = function (url) { + signalR = function (url, qs) { /// Creates a new SignalR connection for the given url /// The URL of the long polling endpoint + /// + /// [Optional] Custom querystring parameters to add to the connection URL. + /// If an object, every non-function member will be added to the querystring. + /// If a string, it's added to the QS as specified. + /// /// - return new signalR.fn.init(url); + return new signalR.fn.init(url, qs); }; signalR.fn = signalR.prototype = { - init: function (url) { + init: function (url, qs) { this.url = url; + this.qs = qs; }, start: function (options, callback) { @@ -232,6 +238,22 @@ // Transports var transportLogic = { + addQs: function (url, connection) { + if (!connection.qs) { + return url; + } + + if (typeof (connection.qs) === "object") { + return url + "&" + $.param(connection.qs); + } + + if (typeof (connection.qs) === "string") { + return url + "&" + connection.qs; + } + + return url + "&" + escape(connection.qs.toString()); + }, + getUrl: function (connection, transport, reconnecting) { /// Gets the url for making a GET based connect request var url = connection.url, @@ -252,11 +274,14 @@ } } url += "?" + qs; + url = this.addQs(url, connection); return url; }, ajaxSend: function (connection, data) { - $.ajax(connection.url + "/send" + "?transport=" + connection.transport.name + "&connectionId=" + window.escape(connection.id), { + var url = connection.url + "/send" + "?transport=" + connection.transport.name + "&connectionId=" + window.escape(connection.id); + url = this.addQs(url, connection); + $.ajax(url, { global: false, type: "POST", dataType: "json", diff --git a/SignalR/Scripts/jquery.signalR.min.js b/SignalR/Scripts/jquery.signalR.min.js index 1ccc05751..175498eb9 100644 --- a/SignalR/Scripts/jquery.signalR.min.js +++ b/SignalR/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n){return new r.fn.init(n)},r.fn=r.prototype={init:function(n){this.url=n},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},disconnect:function(t){var i=this;return n(i).bind("onDisconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={getUrl:function(n,i,r){var f=n.url,u="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(u+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(u+="&messageId="+n.messageId),n.groups&&(u+="&groups="+t.escape(JSON.stringify(n.groups)))):f=f+"/connect",f+="?"+u},ajaxSend:function(i,r){n.ajax(i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id),{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onDisconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===t.EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",timeout:3e3,start:function(r,f,e){var s=this,c=i.foreverFrame.count+=1,h,l,o=n("");n(r).trigger("onSending"),h=i.getUrl(r,this.name),h+="&frameId="+c,o.prop("src",h),i.foreverFrame.connections[c]=r,o.bind("load",function(){u("SignalR: forever frame iframe load event fired"),s.reconnect(r)}),r.frame=o[0],r.frameId=c,f&&(r.onSuccess=f),n("body").append(o),l=t.setTimeout(function(){r.onSuccess&&(s.stop(r),e&&e())},s.timeOut)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n,t){return new r.fn.init(n,t)},r.fn=r.prototype={init:function(n,t){this.url=n,this.qs=t},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},disconnect:function(t){var i=this;return n(i).bind("onDisconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={addQs:function(t,i){return i.qs?typeof i.qs=="object"?t+"&"+n.param(i.qs):typeof i.qs=="string"?t+"&"+i.qs:t+"&"+escape(i.qs.toString()):t},getUrl:function(n,i,r){var u=n.url,f="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(f+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(f+="&messageId="+n.messageId),n.groups&&(f+="&groups="+t.escape(JSON.stringify(n.groups)))):u=u+"/connect",u+="?"+f,u=this.addQs(u,n)},ajaxSend:function(i,r){var u=i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id);u=this.addQs(u,i),n.ajax(u,{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onDisconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===t.EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",timeout:3e3,start:function(r,f,e){var s=this,c=i.foreverFrame.count+=1,h,l,o=n("");n(r).trigger("onSending"),h=i.getUrl(r,this.name),h+="&frameId="+c,o.prop("src",h),i.foreverFrame.connections[c]=r,o.bind("load",function(){u("SignalR: forever frame iframe load event fired"),s.reconnect(r)}),r.frame=o[0],r.frameId=c,f&&(r.onSuccess=f),n("body").append(o),l=t.setTimeout(function(){r.onSuccess&&(s.stop(r),e&&e())},s.timeOut)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file From 13d0971e7e6a596c77d278d74f4d6932817d1040 Mon Sep 17 00:00:00 2001 From: DamianEdwards Date: Thu, 12 Jan 2012 12:23:05 -0800 Subject: [PATCH 0358/1128] fixed forever frame connect timeout logic --- SignalR.Samples/Scripts/jquery.signalR.js | 4 ++-- SignalR.Samples/Scripts/jquery.signalR.min.js | 2 +- SignalR/Scripts/jquery.signalR.js | 4 ++-- SignalR/Scripts/jquery.signalR.min.js | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/SignalR.Samples/Scripts/jquery.signalR.js b/SignalR.Samples/Scripts/jquery.signalR.js index 764239dcb..583507de5 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.js +++ b/SignalR.Samples/Scripts/jquery.signalR.js @@ -545,7 +545,7 @@ foreverFrame: { name: "foreverFrame", - timeout: 3000, + timeOut: 3000, start: function (connection, onSuccess, onFailed) { var that = this, @@ -608,7 +608,7 @@ stop: function (connection) { if (connection.frame) { - connection.frame.remove(); + $(connection.frame).remove(); delete transportLogic.foreverFrame.connections[connection.frameId]; } }, diff --git a/SignalR.Samples/Scripts/jquery.signalR.min.js b/SignalR.Samples/Scripts/jquery.signalR.min.js index 175498eb9..f7d82a3c4 100644 --- a/SignalR.Samples/Scripts/jquery.signalR.min.js +++ b/SignalR.Samples/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n,t){return new r.fn.init(n,t)},r.fn=r.prototype={init:function(n,t){this.url=n,this.qs=t},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},disconnect:function(t){var i=this;return n(i).bind("onDisconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={addQs:function(t,i){return i.qs?typeof i.qs=="object"?t+"&"+n.param(i.qs):typeof i.qs=="string"?t+"&"+i.qs:t+"&"+escape(i.qs.toString()):t},getUrl:function(n,i,r){var u=n.url,f="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(f+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(f+="&messageId="+n.messageId),n.groups&&(f+="&groups="+t.escape(JSON.stringify(n.groups)))):u=u+"/connect",u+="?"+f,u=this.addQs(u,n)},ajaxSend:function(i,r){var u=i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id);u=this.addQs(u,i),n.ajax(u,{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onDisconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===t.EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",timeout:3e3,start:function(r,f,e){var s=this,c=i.foreverFrame.count+=1,h,l,o=n("");n(r).trigger("onSending"),h=i.getUrl(r,this.name),h+="&frameId="+c,o.prop("src",h),i.foreverFrame.connections[c]=r,o.bind("load",function(){u("SignalR: forever frame iframe load event fired"),s.reconnect(r)}),r.frame=o[0],r.frameId=c,f&&(r.onSuccess=f),n("body").append(o),l=t.setTimeout(function(){r.onSuccess&&(s.stop(r),e&&e())},s.timeOut)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n,t){return new r.fn.init(n,t)},r.fn=r.prototype={init:function(n,t){this.url=n,this.qs=t},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},disconnect:function(t){var i=this;return n(i).bind("onDisconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={addQs:function(t,i){return i.qs?typeof i.qs=="object"?t+"&"+n.param(i.qs):typeof i.qs=="string"?t+"&"+i.qs:t+"&"+escape(i.qs.toString()):t},getUrl:function(n,i,r){var u=n.url,f="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(f+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(f+="&messageId="+n.messageId),n.groups&&(f+="&groups="+t.escape(JSON.stringify(n.groups)))):u=u+"/connect",u+="?"+f,u=this.addQs(u,n)},ajaxSend:function(i,r){var u=i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id);u=this.addQs(u,i),n.ajax(u,{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onDisconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===t.EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",timeOut:3e3,start:function(r,f,e){var s=this,c=i.foreverFrame.count+=1,h,l,o=n("");n(r).trigger("onSending"),h=i.getUrl(r,this.name),h+="&frameId="+c,o.prop("src",h),i.foreverFrame.connections[c]=r,o.bind("load",function(){u("SignalR: forever frame iframe load event fired"),s.reconnect(r)}),r.frame=o[0],r.frameId=c,f&&(r.onSuccess=f),n("body").append(o),l=t.setTimeout(function(){r.onSuccess&&(s.stop(r),e&&e())},s.timeOut)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(t){t.frame&&(n(t.frame).remove(),delete i.foreverFrame.connections[t.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file diff --git a/SignalR/Scripts/jquery.signalR.js b/SignalR/Scripts/jquery.signalR.js index 764239dcb..583507de5 100644 --- a/SignalR/Scripts/jquery.signalR.js +++ b/SignalR/Scripts/jquery.signalR.js @@ -545,7 +545,7 @@ foreverFrame: { name: "foreverFrame", - timeout: 3000, + timeOut: 3000, start: function (connection, onSuccess, onFailed) { var that = this, @@ -608,7 +608,7 @@ stop: function (connection) { if (connection.frame) { - connection.frame.remove(); + $(connection.frame).remove(); delete transportLogic.foreverFrame.connections[connection.frameId]; } }, diff --git a/SignalR/Scripts/jquery.signalR.min.js b/SignalR/Scripts/jquery.signalR.min.js index 175498eb9..f7d82a3c4 100644 --- a/SignalR/Scripts/jquery.signalR.min.js +++ b/SignalR/Scripts/jquery.signalR.min.js @@ -1 +1 @@ -(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n,t){return new r.fn.init(n,t)},r.fn=r.prototype={init:function(n,t){this.url=n,this.qs=t},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},disconnect:function(t){var i=this;return n(i).bind("onDisconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={addQs:function(t,i){return i.qs?typeof i.qs=="object"?t+"&"+n.param(i.qs):typeof i.qs=="string"?t+"&"+i.qs:t+"&"+escape(i.qs.toString()):t},getUrl:function(n,i,r){var u=n.url,f="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(f+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(f+="&messageId="+n.messageId),n.groups&&(f+="&groups="+t.escape(JSON.stringify(n.groups)))):u=u+"/connect",u+="?"+f,u=this.addQs(u,n)},ajaxSend:function(i,r){var u=i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id);u=this.addQs(u,i),n.ajax(u,{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onDisconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===t.EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",timeout:3e3,start:function(r,f,e){var s=this,c=i.foreverFrame.count+=1,h,l,o=n("");n(r).trigger("onSending"),h=i.getUrl(r,this.name),h+="&frameId="+c,o.prop("src",h),i.foreverFrame.connections[c]=r,o.bind("load",function(){u("SignalR: forever frame iframe load event fired"),s.reconnect(r)}),r.frame=o[0],r.frameId=c,f&&(r.onSuccess=f),n("body").append(o),l=t.setTimeout(function(){r.onSuccess&&(s.stop(r),e&&e())},s.timeOut)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(n){n.frame&&(n.frame.remove(),delete i.foreverFrame.connections[n.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file +(function(n,t){"use strict";var r,f,u,i;if(typeof n!="function")throw"SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";if(!t.JSON)throw"SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.";u=typeof console!="undefined"&&console&&console.debug?function(n){console.debug(n)}:n.noop,r=function(n,t){return new r.fn.init(n,t)},r.fn=r.prototype={init:function(n,t){this.url=n,this.qs=t},start:function(i,u){var f=this,e={transport:"auto"},o;return f.transport?f:(n.type(i)==="function"?u=i:n.type(i)==="object"&&(n.extend(e,i),n.type(e.callback)==="function"&&(u=e.callback)),n.type(u)==="function"&&n(f).bind("onStart",function(){u.call(f)}),o=function(t,i){i=i||0;if(i>=t.length){if(!f.transport)throw"SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.";return}var u=t[i],e=n.type(u)==="object"?u:r.transports[u];e.start(f,function(){f.transport=e,n(f).trigger("onStart")},function(){o(t,i+1)})},t.setTimeout(function(){n.ajax(f.url+"/negotiate",{global:!1,type:"POST",data:{},success:function(t){f.appRelativeUrl=t.Url,f.id=t.ConnectionId,f.webSocketServerUrl=t.WebSocketServerUrl;if(!t.ProtocolVersion||t.ProtocolVersion!=="1.0")throw"SignalR: Incompatible protocol version.";n(f).trigger("onStarting");var u=[],i=[];n.each(r.transports,function(n){if(n==="webSockets"&&!t.TryWebSockets)return!0;i.push(n)}),n.isArray(e.transport)?n.each(e.transport,function(){var t=this;n.type(t)!=="object"&&(n.type(t)!=="string"||n.inArray(""+t,i)<0)||u.push(n.type(t)==="string"?""+t:t)}):n.type(e.transport)!=="object"&&n.inArray(e.transport,i)<0?u=i:u.push(e.transport),o(u)}})},0),f)},starting:function(t){var i=this,r=n(i);return r.bind("onStarting",function(){t.call(i),r.unbind("onStarting")}),i},send:function(n){var t=this;if(!t.transport)throw"SignalR: Connection must be started before data can be sent. Call .start() before .send()";return t.transport.send(t,n),t},sending:function(t){var i=this;return n(i).bind("onSending",function(){t.call(i)}),i},received:function(t){var i=this;return n(i).bind("onReceived",function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind("onError",function(n,r){t.call(i,r)}),i},disconnect:function(t){var i=this;return n(i).bind("onDisconnect",function(){t.call(i)}),i},stop:function(){var n=this;return n.transport&&(n.transport.stop(n),n.transport=null),delete n.messageId,delete n.groups,n}},r.fn.init.prototype=r.fn,i={addQs:function(t,i){return i.qs?typeof i.qs=="object"?t+"&"+n.param(i.qs):typeof i.qs=="string"?t+"&"+i.qs:t+"&"+escape(i.qs.toString()):t},getUrl:function(n,i,r){var u=n.url,f="transport="+i+"&connectionId="+t.escape(n.id);return n.data&&(f+="&connectionData="+t.escape(n.data)),r?(n.messageId&&(f+="&messageId="+n.messageId),n.groups&&(f+="&groups="+t.escape(JSON.stringify(n.groups)))):u=u+"/connect",u+="?"+f,u=this.addQs(u,n)},ajaxSend:function(i,r){var u=i.url+"/send?transport="+i.transport.name+"&connectionId="+t.escape(i.id);u=this.addQs(u,i),n.ajax(u,{global:!1,type:"POST",dataType:"json",data:{data:r},success:function(t){t&&n(i).trigger("onReceived",[t])},error:function(t,r){if(r==="abort")return;n(i).trigger("onError",[t])}})},processMessages:function(t,i){var r=n(t);if(i){if(i.Disconnect){t.stop(),r.trigger("onDisconnect");return}i.Messages&&n.each(i.Messages,function(){try{r.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}),t.messageId=i.MessageId,t.groups=i.TransportData.Groups}},foreverFrame:{count:0,connections:{}}},r.transports={webSockets:{name:"webSockets",send:function(n,t){n.socket.send(t)},start:function(i,r,f){var e,s=!1,o;t.MozWebSocket&&(t.WebSocket=t.MozWebSocket);if(!t.WebSocket){f();return}i.socket||(i.webSocketServerUrl?e=i.webSocketServerUrl:(o=document.location.protocol==="https:"?"wss://":"ws://",e=o+document.location.host+i.appRelativeUrl),n(i).trigger("onSending"),e+=i.data?"?connectionData="+i.data+"&transport=webSockets&connectionId="+i.id:"?transport=webSockets&connectionId="+i.id,i.socket=new t.WebSocket(e),i.socket.onopen=function(){s=!0,r&&r()},i.socket.onclose=function(t){s?typeof t.wasClean!="undefined"&&t.wasClean===!1&&n(i).trigger("onError"):f&&f(),i.socket=null},i.socket.onmessage=function(r){var f=t.JSON.parse(r.data),e;f&&(e=n(i),f.Messages?n.each(f.Messages,function(){try{e.trigger("onReceived",[this])}catch(n){u("Error raising received "+n)}}):e.trigger("onReceived",[f]))})},stop:function(n){n.socket!==null&&(n.socket.close(),n.socket=null)}},serverSentEvents:{name:"serverSentEvents",timeOut:3e3,start:function(r,f,e){var o=this,s=!1,l=!f,c,h;r.eventSource&&r.stop();if(!t.EventSource){e();return}n(r).trigger("onSending"),c=i.getUrl(r,this.name,l);try{r.eventSource=new t.EventSource(c)}catch(a){e&&e();return}h=t.setTimeout(function(){s===!1&&(o.stop(r),e&&e())},o.timeOut),r.eventSource.addEventListener("open",function(){s===!1&&(s=!0,clearTimeout(h),f&&f())},!1),r.eventSource.addEventListener("message",function(n){if(n.data==="initialized")return;var u=t.JSON.parse(n.data);i.processMessages(r,u)},!1),r.eventSource.addEventListener("error",function(i){s||e&&e(),u("SignalR: EventSource readyState: "+r.eventSource.readyState),i.eventPhase===t.EventSource.CLOSED?r.eventSource.readyState===t.EventSource.CONNECTING?(u("["+(new Date).toTimeString()+"] SignalR: EventSource reconnecting"),o.stop(r),o.start(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource closed"),o.stop(r)):(u("["+(new Date).toTimeString()+"] SignalR: EventSource error"),n(instance).trigger("onError",[data]))},!1)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n&&n.eventSource&&(n.eventSource.close(),n.eventSource=null)}},foreverFrame:{name:"foreverFrame",timeOut:3e3,start:function(r,f,e){var s=this,c=i.foreverFrame.count+=1,h,l,o=n("");n(r).trigger("onSending"),h=i.getUrl(r,this.name),h+="&frameId="+c,o.prop("src",h),i.foreverFrame.connections[c]=r,o.bind("load",function(){u("SignalR: forever frame iframe load event fired"),s.reconnect(r)}),r.frame=o[0],r.frameId=c,f&&(r.onSuccess=f),n("body").append(o),l=t.setTimeout(function(){r.onSuccess&&(s.stop(r),e&&e())},s.timeOut)},reconnect:function(n){var r=this;t.setTimeout(function(){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;u.src=t},2e3)},send:function(n,t){i.ajaxSend(n,t)},receive:i.processMessages,stop:function(t){t.frame&&(n(t.frame).remove(),delete i.foreverFrame.connections[t.frameId])},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(n){n.onSuccess&&(n.onSuccess(),delete n.onSuccess)}},longPolling:{name:"longPolling",start:function(r,u){var e=this;r.pollXhr&&r.stop(),r.messageId=null,t.setTimeout(function(){(function f(r){n(r).trigger("onSending");var s=r.messageId,o=s===null,u=i.getUrl(r,e.name,!o);r.pollXhr=n.ajax(u,{global:!1,type:"GET",data:{connectionData:r.data},dataType:"json",success:function(u){var e=0;i.processMessages(r,u),u&&n.type(u.TransportData.LongPollDelay)==="number"&&(e=u.TransportData.LongPollDelay),e>0?t.setTimeout(function(){f(r)},e):f(r)},error:function(i,u){if(u==="abort")return;n(r).trigger("onError",[i]),t.setTimeout(function(){f(r)},2e3)}})})(r),setTimeout(u,150)},250)},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null)}}},r.noConflict=function(){return n.connection===r&&(n.connection=f),r},n.connection&&(f=n.connection),n.connection=n.signalR=r})(window.jQuery,window) \ No newline at end of file From 777fc0332b41c99f9f70e86241a54e981b415a5a Mon Sep 17 00:00:00 2001 From: David Fowler Date: Fri, 13 Jan 2012 00:39:33 -0800 Subject: [PATCH 0359/1128] Added WP7 sample. --- SignalR.Client.WP7.Sample/App.xaml | 19 ++ SignalR.Client.WP7.Sample/App.xaml.cs | 166 ++++++++++++++++++ SignalR.Client.WP7.Sample/ApplicationIcon.png | Bin 0 -> 1881 bytes SignalR.Client.WP7.Sample/Background.png | Bin 0 -> 3521 bytes SignalR.Client.WP7.Sample/DetailsPage.xaml | 48 +++++ SignalR.Client.WP7.Sample/DetailsPage.xaml.cs | 36 ++++ SignalR.Client.WP7.Sample/MainPage.xaml | 57 ++++++ SignalR.Client.WP7.Sample/MainPage.xaml.cs | 58 ++++++ .../Properties/AppManifest.xml | 6 + .../Properties/AssemblyInfo.cs | 37 ++++ .../Properties/WMAppManifest.xml | 35 ++++ .../SampleData/MainViewModelSampleData.xaml | 16 ++ .../SignalR.Client.WP7.Sample.csproj | 128 ++++++++++++++ .../SplashScreenImage.jpg | Bin 0 -> 9417 bytes .../ViewModels/ItemViewModel.cs | 91 ++++++++++ .../ViewModels/MainViewModel.cs | 68 +++++++ SignalR.Client.WP7.Sample/packages.config | 4 + SignalR.Client.WP7/SignalR.Client.WP7.csproj | 5 +- SignalR.Client.WP7/packages.config | 2 +- .../SignalR.Client.WP71.csproj | 5 +- SignalR.Client.WP71/packages.config | 2 +- SignalR.WP7.sln | 26 +++ 22 files changed, 803 insertions(+), 6 deletions(-) create mode 100644 SignalR.Client.WP7.Sample/App.xaml create mode 100644 SignalR.Client.WP7.Sample/App.xaml.cs create mode 100644 SignalR.Client.WP7.Sample/ApplicationIcon.png create mode 100644 SignalR.Client.WP7.Sample/Background.png create mode 100644 SignalR.Client.WP7.Sample/DetailsPage.xaml create mode 100644 SignalR.Client.WP7.Sample/DetailsPage.xaml.cs create mode 100644 SignalR.Client.WP7.Sample/MainPage.xaml create mode 100644 SignalR.Client.WP7.Sample/MainPage.xaml.cs create mode 100644 SignalR.Client.WP7.Sample/Properties/AppManifest.xml create mode 100644 SignalR.Client.WP7.Sample/Properties/AssemblyInfo.cs create mode 100644 SignalR.Client.WP7.Sample/Properties/WMAppManifest.xml create mode 100644 SignalR.Client.WP7.Sample/SampleData/MainViewModelSampleData.xaml create mode 100644 SignalR.Client.WP7.Sample/SignalR.Client.WP7.Sample.csproj create mode 100644 SignalR.Client.WP7.Sample/SplashScreenImage.jpg create mode 100644 SignalR.Client.WP7.Sample/ViewModels/ItemViewModel.cs create mode 100644 SignalR.Client.WP7.Sample/ViewModels/MainViewModel.cs create mode 100644 SignalR.Client.WP7.Sample/packages.config diff --git a/SignalR.Client.WP7.Sample/App.xaml b/SignalR.Client.WP7.Sample/App.xaml new file mode 100644 index 000000000..780aa2cd1 --- /dev/null +++ b/SignalR.Client.WP7.Sample/App.xaml @@ -0,0 +1,19 @@ + + + + + + + + + + + + diff --git a/SignalR.Client.WP7.Sample/App.xaml.cs b/SignalR.Client.WP7.Sample/App.xaml.cs new file mode 100644 index 000000000..dc0760697 --- /dev/null +++ b/SignalR.Client.WP7.Sample/App.xaml.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Navigation; +using System.Windows.Shapes; +using Microsoft.Phone.Controls; +using Microsoft.Phone.Shell; + +namespace SignalR.Client.WP7.Sample +{ + public partial class App : Application + { + private static MainViewModel viewModel = null; + + /// + /// A static ViewModel used by the views to bind against. + /// + /// The MainViewModel object. + public static MainViewModel ViewModel + { + get + { + // Delay creation of the view model until necessary + if (viewModel == null) + viewModel = new MainViewModel(); + + return viewModel; + } + } + + /// + /// Provides easy access to the root frame of the Phone Application. + /// + /// The root frame of the Phone Application. + public PhoneApplicationFrame RootFrame { get; private set; } + + /// + /// Constructor for the Application object. + /// + public App() + { + // Global handler for uncaught exceptions. + UnhandledException += Application_UnhandledException; + + // Standard Silverlight initialization + InitializeComponent(); + + // Phone-specific initialization + InitializePhoneApplication(); + + // Show graphics profiling information while debugging. + if (System.Diagnostics.Debugger.IsAttached) + { + // Display the current frame rate counters. + Application.Current.Host.Settings.EnableFrameRateCounter = true; + + // Show the areas of the app that are being redrawn in each frame. + //Application.Current.Host.Settings.EnableRedrawRegions = true; + + // Enable non-production analysis visualization mode, + // which shows areas of a page that are handed off GPU with a colored overlay. + //Application.Current.Host.Settings.EnableCacheVisualization = true; + + // Disable the application idle detection by setting the UserIdleDetectionMode property of the + // application's PhoneApplicationService object to Disabled. + // Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run + // and consume battery power when the user is not using the phone. + PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled; + } + + } + + // Code to execute when the application is launching (eg, from Start) + // This code will not execute when the application is reactivated + private void Application_Launching(object sender, LaunchingEventArgs e) + { + } + + // Code to execute when the application is activated (brought to foreground) + // This code will not execute when the application is first launched + private void Application_Activated(object sender, ActivatedEventArgs e) + { + // Ensure that application state is restored appropriately + //if (!App.ViewModel.IsDataLoaded) + //{ + // App.ViewModel.LoadData(); + //} + } + + // Code to execute when the application is deactivated (sent to background) + // This code will not execute when the application is closing + private void Application_Deactivated(object sender, DeactivatedEventArgs e) + { + // Ensure that required application state is persisted here. + } + + // Code to execute when the application is closing (eg, user hit Back) + // This code will not execute when the application is deactivated + private void Application_Closing(object sender, ClosingEventArgs e) + { + } + + // Code to execute if a navigation fails + private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e) + { + if (System.Diagnostics.Debugger.IsAttached) + { + // A navigation has failed; break into the debugger + System.Diagnostics.Debugger.Break(); + } + } + + // Code to execute on Unhandled Exceptions + private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e) + { + if (System.Diagnostics.Debugger.IsAttached) + { + // An unhandled exception has occurred; break into the debugger + System.Diagnostics.Debugger.Break(); + } + } + + #region Phone application initialization + + // Avoid double-initialization + private bool phoneApplicationInitialized = false; + + // Do not add any additional code to this method + private void InitializePhoneApplication() + { + if (phoneApplicationInitialized) + return; + + // Create the frame but don't set it as RootVisual yet; this allows the splash + // screen to remain active until the application is ready to render. + RootFrame = new PhoneApplicationFrame(); + RootFrame.Navigated += CompleteInitializePhoneApplication; + + // Handle navigation failures + RootFrame.NavigationFailed += RootFrame_NavigationFailed; + + // Ensure we don't initialize again + phoneApplicationInitialized = true; + } + + // Do not add any additional code to this method + private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e) + { + // Set the root visual to allow the application to render + if (RootVisual != RootFrame) + RootVisual = RootFrame; + + // Remove this handler since it is no longer needed + RootFrame.Navigated -= CompleteInitializePhoneApplication; + } + + #endregion + } +} \ No newline at end of file diff --git a/SignalR.Client.WP7.Sample/ApplicationIcon.png b/SignalR.Client.WP7.Sample/ApplicationIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..5859393ca1056103ba35d225773352a9fa3ab754 GIT binary patch literal 1881 zcmV-f2d4OmP)GXLPE9uD;d|3K9jFs0^O3no;*#kUHY6H}PH_^L3*aj} zl+rSw?L`W0p^tkju#a}u3PxyqFY3}?ZpgV`?z#P)?|kQbU67g4(Jt>MC@$|ToYbAv zoz$Jwoz$JwUyrEBh@^xBYY9T32$%4g4Eozd9YH8m>Im7iz@UwrH+y+`StMa0!Jlv1 zOs9{eP8~IOLbaYWZ(elNbZg1v$&=#~5`=udMY3Y~^2n$tPDrTrV}g_vPDsd`qdqj` z?(SZ2=x{`MxJ4Ea7yy62*t!)g2L{u*+?5%tV8G+?EYFApOLBK?H(-W0sdbrL7RKcs zEi7D+mK#@=wJ2&3^B#Z6SE|tW^ z&fa_A0KDdD7g}CkuGQ%*uYy_6tlm5}8JiaBN~M03ks%g~tMA@5>&#%lv|6osRa0w$GfX-lmv6;tg7E35t(xdFTQCUeAbpwWpTg5+|K7IY# zwbIg(hK5FUUmu3O*mrLl?KRUayoa}&LPcB<-$|3+(JUi7*{UTR~?sv$M1P1N>*rj)mLHrKNHON-lLA!%9m^1R@cPXUv=l zmwktfMx&WuVn$tE?SL(YK0XhFAR`119z6nm6Brlagxr7dz}8t~Yn@7=%#V+Eb8|(} zt{zpd`CNjX*sbr;=t5DIS`Dt#+slj1W=)+krM;u$iMZ8K>adS$jr!2xBjD^>TUvBF z9d0yoxm*IZ?l;-5Ain@y6NACvgojsGRpsU7j%^L&Y-U7+haE2}3J&Fvoey?)v}dhb zCu#eKuxQ%SD*0;n9-YaBlZv8a$Hv3kwrJWRlMUc_^1_AW@h&SVIeq4*!NEa-a~Q~4 z+oV!0nTl_2YVx!S#tzx~^ayTn&@|YYz@VU6F)=fuXV94}i1{Q@*QnG@O^x-nwNi;h z*`t)BXsbkOr-$)j`>g(C&HN({S~o10_f z;%IdG#4O<1vuEG#-(Pg%q)ef3=$L{Rf85e?x28HGf*TMRL^f|}YwPyhobszzVFAQ= zm~N%=UR|9)Ab@0*RDI`VXWyx=A<{WQ0h*baId@(Hx%~+Wg~envH8%FDRE|=IfS_Gc9lLglmM*of63B)(jlMWFl^Yh` z-PHwg&`!V3goKDr=Y)nNCGiA8A;+rnCkfqM=&e6)R#x6>e)2@4>Few7cQpMPZm{_I z@>5c?^YUI_#W{jAr%qkC_-ltu?lLq)WR>)^Tj}QN>OSs3Z67*1WyCi#!0z{cbAY@o z006~x)1!xywl>13BNU2g3IFKvB5+=0+j?T+{J*NJA&d|~9YF+(7w2r-hRPK^N~OV| z*K2jK7)uw$abO06sz<3-X}VB_(P)g1kH1%6r&4Pit&*rzYH08@48w6FjvxpyMqH5G z+#G(IkSOXBu{bAtE4UNjJv2Dv?d^r*xNNN6#<*(JR_RN|$7Zqo{R0Rz5+7e*D63_1 z1<}onQAk;|C^{yFKD+Wz{HrNFFf|SuX0K6dp!F8pg;@r>Y=rsCpr)?Z@)E?>v3>(o$ zrqR3^&og+a=b!w5N57jd;8|i|GIMu#V_YC94Rc^kd-O#lzDi#4-5>B z`F?^uj5UIS0&A+OHhi}J_wsTa!}NN6eQllj6z=lWJG4TH)* z?`+z*0Yy=!W5up*A7no6n!nw*wo>MkzI-d?@orCP?%cM_FCIaX9;>6gpovZ%<&lG0MhzAcYIoOgsb z0n|ss&CTu6!-opA%OZibfuXl|${hS;XMd^jw!!)D4NmG#>Q3rT>i;k5{|Ybw$j9Fp Tu#Z&K00000NkvXXu0mjfnP|DN literal 0 HcmV?d00001 diff --git a/SignalR.Client.WP7.Sample/Background.png b/SignalR.Client.WP7.Sample/Background.png new file mode 100644 index 0000000000000000000000000000000000000000..e46f21d9407f1ae4d6fedcf793778f32bae3f06d GIT binary patch literal 3521 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 literal 0 HcmV?d00001 diff --git a/SignalR.Client.WP7.Sample/DetailsPage.xaml b/SignalR.Client.WP7.Sample/DetailsPage.xaml new file mode 100644 index 000000000..57aebfd3f --- /dev/null +++ b/SignalR.Client.WP7.Sample/DetailsPage.xaml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SignalR.Client.WP7.Sample/DetailsPage.xaml.cs b/SignalR.Client.WP7.Sample/DetailsPage.xaml.cs new file mode 100644 index 000000000..c5488c3d2 --- /dev/null +++ b/SignalR.Client.WP7.Sample/DetailsPage.xaml.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using System.Windows.Navigation; +using Microsoft.Phone.Controls; + +namespace SignalR.Client.WP7.Sample +{ + public partial class DetailsPage : PhoneApplicationPage + { + // Constructor + public DetailsPage() + { + InitializeComponent(); + } + + // When page is navigated to set data context to selected item in list + protected override void OnNavigatedTo(NavigationEventArgs e) + { + string selectedIndex = ""; + if (NavigationContext.QueryString.TryGetValue("selectedItem", out selectedIndex)) + { + int index = int.Parse(selectedIndex); + DataContext = App.ViewModel.Items[index]; + } + } + } +} \ No newline at end of file diff --git a/SignalR.Client.WP7.Sample/MainPage.xaml b/SignalR.Client.WP7.Sample/MainPage.xaml new file mode 100644 index 000000000..3d36436d5 --- /dev/null +++ b/SignalR.Client.WP7.Sample/MainPage.xaml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SignalR.Client.WP7.Sample/MainPage.xaml.cs b/SignalR.Client.WP7.Sample/MainPage.xaml.cs new file mode 100644 index 000000000..3a67ed67e --- /dev/null +++ b/SignalR.Client.WP7.Sample/MainPage.xaml.cs @@ -0,0 +1,58 @@ +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using Microsoft.Phone.Controls; +using SignalR.Client.Transports; + +namespace SignalR.Client.WP7.Sample +{ + public partial class MainPage : PhoneApplicationPage + { + // Constructor + public MainPage() + { + InitializeComponent(); + + // Set the data context of the listbox control to the sample data + DataContext = App.ViewModel; + this.Loaded += new RoutedEventHandler(MainPage_Loaded); + + var connection = new Connection("http://localhost:40476/Streaming/streaming"); + connection.Received += data => + { + Dispatcher.BeginInvoke(() => + { + App.ViewModel.Items.Add(new ItemViewModel { LineOne = data }); + }); + }; + + connection.Start(Transport.LongPolling).ContinueWith(task => + { + Debug.WriteLine("ERROR: {0}", task.Exception.GetBaseException().Message); + }, + TaskContinuationOptions.OnlyOnFaulted); + } + + // Handle selection changed on ListBox + private void MainListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + // If selected index is -1 (no selection) do nothing + if (MainListBox.SelectedIndex == -1) + return; + + // Navigate to the new page + NavigationService.Navigate(new Uri("/DetailsPage.xaml?selectedItem=" + MainListBox.SelectedIndex, UriKind.Relative)); + + // Reset selected index to -1 (no selection) + MainListBox.SelectedIndex = -1; + } + + // Load data for the ViewModel Items + private void MainPage_Loaded(object sender, RoutedEventArgs e) + { + + } + } +} \ No newline at end of file diff --git a/SignalR.Client.WP7.Sample/Properties/AppManifest.xml b/SignalR.Client.WP7.Sample/Properties/AppManifest.xml new file mode 100644 index 000000000..6712a1178 --- /dev/null +++ b/SignalR.Client.WP7.Sample/Properties/AppManifest.xml @@ -0,0 +1,6 @@ + + + + diff --git a/SignalR.Client.WP7.Sample/Properties/AssemblyInfo.cs b/SignalR.Client.WP7.Sample/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..ecee015af --- /dev/null +++ b/SignalR.Client.WP7.Sample/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Resources; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SignalR.Client.WP7.Sample")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("SignalR.Client.WP7.Sample")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2012")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c80a194d-5d64-46f3-b074-6a7a77b82189")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: NeutralResourcesLanguageAttribute("en-US")] diff --git a/SignalR.Client.WP7.Sample/Properties/WMAppManifest.xml b/SignalR.Client.WP7.Sample/Properties/WMAppManifest.xml new file mode 100644 index 000000000..d0a08aeea --- /dev/null +++ b/SignalR.Client.WP7.Sample/Properties/WMAppManifest.xml @@ -0,0 +1,35 @@ + + + + + ApplicationIcon.png + + + + + + + + + + + + + + + + + + + + + + + Background.png + 0 + SignalR.Client.WP7.Sample + + + + + diff --git a/SignalR.Client.WP7.Sample/SampleData/MainViewModelSampleData.xaml b/SignalR.Client.WP7.Sample/SampleData/MainViewModelSampleData.xaml new file mode 100644 index 000000000..1dc42d3c6 --- /dev/null +++ b/SignalR.Client.WP7.Sample/SampleData/MainViewModelSampleData.xaml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/SignalR.Client.WP7.Sample/SignalR.Client.WP7.Sample.csproj b/SignalR.Client.WP7.Sample/SignalR.Client.WP7.Sample.csproj new file mode 100644 index 000000000..72a51ce79 --- /dev/null +++ b/SignalR.Client.WP7.Sample/SignalR.Client.WP7.Sample.csproj @@ -0,0 +1,128 @@ + + + + Debug + AnyCPU + 10.0.20506 + 2.0 + {E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF} + {C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + SignalR.Client.WP7.Sample + SignalR.Client.WP7.Sample + v4.0 + $(TargetFrameworkVersion) + WindowsPhone71 + Silverlight + true + + + true + true + SignalR.Client.WP7.Sample.xap + Properties\AppManifest.xml + SignalR.Client.WP7.Sample.App + true + true + ..\..\SignalR\ + true + + + true + full + false + Bin\Debug + DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE + true + true + prompt + 4 + + + pdbonly + true + Bin\Release + TRACE;SILVERLIGHT;WINDOWS_PHONE + true + true + prompt + 4 + + + + + + ..\packages\System.Threading.Tasks.2.1.2\lib\sl4-wp71\System.Threading.Tasks.WP71.dll + + + + + + + + + + + App.xaml + + + DetailsPage.xaml + + + MainPage.xaml + + + + + + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + + + + + + + + PreserveNewest + + + + PreserveNewest + + + + + {1B2BD09D-ECFF-427F-BA58-C59A01EE6A2C} + SignalR.Client.WP71 + + + + + + + + \ No newline at end of file diff --git a/SignalR.Client.WP7.Sample/SplashScreenImage.jpg b/SignalR.Client.WP7.Sample/SplashScreenImage.jpg new file mode 100644 index 0000000000000000000000000000000000000000..353b1927b9d397aac7f23098e427739615db19fd GIT binary patch literal 9417 zcmeH}dpK0v|HtDPw1DzpdZ%obNxsKYq{ma-Qe=JhS%m*?Xpd@ukwIs*Fu0FVSw2n-+r=Mb=+L2UaoABxxu zAiuvCgDVXH_zuX!cENe`_qMQ00C1KE#J{&$&!hj5hl}g!G;Hh5zCTX{;3J~-ezJDk z8DpepqGt?`W&*HUAKSl_)}8!I4%R;w{tC!TB6lKYkci!Ym^=b0j}X!THF)$A|LYqT z@jW5Lkm3>>P?Ay`rQrq*n*lKd5-BE*l#mdI1A$0@>j80j3G^-l%MA+70jS;Q6%8-v z+?P~8`m|liWdPJLI&&deYNN8smaQ1gJz9IU_ZgcUG(B|K%<7o6jqPzedsjDi4^J;| zpTMBA!67(&XjC*ICYBg?@ygYdYpH4J8M(J^=aKV&xl>T`;Nhdvvhv3jwRQCkjZM#< zzv$?sb#?c=eD!*8XqZ027#$mDvS#1Uefa(3r+M}tU%swzR-tce>$(sC^3Ra{pSt8> zU1H+mNO9Dee<1@p zmpZ1D+4@=Lwk1TX_Ho%7R%-m~! zQ-|56f$_1fO+ybENjnH(u)A6Q9c&<9%Z`oZDd}_4>G5*2lSey-e1>3G)UjF_xgJuz ztM{oY&x22Rj%@2i#tt#aeDx-}x-3k$JnX2p`KZg`SzKt45J)B`b_f9}bzcQeI9#$x z>^EC3Jag>`Nl7q@>2Bp(5u3dBz4^fP92_t=DDkrY^#fkfJz}& z`q0hoSmA-LY`HW-@4^iyZ-zZ>y7$9^xVc>KExCP; z3726V3{BEZ3F!%5JQ;hgcewEKAf)TYG>TqXy{+fezm|pB@&Lheisd zLMm{z%XTGDAgJXHS=M~h?Uas0=YAS{-F=8Tp>8`?0mj}bj zTb|jQV4*|#0@tnF(?Vd9N?Oq6p=-L#fVD52W_~IOr$73j>qSUvn;{h1?IcUA7@nuf z2=+nx?v3o&j>wEewP?HQmCpe5yS*XahyM2`4vK?5`Ax%%o#ic0uk1Vd-i+_YPr*i1 zBReokj72;*czhnKOgf#NX*z%xXn=Ez)92W>i{Dsb9V2U=J#v@Vss~T`68&iw%FTE5 z8>*FGQf{$)cSTm~RXr^7pXT**e-^xK+t1G+;jjwvTiF{b@vOJ9tyy`qPAUFq<6=E0 zxp-H|ku?$Z7*gvQm!P2?BXx`FN=@@(w;R4}^l2!4aK^i>Pc_NnSV_iS;4d)bij_ce z6}0M5QxpQ{WK3C3UB1SlSfmgLnihkmIaN@rIQWfh`a-V#uIvJBO*ZA?@~enlY}9q# zdQ}Tt#FidH5aY>~M;?Tl6d{1C$tp7CmnI(Oxk2C9%7Sp)Zmd#E7=_sBnd|N6Rwo#4 z$#sy~@wPAcz^4M`g8g5^_tBm5@uj0a6UUSf-Ig1E3uy)QGK---wmju8EX{(vk+0KN zNwq4SVcr9zgdOSZ%vnk^GaKDOIqVg2E__!OGjV}t5uuVzdjaSyMM8Uecn=*oGD4v4 zCI1d}B?{7O)2^wf&J+$=MG6548U9s@UhuW?DYXd|4vu^&Gd(a_a(DKu{uPW}aDm71 zk_;1xB0E)QMde^X;Rm&FQag?M$1iUuP=Y0{5b%zi$xGB`EZg$76PU%DasHqubv)8z z#5lpqamYO6K|G4BlA>Ru`|uirW~N%a$Ok(+OfORMjFR%n9s(_pnij2HmR8&I7`Mu) zI!0bbF zu_c?9vDVDl!7p2>8J*@-LIzyxPs%lqC*m#GL59Sd&iUzPwr7$_Z&o=VqjfkqOtou2aJ%D7LCHS|B zU22~F3~Bwo zu&>IXTMmOJVm~yVo<0S0^|P`5XoV2qJbenyanU?aF#85im1|Et`gT?=yG4SDqqy<* zLzh`8jdX&;r4J9%6dD#%`bNNYo==XRM3cz)-_#%bl3j- zx@Wz&&537sEg>yOT3jhO!B%i8KY>C_rD5)7W#0cdezqn5Vpe5kXsm%ZevNRWYTw{* zzEi&aO06vhkK;6R+-)2a`{CYg^}ZI5!m7^l@4|Z)IdKbpzdkQvCftKG?!=)FN1uGQ zd~TYP$^MWklbs^i>KGe#q?0&};vvm1{&er5Im&aG2hmiVPj%l@OCNT&1{D7SQ(Ak) zRTYdid1NQ+d9!#eZuU&D|E3Ic%kyc!5EOS75oN zF0Q+vdyW34hHJ(PVTUB?60}@vAjdt8j6^l4bmz;UCbn&oxpia(WaXEIduORWk1=QL z-auqMOEWN8+e`&L{p3rTa6< z#h389yo`yUXV=D+G%b&xkZ=ch$7^uVR=DpdeyMqQ8P5ztfi>OJN-crq{o9>C?T~#} zW8z<{ab0mLbalYScv9}i)FaCT$1}@4KQ&z6s;3wDgAdhp7kq9>hkG|PH>{2WKMN#N z!AwVnO4P(;ydA&B!i;xjK)tZScnZU|NWljZHvT=LF*iVg;yH)P-Ucz@J?AR_iG?;~ zZa=iNO{V*i7aE~AExt5 zUrCr@ZS4sZ9ph09stY8k0dcHKsxlG9xe1Tzkwe)je47loni5ezjp1$Pr|T1Rw34}9 zZ95t2Di|ycn>Fs++D9H&zI8gnSWRkLrS(}Z{(ky0eA?pvhQ8KJ#$?KFmPf5pK?)W=_QKorGU6lW0wA_hbZh!_wtAYwqo UfQSJR10n`Q4E%c;KnlnH2jCM6ivR!s literal 0 HcmV?d00001 diff --git a/SignalR.Client.WP7.Sample/ViewModels/ItemViewModel.cs b/SignalR.Client.WP7.Sample/ViewModels/ItemViewModel.cs new file mode 100644 index 000000000..09f55b9bf --- /dev/null +++ b/SignalR.Client.WP7.Sample/ViewModels/ItemViewModel.cs @@ -0,0 +1,91 @@ +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +namespace SignalR.Client.WP7.Sample +{ + public class ItemViewModel : INotifyPropertyChanged + { + private string _lineOne; + /// + /// Sample ViewModel property; this property is used in the view to display its value using a Binding. + /// + /// + public string LineOne + { + get + { + return _lineOne; + } + set + { + if (value != _lineOne) + { + _lineOne = value; + NotifyPropertyChanged("LineOne"); + } + } + } + + private string _lineTwo; + /// + /// Sample ViewModel property; this property is used in the view to display its value using a Binding. + /// + /// + public string LineTwo + { + get + { + return _lineTwo; + } + set + { + if (value != _lineTwo) + { + _lineTwo = value; + NotifyPropertyChanged("LineTwo"); + } + } + } + + private string _lineThree; + /// + /// Sample ViewModel property; this property is used in the view to display its value using a Binding. + /// + /// + public string LineThree + { + get + { + return _lineThree; + } + set + { + if (value != _lineThree) + { + _lineThree = value; + NotifyPropertyChanged("LineThree"); + } + } + } + + public event PropertyChangedEventHandler PropertyChanged; + private void NotifyPropertyChanged(String propertyName) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (null != handler) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + } +} \ No newline at end of file diff --git a/SignalR.Client.WP7.Sample/ViewModels/MainViewModel.cs b/SignalR.Client.WP7.Sample/ViewModels/MainViewModel.cs new file mode 100644 index 000000000..52e3cde35 --- /dev/null +++ b/SignalR.Client.WP7.Sample/ViewModels/MainViewModel.cs @@ -0,0 +1,68 @@ +using System; +using System.ComponentModel; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using System.Collections.ObjectModel; + + +namespace SignalR.Client.WP7.Sample +{ + public class MainViewModel : INotifyPropertyChanged + { + public MainViewModel() + { + this.Items = new ObservableCollection(); + } + + /// + /// A collection for ItemViewModel objects. + /// + public ObservableCollection Items { get; private set; } + + private string _sampleProperty = "Sample Runtime Property Value"; + /// + /// Sample ViewModel property; this property is used in the view to display its value using a Binding + /// + /// + public string SampleProperty + { + get + { + return _sampleProperty; + } + set + { + if (value != _sampleProperty) + { + _sampleProperty = value; + NotifyPropertyChanged("SampleProperty"); + } + } + } + + public bool IsDataLoaded + { + get; + private set; + } + + public event PropertyChangedEventHandler PropertyChanged; + private void NotifyPropertyChanged(String propertyName) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (null != handler) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + } +} \ No newline at end of file diff --git a/SignalR.Client.WP7.Sample/packages.config b/SignalR.Client.WP7.Sample/packages.config new file mode 100644 index 000000000..d29be409b --- /dev/null +++ b/SignalR.Client.WP7.Sample/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SignalR.Client.WP7/SignalR.Client.WP7.csproj b/SignalR.Client.WP7/SignalR.Client.WP7.csproj index 880487713..3bd0a6309 100644 --- a/SignalR.Client.WP7/SignalR.Client.WP7.csproj +++ b/SignalR.Client.WP7/SignalR.Client.WP7.csproj @@ -44,8 +44,9 @@ 4 - - ..\packages\Newtonsoft.Json.4.0.3\lib\sl3-wp\Newtonsoft.Json.dll + + False + ..\packages\Newtonsoft.Json.4.0.4\lib\sl3-wp\Newtonsoft.Json.dll ..\packages\System.Threading.Tasks.2.1.2\lib\sl3-wp\System.Threading.Tasks.WP7.dll diff --git a/SignalR.Client.WP7/packages.config b/SignalR.Client.WP7/packages.config index d13905e52..1ae7641d5 100644 --- a/SignalR.Client.WP7/packages.config +++ b/SignalR.Client.WP7/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/SignalR.Client.WP71/SignalR.Client.WP71.csproj b/SignalR.Client.WP71/SignalR.Client.WP71.csproj index fdb7a9851..b8efa82ea 100644 --- a/SignalR.Client.WP71/SignalR.Client.WP71.csproj +++ b/SignalR.Client.WP71/SignalR.Client.WP71.csproj @@ -43,8 +43,9 @@ 4 - - ..\packages\Newtonsoft.Json.4.0.3\lib\sl3-wp\Newtonsoft.Json.dll + + False + ..\packages\Newtonsoft.Json.4.0.4\lib\sl3-wp\Newtonsoft.Json.dll ..\packages\System.Threading.Tasks.2.1.2\lib\sl4-wp71\System.Threading.Tasks.WP71.dll diff --git a/SignalR.Client.WP71/packages.config b/SignalR.Client.WP71/packages.config index d13905e52..1ae7641d5 100644 --- a/SignalR.Client.WP71/packages.config +++ b/SignalR.Client.WP71/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/SignalR.WP7.sln b/SignalR.WP7.sln index 9bce1960a..db6806fae 100644 --- a/SignalR.WP7.sln +++ b/SignalR.WP7.sln @@ -18,6 +18,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.Client.WP7", "Signa EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.Client.WP71", "SignalR.Client.WP71\SignalR.Client.WP71.csproj", "{1B2BD09D-ECFF-427F-BA58-C59A01EE6A2C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.Client.WP7.Sample", "SignalR.Client.WP7.Sample\SignalR.Client.WP7.Sample.csproj", "{E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.Samples", "SignalR.Samples\SignalR.Samples.csproj", "{1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.ScaleOut", "SignalR.ScaleOut\SignalR.ScaleOut.csproj", "{32D16B36-970E-4CF2-B954-78CB1833CBC1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.AspNet", "SignalR.AspNet\SignalR.AspNet.csproj", "{0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -40,6 +48,24 @@ Global {1B2BD09D-ECFF-427F-BA58-C59A01EE6A2C}.Debug|Any CPU.Build.0 = Debug|Any CPU {1B2BD09D-ECFF-427F-BA58-C59A01EE6A2C}.Release|Any CPU.ActiveCfg = Release|Any CPU {1B2BD09D-ECFF-427F-BA58-C59A01EE6A2C}.Release|Any CPU.Build.0 = Release|Any CPU + {E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Release|Any CPU.Build.0 = Release|Any CPU + {E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Release|Any CPU.Deploy.0 = Release|Any CPU + {1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Release|Any CPU.Build.0 = Release|Any CPU + {32D16B36-970E-4CF2-B954-78CB1833CBC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32D16B36-970E-4CF2-B954-78CB1833CBC1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32D16B36-970E-4CF2-B954-78CB1833CBC1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32D16B36-970E-4CF2-B954-78CB1833CBC1}.Release|Any CPU.Build.0 = Release|Any CPU + {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 17ba03a829d5c92d088ae19830d9111379d7d12a Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sat, 31 Dec 2011 14:18:27 -0800 Subject: [PATCH 0360/1128] Initial owin host for signalr. --- SignalR.Owin/OwinExtensions.cs | 12 +++ SignalR.Owin/OwinHost.cs | 86 ++++++++++++++++++ SignalR.Owin/OwinRequest.cs | 113 ++++++++++++++++++++++++ SignalR.Owin/OwinResponse.cs | 70 +++++++++++++++ SignalR.Owin/Properties/AssemblyInfo.cs | 10 +++ SignalR.Owin/SignalR.Owin.csproj | 75 ++++++++++++++++ SignalR.Owin/packages.config | 4 + SignalR.sln | 6 ++ SignalR/SignalR.csproj | 4 +- 9 files changed, 379 insertions(+), 1 deletion(-) create mode 100644 SignalR.Owin/OwinExtensions.cs create mode 100644 SignalR.Owin/OwinHost.cs create mode 100644 SignalR.Owin/OwinRequest.cs create mode 100644 SignalR.Owin/OwinResponse.cs create mode 100644 SignalR.Owin/Properties/AssemblyInfo.cs create mode 100644 SignalR.Owin/SignalR.Owin.csproj create mode 100644 SignalR.Owin/packages.config diff --git a/SignalR.Owin/OwinExtensions.cs b/SignalR.Owin/OwinExtensions.cs new file mode 100644 index 000000000..e433a36d9 --- /dev/null +++ b/SignalR.Owin/OwinExtensions.cs @@ -0,0 +1,12 @@ +using Gate; + +namespace SignalR.Owin +{ + public static class OwinExtensions + { + public static IAppBuilder MapConnection(this IAppBuilder builder, string path) where T : PersistentConnection + { + return builder.Map(path, Delegates.ToDelegate(new OwinHost().ProcessRequest)); + } + } +} diff --git a/SignalR.Owin/OwinHost.cs b/SignalR.Owin/OwinHost.cs new file mode 100644 index 000000000..8e115ef9b --- /dev/null +++ b/SignalR.Owin/OwinHost.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using SignalR.Abstractions; +using SignalR.Infrastructure; + +namespace SignalR.Owin +{ + using BodyDelegate = System.Func, // data + System.Action, // continuation + bool>, // continuation will be invoked + System.Action, // onError + System.Action, // on Complete + System.Action>; + + using ResponseCallBack = System.Action, System.Func, System.Action, bool>, System.Action, System.Action, System.Action>>; + + public class OwinHost + { + public void ProcessRequest(IDictionary environment, ResponseCallBack responseCallback, Action fault) + { + // Read the request body then process the request. + ParseBodyAsync(environment).ContinueWith(task => + { + if (task.IsFaulted) + { + // There was an error reading the body + fault(task.Exception); + } + else + { + var request = new OwinRequest(environment, task.Result); + var response = new OwinResponse(responseCallback); + var hostContext = new HostContext(request, response, null); + + try + { + var factory = DependencyResolver.Resolve(); + PersistentConnection connection = factory.CreateInstance(typeof(T)); + + connection.ProcessRequestAsync(hostContext).ContinueWith(innerTask => + { + fault(innerTask.Exception); + }, + TaskContinuationOptions.OnlyOnFaulted); + } + catch (Exception ex) + { + fault(ex); + } + } + }); + } + + private static Task ParseBodyAsync(IDictionary environment) + { + var requestBodyDelegate = GetRequestBodyDelegate(environment); + + var tcs = new TaskCompletionSource(); + if (requestBodyDelegate == null) + { + tcs.SetResult(null); + return tcs.Task; + } + + string text = null; + + requestBodyDelegate.Invoke((data, continuation) => + { + // TODO: Check the continuation and read async if it isn't null + text = Encoding.UTF8.GetString(data.Array, data.Offset, data.Count); + return false; + }, + tcs.SetException, + () => tcs.SetResult(text)); + + return tcs.Task; + } + + private static BodyDelegate GetRequestBodyDelegate(IDictionary environment) + { + return (BodyDelegate)environment["owin.RequestBody"]; + } + } +} diff --git a/SignalR.Owin/OwinRequest.cs b/SignalR.Owin/OwinRequest.cs new file mode 100644 index 000000000..d9cda7b2f --- /dev/null +++ b/SignalR.Owin/OwinRequest.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using SignalR.Abstractions; + +namespace SignalR.Owin +{ + public class OwinRequest : IRequest + { + public OwinRequest(IDictionary environment, string body) + { + var env = new Gate.Environment(environment); + var headers = env.Headers; + + Url = BuildUrl(env); + Headers = new NameValueCollection(); + + foreach (var pair in headers) + { + Headers.Add(pair.Key, pair.Value); + } + + Cookies = new NameValueCollection(); + QueryString = ParseDelimited(env.QueryString); + Form = ParseDelimited(body); + } + + public NameValueCollection Cookies + { + get; + private set; + } + + public NameValueCollection Form + { + get; + private set; + } + + public NameValueCollection Headers + { + get; + private set; + } + + public NameValueCollection QueryString + { + get; + private set; + } + + public Uri Url + { + get; + private set; + } + + /// + /// Based on http://owin.org/spec/owin-1.0.0draft5.html#URIReconstruction + /// + private Uri BuildUrl(Gate.Environment env) + { + string url = env.Scheme + "://" + env.Headers["Host"] + env.PathBase + env.Path; + + if (!String.IsNullOrEmpty(env.QueryString)) + { + url += "?" + env.QueryString; + } + + return new Uri(url); + } + + private NameValueCollection ParseDelimited(string s) + { + var nvc = new NameValueCollection(); + if (s == null) + { + return nvc; + } + + foreach (var pair in s.Split('&')) + { + var kvp = pair.Split(new[] { "=" }, StringSplitOptions.RemoveEmptyEntries); + if (kvp.Length == 0) + { + continue; + } + + string key = kvp[0].Trim(); + if (String.IsNullOrEmpty(key)) + { + continue; + } + string value = kvp.Length > 1 ? kvp[1].Trim() : null; + nvc[key] = UrlDecode(value); + } + + return nvc; + } + + private static string UrlDecode(string url) + { + if (url == null) + { + return null; + } + + // HACK: Uri.UnescapeDataString doesn't seem to handle + + // TODO: Copy impl from System.Web.HttpUtility.UrlDecode + return Uri.UnescapeDataString(url).Replace("+", " "); + } + } +} diff --git a/SignalR.Owin/OwinResponse.cs b/SignalR.Owin/OwinResponse.cs new file mode 100644 index 000000000..c7c0082e9 --- /dev/null +++ b/SignalR.Owin/OwinResponse.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using SignalR.Abstractions; + +namespace SignalR.Owin +{ + using ResponseCallBack = System.Action, System.Func, System.Action, bool>, System.Action, System.Action, System.Action>>; + + public class OwinResponse : IResponse + { + private readonly ResponseCallBack _responseCallback; + + public OwinResponse(ResponseCallBack responseCallback) + { + _responseCallback = responseCallback; + + IsClientConnected = true; + Buffer = true; + } + + public bool Buffer + { + get; + set; + } + + public string ContentType + { + get; + set; + } + + public bool IsClientConnected + { + get; + private set; + } + + public Task WriteAsync(string data) + { + var tcs = new TaskCompletionSource(); + var headers = new Dictionary { { "Content-Type", ContentType } }; + + _responseCallback.Invoke("200 OK", headers, (next, error, complete) => DoWrite(tcs, data, next, error, complete)); + + return tcs.Task; + } + + private Action DoWrite(TaskCompletionSource tcs, string data, Func, Action, bool> next, Action error, Action complete) + { + try + { + var value = new ArraySegment(Encoding.UTF8.GetBytes(data)); + next(value, null); + complete(); + tcs.SetResult(null); + } + catch (Exception ex) + { + IsClientConnected = false; + error(ex); + tcs.SetException(ex); + } + + return null; + } + } +} diff --git a/SignalR.Owin/Properties/AssemblyInfo.cs b/SignalR.Owin/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..fd41a8fab --- /dev/null +++ b/SignalR.Owin/Properties/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SignalR.Owin")] +[assembly: AssemblyDescription("Owin host for SignalR")] +[assembly: AssemblyVersion("0.3.6")] \ No newline at end of file diff --git a/SignalR.Owin/SignalR.Owin.csproj b/SignalR.Owin/SignalR.Owin.csproj new file mode 100644 index 000000000..f5f81da08 --- /dev/null +++ b/SignalR.Owin/SignalR.Owin.csproj @@ -0,0 +1,75 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {A5726CD0-DE18-40A9-A2EF-C2765EEDBA30} + Library + Properties + SignalR.Owin + SignalR.Owin + v4.0 + 512 + ..\..\SignalR\ + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Gate.0.1.4\lib\Gate.dll + + + + + + + + + + + + Properties\CommonAssemblyInfo.cs + + + + + + + + + + + + + {1B9A82C4-BCA1-4834-A33E-226F17BE070B} + SignalR + + + + + + \ No newline at end of file diff --git a/SignalR.Owin/packages.config b/SignalR.Owin/packages.config new file mode 100644 index 000000000..618f92de3 --- /dev/null +++ b/SignalR.Owin/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SignalR.sln b/SignalR.sln index 2ceedab39..ae697adec 100644 --- a/SignalR.sln +++ b/SignalR.sln @@ -25,6 +25,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.Client.Samples", "S EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.SelfHost.Samples", "SignalR.SelfHost.Samples\SignalR.SelfHost.Samples.csproj", "{8FD9E25A-2268-4F6E-85F3-B83DC018ECE9}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.Owin", "SignalR.Owin\SignalR.Owin.csproj", "{A5726CD0-DE18-40A9-A2EF-C2765EEDBA30}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -125,6 +127,10 @@ Global {8FD9E25A-2268-4F6E-85F3-B83DC018ECE9}.Release|Mixed Platforms.Build.0 = Release|x86 {8FD9E25A-2268-4F6E-85F3-B83DC018ECE9}.Release|x86.ActiveCfg = Release|x86 {8FD9E25A-2268-4F6E-85F3-B83DC018ECE9}.Release|x86.Build.0 = Release|x86 + {A5726CD0-DE18-40A9-A2EF-C2765EEDBA30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5726CD0-DE18-40A9-A2EF-C2765EEDBA30}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5726CD0-DE18-40A9-A2EF-C2765EEDBA30}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5726CD0-DE18-40A9-A2EF-C2765EEDBA30}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SignalR/SignalR.csproj b/SignalR/SignalR.csproj index 2fe35e773..de8082d05 100644 --- a/SignalR/SignalR.csproj +++ b/SignalR/SignalR.csproj @@ -124,7 +124,9 @@ - + + Designer + Designer From 83abd560fc9ddb062ec1fe11798ae6ef50a95d1a Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Sun, 1 Jan 2012 00:59:09 -0800 Subject: [PATCH 0361/1128] Updating for 0.2.0 refresh --- SignalR.Owin/OwinExtensions.cs | 12 - SignalR.Owin/OwinHost.cs | 172 +- SignalR.Owin/OwinResponse.cs | 11 +- SignalR.Owin/SignalR.Owin.csproj | 138 +- SignalR.Owin/packages.config | 7 +- SignalR.Samples/SignalR.Samples.csproj | 530 +- SignalR.SelfHost/SignalR.SelfHost.csproj | 9 +- SignalR.sln | 21 + SignalrKayakGateDemo/Program.cs | 22 + .../Properties/AssemblyInfo.cs | 36 + SignalrKayakGateDemo/Raw.cs | 135 + SignalrKayakGateDemo/SchedulerDelegate.cs | 23 + .../SignalrKayakGateDemo.csproj | 154 + SignalrKayakGateDemo/Startup.cs | 48 + SignalrKayakGateDemo/Workaround.cs | 14 + SignalrKayakGateDemo/app.config | 3 + SignalrKayakGateDemo/packages.config | 9 + .../public/Scripts/jQuery.tmpl.js | 486 + .../public/Scripts/jQuery.tmpl.min.js | 1 + .../public/Scripts/jquery-1.6.2-vsdoc.js | 6987 ++++++++++ .../public/Scripts/jquery-1.6.2.js | 8981 ++++++++++++ .../public/Scripts/jquery-1.6.2.min.js | 18 + .../public/Scripts/jquery-ui-1.8.13.js | 11631 ++++++++++++++++ .../public/Scripts/jquery-ui-1.8.13.min.js | 784 ++ .../public/Scripts/jquery.color.js | 129 + .../public/Scripts/jquery.cookie.js | 96 + .../public/Scripts/jquery.easing.1.3.js | 205 + .../public/Scripts/jquery.hoverMorph.js | 120 + .../public/Scripts/jquery.mobile-1.0b2.js | 6259 +++++++++ .../public/Scripts/jquery.mobile-1.0b2.min.js | 155 + .../public/Scripts/jquery.signalR.js | 599 + .../public/Scripts/jquery.signalR.min.js | 1 + .../public/Scripts/jquery.transform.js | 532 + SignalrKayakGateDemo/public/Scripts/json2.js | 480 + .../public/Scripts/json2.min.js | 1 + .../public/Scripts/remedial.js | 83 + SignalrKayakGateDemo/public/index.html | 119 + 37 files changed, 38563 insertions(+), 448 deletions(-) delete mode 100644 SignalR.Owin/OwinExtensions.cs create mode 100644 SignalrKayakGateDemo/Program.cs create mode 100644 SignalrKayakGateDemo/Properties/AssemblyInfo.cs create mode 100644 SignalrKayakGateDemo/Raw.cs create mode 100644 SignalrKayakGateDemo/SchedulerDelegate.cs create mode 100644 SignalrKayakGateDemo/SignalrKayakGateDemo.csproj create mode 100644 SignalrKayakGateDemo/Startup.cs create mode 100644 SignalrKayakGateDemo/Workaround.cs create mode 100644 SignalrKayakGateDemo/app.config create mode 100644 SignalrKayakGateDemo/packages.config create mode 100644 SignalrKayakGateDemo/public/Scripts/jQuery.tmpl.js create mode 100644 SignalrKayakGateDemo/public/Scripts/jQuery.tmpl.min.js create mode 100644 SignalrKayakGateDemo/public/Scripts/jquery-1.6.2-vsdoc.js create mode 100644 SignalrKayakGateDemo/public/Scripts/jquery-1.6.2.js create mode 100644 SignalrKayakGateDemo/public/Scripts/jquery-1.6.2.min.js create mode 100644 SignalrKayakGateDemo/public/Scripts/jquery-ui-1.8.13.js create mode 100644 SignalrKayakGateDemo/public/Scripts/jquery-ui-1.8.13.min.js create mode 100644 SignalrKayakGateDemo/public/Scripts/jquery.color.js create mode 100644 SignalrKayakGateDemo/public/Scripts/jquery.cookie.js create mode 100644 SignalrKayakGateDemo/public/Scripts/jquery.easing.1.3.js create mode 100644 SignalrKayakGateDemo/public/Scripts/jquery.hoverMorph.js create mode 100644 SignalrKayakGateDemo/public/Scripts/jquery.mobile-1.0b2.js create mode 100644 SignalrKayakGateDemo/public/Scripts/jquery.mobile-1.0b2.min.js create mode 100644 SignalrKayakGateDemo/public/Scripts/jquery.signalR.js create mode 100644 SignalrKayakGateDemo/public/Scripts/jquery.signalR.min.js create mode 100644 SignalrKayakGateDemo/public/Scripts/jquery.transform.js create mode 100644 SignalrKayakGateDemo/public/Scripts/json2.js create mode 100644 SignalrKayakGateDemo/public/Scripts/json2.min.js create mode 100644 SignalrKayakGateDemo/public/Scripts/remedial.js create mode 100644 SignalrKayakGateDemo/public/index.html diff --git a/SignalR.Owin/OwinExtensions.cs b/SignalR.Owin/OwinExtensions.cs deleted file mode 100644 index e433a36d9..000000000 --- a/SignalR.Owin/OwinExtensions.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Gate; - -namespace SignalR.Owin -{ - public static class OwinExtensions - { - public static IAppBuilder MapConnection(this IAppBuilder builder, string path) where T : PersistentConnection - { - return builder.Map(path, Delegates.ToDelegate(new OwinHost().ProcessRequest)); - } - } -} diff --git a/SignalR.Owin/OwinHost.cs b/SignalR.Owin/OwinHost.cs index 8e115ef9b..10b4b732f 100644 --- a/SignalR.Owin/OwinHost.cs +++ b/SignalR.Owin/OwinHost.cs @@ -1,86 +1,86 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using SignalR.Abstractions; -using SignalR.Infrastructure; - -namespace SignalR.Owin -{ - using BodyDelegate = System.Func, // data - System.Action, // continuation - bool>, // continuation will be invoked - System.Action, // onError - System.Action, // on Complete - System.Action>; - - using ResponseCallBack = System.Action, System.Func, System.Action, bool>, System.Action, System.Action, System.Action>>; - - public class OwinHost - { - public void ProcessRequest(IDictionary environment, ResponseCallBack responseCallback, Action fault) - { - // Read the request body then process the request. - ParseBodyAsync(environment).ContinueWith(task => - { - if (task.IsFaulted) - { - // There was an error reading the body - fault(task.Exception); - } - else - { - var request = new OwinRequest(environment, task.Result); - var response = new OwinResponse(responseCallback); - var hostContext = new HostContext(request, response, null); - - try - { - var factory = DependencyResolver.Resolve(); - PersistentConnection connection = factory.CreateInstance(typeof(T)); - - connection.ProcessRequestAsync(hostContext).ContinueWith(innerTask => - { - fault(innerTask.Exception); - }, - TaskContinuationOptions.OnlyOnFaulted); - } - catch (Exception ex) - { - fault(ex); - } - } - }); - } - - private static Task ParseBodyAsync(IDictionary environment) - { - var requestBodyDelegate = GetRequestBodyDelegate(environment); - - var tcs = new TaskCompletionSource(); - if (requestBodyDelegate == null) - { - tcs.SetResult(null); - return tcs.Task; - } - - string text = null; - - requestBodyDelegate.Invoke((data, continuation) => - { - // TODO: Check the continuation and read async if it isn't null - text = Encoding.UTF8.GetString(data.Array, data.Offset, data.Count); - return false; - }, - tcs.SetException, - () => tcs.SetResult(text)); - - return tcs.Task; - } - - private static BodyDelegate GetRequestBodyDelegate(IDictionary environment) - { - return (BodyDelegate)environment["owin.RequestBody"]; - } - } -} +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Gate.Owin; +using SignalR.Abstractions; +using SignalR.Infrastructure; + +namespace SignalR.Owin +{ + public static class OwinHost + { + public static IAppBuilder RunSignalR(this IAppBuilder builder) where T : PersistentConnection + { + return builder.Use(_ => App(typeof(T))); + } + + public static AppDelegate App(Type persistentConnectionType) + { + return (environment, result, fault) => + { + // Read the request body then process the request. + ParseBodyAsync(environment).ContinueWith(task => + { + if (task.IsFaulted) + { + // There was an error reading the body + fault(task.Exception); + } + else + { + var request = new OwinRequest(environment, task.Result); + var response = new OwinResponse(result); + var hostContext = new HostContext(request, response, null); + + try + { + var factory = DependencyResolver.Resolve(); + PersistentConnection connection = factory.CreateInstance(persistentConnectionType); + + connection.ProcessRequestAsync(hostContext).ContinueWith(innerTask => + { + fault(innerTask.Exception); + }, + TaskContinuationOptions.OnlyOnFaulted); + } + catch (Exception ex) + { + fault(ex); + } + } + }); + }; + } + + private static Task ParseBodyAsync(IDictionary environment) + { + var requestBodyDelegate = GetRequestBodyDelegate(environment); + + var tcs = new TaskCompletionSource(); + if (requestBodyDelegate == null) + { + tcs.SetResult(null); + return tcs.Task; + } + + string text = null; + + requestBodyDelegate.Invoke((data, continuation) => + { + // TODO: Check the continuation and read async if it isn't null + text = Encoding.UTF8.GetString(data.Array, data.Offset, data.Count); + return false; + }, + ex => tcs.SetException(ex), + () => tcs.SetResult(text)); + + return tcs.Task; + } + + private static BodyDelegate GetRequestBodyDelegate(IDictionary environment) + { + return (BodyDelegate)environment["owin.RequestBody"]; + } + } +} diff --git a/SignalR.Owin/OwinResponse.cs b/SignalR.Owin/OwinResponse.cs index c7c0082e9..2751b1f65 100644 --- a/SignalR.Owin/OwinResponse.cs +++ b/SignalR.Owin/OwinResponse.cs @@ -1,18 +1,17 @@ using System; using System.Collections.Generic; using System.Text; -using System.Threading.Tasks; +using System.Threading.Tasks; +using Gate.Owin; using SignalR.Abstractions; namespace SignalR.Owin { - using ResponseCallBack = System.Action, System.Func, System.Action, bool>, System.Action, System.Action, System.Action>>; - public class OwinResponse : IResponse { - private readonly ResponseCallBack _responseCallback; - - public OwinResponse(ResponseCallBack responseCallback) + private readonly ResultDelegate _responseCallback; + + public OwinResponse(ResultDelegate responseCallback) { _responseCallback = responseCallback; diff --git a/SignalR.Owin/SignalR.Owin.csproj b/SignalR.Owin/SignalR.Owin.csproj index f5f81da08..cff5fcf1a 100644 --- a/SignalR.Owin/SignalR.Owin.csproj +++ b/SignalR.Owin/SignalR.Owin.csproj @@ -1,75 +1,77 @@ - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {A5726CD0-DE18-40A9-A2EF-C2765EEDBA30} - Library - Properties - SignalR.Owin - SignalR.Owin - v4.0 - 512 - ..\..\SignalR\ - true - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\Gate.0.1.4\lib\Gate.dll - - - - - - - - - - - - Properties\CommonAssemblyInfo.cs - - - - - - - - - - - - - {1B9A82C4-BCA1-4834-A33E-226F17BE070B} - SignalR - - - - + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {A5726CD0-DE18-40A9-A2EF-C2765EEDBA30} + Library + Properties + SignalR.Owin + SignalR.Owin + v4.0 + 512 + ..\..\SignalR\ + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Gate.Owin.0.2.0\lib\net40\Gate.Owin.dll + + + ..\packages\Gate.0.2.0\lib\net40\Gate.dll + + + + + + + + + + + + Properties\CommonAssemblyInfo.cs + + + + + + + + + + + + {1B9A82C4-BCA1-4834-A33E-226F17BE070B} + SignalR + + + + + --> \ No newline at end of file diff --git a/SignalR.Owin/packages.config b/SignalR.Owin/packages.config index 618f92de3..dcc52aedd 100644 --- a/SignalR.Owin/packages.config +++ b/SignalR.Owin/packages.config @@ -1,4 +1,5 @@ - - - + + + + \ No newline at end of file diff --git a/SignalR.Samples/SignalR.Samples.csproj b/SignalR.Samples/SignalR.Samples.csproj index e39d0b20d..9999b888a 100644 --- a/SignalR.Samples/SignalR.Samples.csproj +++ b/SignalR.Samples/SignalR.Samples.csproj @@ -1,270 +1,270 @@ - - - - - Debug - AnyCPU - 2.0 - {1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82} - {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} - Library - Properties - SignalR.Samples - SignalR.Samples - v4.0 - true - ..\ - true - - - true - full - false - bin\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\ - TRACE - prompt - 4 - - - - ..\packages\elmah.corelibrary.1.2\lib\Elmah.dll - - - ..\packages\EntityFramework.4.1.10331.0\lib\EntityFramework.dll - - - ..\packages\AntiXSS.4.0.1\lib\net35\HtmlSanitizationLibrary.dll - - - - True - ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - - - - - - - - - - - - - - - - - - - - - - False - ..\packages\WebActivator.1.5\lib\net40\WebActivator.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Designer - - - Web.config - - - Web.config - - - - - - Default.aspx - ASPXCodeBehind - - - Default.aspx - - - - - - - - - - - - - - - SignalR.Master - ASPXCodeBehind - - - SignalR.Master - - - SignalR.Samples.master - ASPXCodeBehind - - - SignalR.Samples.master - - - - - - Designer - - - - - - - - {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2} - SignalR.AspNet - - - {32D16B36-970E-4CF2-B954-78CB1833CBC1} - SignalR.ScaleOut - - - {1B9A82C4-BCA1-4834-A33E-226F17BE070B} - SignalR - - - - - - - - - - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - - - - - - - True - True - 40476 - / - http://localhost:40476/ - False - False - - - False - - - - - - - - + + + + + Debug + AnyCPU + 2.0 + {1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + SignalR.Samples + SignalR.Samples + v4.0 + false + ..\ + true + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + ..\packages\elmah.corelibrary.1.2\lib\Elmah.dll + + + ..\packages\EntityFramework.4.1.10331.0\lib\EntityFramework.dll + + + ..\packages\AntiXSS.4.0.1\lib\net35\HtmlSanitizationLibrary.dll + + + + True + ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll + + + + + + + + + + + + + + + + + + + + + + + False + ..\packages\WebActivator.1.5\lib\net40\WebActivator.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + + + Web.config + + + Web.config + + + + + + Default.aspx + ASPXCodeBehind + + + Default.aspx + + + + + + + + + + + + + + + SignalR.Master + ASPXCodeBehind + + + SignalR.Master + + + SignalR.Samples.master + ASPXCodeBehind + + + SignalR.Samples.master + + + + + + Designer + + + + + + + + {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2} + SignalR.AspNet + + + {32D16B36-970E-4CF2-B954-78CB1833CBC1} + SignalR.ScaleOut + + + {1B9A82C4-BCA1-4834-A33E-226F17BE070B} + SignalR + + + + + + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + + + True + False + 40476 + / + http://localhost/SignalR.Samples + False + False + + + False + + + + + + + + - - - - + --> + + + + \ No newline at end of file diff --git a/SignalR.SelfHost/SignalR.SelfHost.csproj b/SignalR.SelfHost/SignalR.SelfHost.csproj index 54d60b8bf..d0cd0cf9e 100644 --- a/SignalR.SelfHost/SignalR.SelfHost.csproj +++ b/SignalR.SelfHost/SignalR.SelfHost.csproj @@ -1,4 +1,4 @@ - + Debug @@ -62,11 +62,4 @@ - \ No newline at end of file diff --git a/SignalR.sln b/SignalR.sln index ae697adec..938f26cb6 100644 --- a/SignalR.sln +++ b/SignalR.sln @@ -27,6 +27,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.SelfHost.Samples", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.Owin", "SignalR.Owin\SignalR.Owin.csproj", "{A5726CD0-DE18-40A9-A2EF-C2765EEDBA30}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalrKayakGateDemo", "SignalrKayakGateDemo\SignalrKayakGateDemo.csproj", "{8BF43A5B-4C96-4490-A9AF-5A057AC37C24}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -127,10 +129,29 @@ Global {8FD9E25A-2268-4F6E-85F3-B83DC018ECE9}.Release|Mixed Platforms.Build.0 = Release|x86 {8FD9E25A-2268-4F6E-85F3-B83DC018ECE9}.Release|x86.ActiveCfg = Release|x86 {8FD9E25A-2268-4F6E-85F3-B83DC018ECE9}.Release|x86.Build.0 = Release|x86 + {A2BD866D-906A-4742-AB3B-DB8740830400}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {A2BD866D-906A-4742-AB3B-DB8740830400}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {A2BD866D-906A-4742-AB3B-DB8740830400}.Release|x86.ActiveCfg = Release|Any CPU {A5726CD0-DE18-40A9-A2EF-C2765EEDBA30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A5726CD0-DE18-40A9-A2EF-C2765EEDBA30}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5726CD0-DE18-40A9-A2EF-C2765EEDBA30}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {A5726CD0-DE18-40A9-A2EF-C2765EEDBA30}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {A5726CD0-DE18-40A9-A2EF-C2765EEDBA30}.Debug|x86.ActiveCfg = Debug|Any CPU {A5726CD0-DE18-40A9-A2EF-C2765EEDBA30}.Release|Any CPU.ActiveCfg = Release|Any CPU {A5726CD0-DE18-40A9-A2EF-C2765EEDBA30}.Release|Any CPU.Build.0 = Release|Any CPU + {A5726CD0-DE18-40A9-A2EF-C2765EEDBA30}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {A5726CD0-DE18-40A9-A2EF-C2765EEDBA30}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {A5726CD0-DE18-40A9-A2EF-C2765EEDBA30}.Release|x86.ActiveCfg = Release|Any CPU + {8BF43A5B-4C96-4490-A9AF-5A057AC37C24}.Debug|Any CPU.ActiveCfg = Debug|x86 + {8BF43A5B-4C96-4490-A9AF-5A057AC37C24}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {8BF43A5B-4C96-4490-A9AF-5A057AC37C24}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {8BF43A5B-4C96-4490-A9AF-5A057AC37C24}.Debug|x86.ActiveCfg = Debug|x86 + {8BF43A5B-4C96-4490-A9AF-5A057AC37C24}.Debug|x86.Build.0 = Debug|x86 + {8BF43A5B-4C96-4490-A9AF-5A057AC37C24}.Release|Any CPU.ActiveCfg = Release|x86 + {8BF43A5B-4C96-4490-A9AF-5A057AC37C24}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {8BF43A5B-4C96-4490-A9AF-5A057AC37C24}.Release|Mixed Platforms.Build.0 = Release|x86 + {8BF43A5B-4C96-4490-A9AF-5A057AC37C24}.Release|x86.ActiveCfg = Release|x86 + {8BF43A5B-4C96-4490-A9AF-5A057AC37C24}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SignalrKayakGateDemo/Program.cs b/SignalrKayakGateDemo/Program.cs new file mode 100644 index 000000000..b2b16a07a --- /dev/null +++ b/SignalrKayakGateDemo/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; +using Gate.Hosts.Kayak; +using SignalR; +using SignalR.Abstractions; + +namespace SignalrKayakGateDemo +{ + public class Program + { + // plain-ol' entry point. + public static void Main(string[] args) + { + var ep = new IPEndPoint(IPAddress.Any, 5500); + Console.WriteLine("Listening on " + ep); + + KayakGate.Start(new SchedulerDelegate(), ep, Startup.Configuration); + } + } +} diff --git a/SignalrKayakGateDemo/Properties/AssemblyInfo.cs b/SignalrKayakGateDemo/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..7392c9483 --- /dev/null +++ b/SignalrKayakGateDemo/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SignalrKayakGateDemo")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("SignalrKayakGateDemo")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ba0fc719-d4be-4257-8dfb-56f5d4934301")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SignalrKayakGateDemo/Raw.cs b/SignalrKayakGateDemo/Raw.cs new file mode 100644 index 000000000..845542879 --- /dev/null +++ b/SignalrKayakGateDemo/Raw.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Web.Script.Serialization; +using SignalR; +using SignalR.Abstractions; + +namespace SignalrKayakGateDemo +{ + public class Raw : PersistentConnection + { + private static readonly Dictionary _users = new Dictionary(); + private static readonly Dictionary _clients = new Dictionary(); + + protected override Task OnConnectedAsync(IRequest request, string connectionId) + { + var userName = request.Cookies["user"]; + if (!String.IsNullOrEmpty(userName)) + { + _clients[connectionId] = userName; + _users[userName] = connectionId; + } + + string user = GetUser(connectionId); + + return Connection.Broadcast(DateTime.Now + ": " + user + " joined"); + } + + protected override Task OnDisconnectAsync(string connectionId) + { + _users.Remove(connectionId); + return Connection.Broadcast(DateTime.Now + ": " + GetUser(connectionId) + " disconnected"); + } + + protected override Task OnReceivedAsync(string connectionId, string data) + { + var serializer = new JavaScriptSerializer(); + var message = serializer.Deserialize(data); + + switch (message.Type) + { + case MessageType.Broadcast: + Connection.Broadcast(new + { + type = MessageType.Broadcast, + from = GetUser(connectionId), + data = message.Value + }); + break; + case MessageType.Send: + Send(new + { + type = MessageType.Send, + from = GetUser(connectionId), + data = message.Value + }); + break; + case MessageType.Join: + string name = message.Value; + _clients[connectionId] = name; + _users[name] = connectionId; + Send(new + { + type = MessageType.Join, + data = message.Value + }); + break; + case MessageType.PrivateMessage: + var parts = message.Value.Split('|'); + string user = parts[0]; + string msg = parts[1]; + string id = GetClient(user); + Send(id, new + { + from = GetUser(connectionId), + data = msg + }); + break; + case MessageType.AddToGroup: + AddToGroup(connectionId, message.Value); + break; + case MessageType.RemoveFromGroup: + RemoveFromGroup(connectionId, message.Value); + break; + case MessageType.SendToGroup: + var parts2 = message.Value.Split('|'); + string groupName = parts2[0]; + string val = parts2[1]; + SendToGroup(groupName, val); + break; + default: + break; + } + + return base.OnReceivedAsync(connectionId, data); + } + + private string GetUser(string connectionId) + { + string user; + if (!_clients.TryGetValue(connectionId, out user)) + { + return connectionId; + } + return user; + } + + private string GetClient(string user) + { + string connectionId; + if (_users.TryGetValue(user, out connectionId)) + { + return connectionId; + } + return null; + } + + enum MessageType + { + Send, + Broadcast, + Join, + PrivateMessage, + AddToGroup, + RemoveFromGroup, + SendToGroup + } + + class Message + { + public MessageType Type { get; set; } + public string Value { get; set; } + } + } +} \ No newline at end of file diff --git a/SignalrKayakGateDemo/SchedulerDelegate.cs b/SignalrKayakGateDemo/SchedulerDelegate.cs new file mode 100644 index 000000000..c49b93f4d --- /dev/null +++ b/SignalrKayakGateDemo/SchedulerDelegate.cs @@ -0,0 +1,23 @@ +using System; +using Kayak; + +namespace SignalrKayakGateDemo +{ + public class SchedulerDelegate : ISchedulerDelegate + { + public void OnException(IScheduler scheduler, Exception e) + { + // called whenever an exception occurs on Kayak's event loop. + // this is good place for logging. here's a start: + Console.WriteLine("Exception on scheduler"); + Console.Out.WriteStackTrace(e); + } + + public void OnStop(IScheduler scheduler) + { + // called when Kayak's run loop is about to exit. + // this is a good place for doing clean-up or other chores. + Console.WriteLine("Scheduler is stopping."); + } + } +} \ No newline at end of file diff --git a/SignalrKayakGateDemo/SignalrKayakGateDemo.csproj b/SignalrKayakGateDemo/SignalrKayakGateDemo.csproj new file mode 100644 index 000000000..20ca8c9e7 --- /dev/null +++ b/SignalrKayakGateDemo/SignalrKayakGateDemo.csproj @@ -0,0 +1,154 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {8BF43A5B-4C96-4490-A9AF-5A057AC37C24} + Exe + Properties + SignalrKayakGateDemo + SignalrKayakGateDemo + v4.0 + + + 512 + ..\..\SignalR\ + true + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Gate.0.2.0\lib\net40\Gate.dll + + + ..\packages\Gate.Builder.0.2.0\lib\net40\Gate.Builder.dll + + + ..\packages\Gate.Hosts.Kayak.0.2.0\lib\net40\Gate.Hosts.Kayak.dll + + + ..\packages\Gate.Middleware.0.2.0\lib\net40\Gate.Middleware.dll + + + ..\packages\Gate.Owin.0.2.0\lib\net40\Gate.Owin.dll + + + ..\packages\Kayak.0.7.2\lib\Kayak.dll + + + + + + + + + + + + + + + + + + + + + + + + + {A5726CD0-DE18-40A9-A2EF-C2765EEDBA30} + SignalR.Owin + + + {1B9A82C4-BCA1-4834-A33E-226F17BE070B} + SignalR + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + \ No newline at end of file diff --git a/SignalrKayakGateDemo/Startup.cs b/SignalrKayakGateDemo/Startup.cs new file mode 100644 index 000000000..c4b7b1ab6 --- /dev/null +++ b/SignalrKayakGateDemo/Startup.cs @@ -0,0 +1,48 @@ +using System; +using Gate; +using Gate.Hosts.Kayak; +using Gate.Middleware; +using Gate.Owin; +using SignalR.Owin; + +namespace SignalrKayakGateDemo +{ + public class Startup + { + public static void Configuration(IAppBuilder builder) + { + builder + .Use(LogToConsole) + //.RescheduleCallbacks() + .UseShowExceptions() + .Map("/Raw", map => map.RunSignalR()) + .Use(Alias, "/", "/index.html") + .UseStatic("public"); + } + + public static AppDelegate Alias(AppDelegate app, string path, string alias) + { + return + (env, result, fault) => + { + var req = new Request(env); + if (req.Path == path) + { + req.Path = alias; + } + app(env, result, fault); + }; + } + + public static AppDelegate LogToConsole(AppDelegate app) + { + return + (env, result, fault) => + { + var req = new Request(env); + Console.WriteLine(req.Method + " " + req.PathBase + req.Path); + app(env, result, fault); + }; + } + } +} \ No newline at end of file diff --git a/SignalrKayakGateDemo/Workaround.cs b/SignalrKayakGateDemo/Workaround.cs new file mode 100644 index 000000000..7c8692286 --- /dev/null +++ b/SignalrKayakGateDemo/Workaround.cs @@ -0,0 +1,14 @@ +using System; +using Gate.Owin; + +namespace SignalrKayakGateDemo +{ + class Workaround + { +#pragma warning disable 169 + static private ResultDelegate _workaround1; + static private Action _workaround2; +#pragma warning restore 169 + + } +} diff --git a/SignalrKayakGateDemo/app.config b/SignalrKayakGateDemo/app.config new file mode 100644 index 000000000..cb2586beb --- /dev/null +++ b/SignalrKayakGateDemo/app.config @@ -0,0 +1,3 @@ + + + diff --git a/SignalrKayakGateDemo/packages.config b/SignalrKayakGateDemo/packages.config new file mode 100644 index 000000000..eb2fc9f6c --- /dev/null +++ b/SignalrKayakGateDemo/packages.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/SignalrKayakGateDemo/public/Scripts/jQuery.tmpl.js b/SignalrKayakGateDemo/public/Scripts/jQuery.tmpl.js new file mode 100644 index 000000000..1c5b2d62f --- /dev/null +++ b/SignalrKayakGateDemo/public/Scripts/jQuery.tmpl.js @@ -0,0 +1,486 @@ +/* + * jQuery Templating Plugin + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + */ +(function( jQuery, undefined ){ + var oldManip = jQuery.fn.domManip, tmplItmAtt = "_tmplitem", htmlExpr = /^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /, + newTmplItems = {}, wrappedItems = {}, appendToTmplItems, topTmplItem = { key: 0, data: {} }, itemKey = 0, cloneIndex = 0, stack = []; + + function newTmplItem( options, parentItem, fn, data ) { + // Returns a template item data structure for a new rendered instance of a template (a 'template item'). + // The content field is a hierarchical array of strings and nested items (to be + // removed and replaced by nodes field of dom elements, once inserted in DOM). + var newItem = { + data: data || (parentItem ? parentItem.data : {}), + _wrap: parentItem ? parentItem._wrap : null, + tmpl: null, + parent: parentItem || null, + nodes: [], + calls: tiCalls, + nest: tiNest, + wrap: tiWrap, + html: tiHtml, + update: tiUpdate + }; + if ( options ) { + jQuery.extend( newItem, options, { nodes: [], parent: parentItem } ); + } + if ( fn ) { + // Build the hierarchical content to be used during insertion into DOM + newItem.tmpl = fn; + newItem._ctnt = newItem._ctnt || newItem.tmpl( jQuery, newItem ); + newItem.key = ++itemKey; + // Keep track of new template item, until it is stored as jQuery Data on DOM element + (stack.length ? wrappedItems : newTmplItems)[itemKey] = newItem; + } + return newItem; + } + + // Override appendTo etc., in order to provide support for targeting multiple elements. (This code would disappear if integrated in jquery core). + jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" + }, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var ret = [], insert = jQuery( selector ), elems, i, l, tmplItems, + parent = this.length === 1 && this[0].parentNode; + + appendToTmplItems = newTmplItems || {}; + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + ret = this; + } else { + for ( i = 0, l = insert.length; i < l; i++ ) { + cloneIndex = i; + elems = (i > 0 ? this.clone(true) : this).get(); + jQuery.fn[ original ].apply( jQuery(insert[i]), elems ); + ret = ret.concat( elems ); + } + cloneIndex = 0; + ret = this.pushStack( ret, name, insert.selector ); + } + tmplItems = appendToTmplItems; + appendToTmplItems = null; + jQuery.tmpl.complete( tmplItems ); + return ret; + }; + }); + + jQuery.fn.extend({ + // Use first wrapped element as template markup. + // Return wrapped set of template items, obtained by rendering template against data. + tmpl: function( data, options, parentItem ) { + return jQuery.tmpl( this[0], data, options, parentItem ); + }, + + // Find which rendered template item the first wrapped DOM element belongs to + tmplItem: function() { + return jQuery.tmplItem( this[0] ); + }, + + // Consider the first wrapped element as a template declaration, and get the compiled template or store it as a named template. + template: function( name ) { + return jQuery.template( name, this[0] ); + }, + + domManip: function( args, table, callback, options ) { + // This appears to be a bug in the appendTo, etc. implementation + // it should be doing .call() instead of .apply(). See #6227 + if ( args[0] && args[0].nodeType ) { + var dmArgs = jQuery.makeArray( arguments ), argsLength = args.length, i = 0, tmplItem; + while ( i < argsLength && !(tmplItem = jQuery.data( args[i++], "tmplItem" ))) {} + if ( argsLength > 1 ) { + dmArgs[0] = [jQuery.makeArray( args )]; + } + if ( tmplItem && cloneIndex ) { + dmArgs[2] = function( fragClone ) { + // Handler called by oldManip when rendered template has been inserted into DOM. + jQuery.tmpl.afterManip( this, fragClone, callback ); + }; + } + oldManip.apply( this, dmArgs ); + } else { + oldManip.apply( this, arguments ); + } + cloneIndex = 0; + if ( !appendToTmplItems ) { + jQuery.tmpl.complete( newTmplItems ); + } + return this; + } + }); + + jQuery.extend({ + // Return wrapped set of template items, obtained by rendering template against data. + tmpl: function( tmpl, data, options, parentItem ) { + var ret, topLevel = !parentItem; + if ( topLevel ) { + // This is a top-level tmpl call (not from a nested template using {{tmpl}}) + parentItem = topTmplItem; + tmpl = jQuery.template[tmpl] || jQuery.template( null, tmpl ); + wrappedItems = {}; // Any wrapped items will be rebuilt, since this is top level + } else if ( !tmpl ) { + // The template item is already associated with DOM - this is a refresh. + // Re-evaluate rendered template for the parentItem + tmpl = parentItem.tmpl; + newTmplItems[parentItem.key] = parentItem; + parentItem.nodes = []; + if ( parentItem.wrapped ) { + updateWrapped( parentItem, parentItem.wrapped ); + } + // Rebuild, without creating a new template item + return jQuery( build( parentItem, null, parentItem.tmpl( jQuery, parentItem ) )); + } + if ( !tmpl ) { + return []; // Could throw... + } + if ( typeof data === "function" ) { + data = data.call( parentItem || {} ); + } + if ( options && options.wrapped ) { + updateWrapped( options, options.wrapped ); + } + ret = jQuery.isArray( data ) ? + jQuery.map( data, function( dataItem ) { + return dataItem ? newTmplItem( options, parentItem, tmpl, dataItem ) : null; + }) : + [ newTmplItem( options, parentItem, tmpl, data ) ]; + return topLevel ? jQuery( build( parentItem, null, ret ) ) : ret; + }, + + // Return rendered template item for an element. + tmplItem: function( elem ) { + var tmplItem; + if ( elem instanceof jQuery ) { + elem = elem[0]; + } + while ( elem && elem.nodeType === 1 && !(tmplItem = jQuery.data( elem, "tmplItem" )) && (elem = elem.parentNode) ) {} + return tmplItem || topTmplItem; + }, + + // Set: + // Use $.template( name, tmpl ) to cache a named template, + // where tmpl is a template string, a script element or a jQuery instance wrapping a script element, etc. + // Use $( "selector" ).template( name ) to provide access by name to a script block template declaration. + + // Get: + // Use $.template( name ) to access a cached template. + // Also $( selectorToScriptBlock ).template(), or $.template( null, templateString ) + // will return the compiled template, without adding a name reference. + // If templateString includes at least one HTML tag, $.template( templateString ) is equivalent + // to $.template( null, templateString ) + template: function( name, tmpl ) { + if (tmpl) { + // Compile template and associate with name + if ( typeof tmpl === "string" ) { + // This is an HTML string being passed directly in. + tmpl = buildTmplFn( tmpl ) + } else if ( tmpl instanceof jQuery ) { + tmpl = tmpl[0] || {}; + } + if ( tmpl.nodeType ) { + // If this is a template block, use cached copy, or generate tmpl function and cache. + tmpl = jQuery.data( tmpl, "tmpl" ) || jQuery.data( tmpl, "tmpl", buildTmplFn( tmpl.innerHTML )); + } + return typeof name === "string" ? (jQuery.template[name] = tmpl) : tmpl; + } + // Return named compiled template + return name ? (typeof name !== "string" ? jQuery.template( null, name ): + (jQuery.template[name] || + // If not in map, treat as a selector. (If integrated with core, use quickExpr.exec) + jQuery.template( null, htmlExpr.test( name ) ? name : jQuery( name )))) : null; + }, + + encode: function( text ) { + // Do HTML encoding replacing < > & and ' and " by corresponding entities. + return ("" + text).split("<").join("<").split(">").join(">").split('"').join(""").split("'").join("'"); + } + }); + + jQuery.extend( jQuery.tmpl, { + tag: { + "tmpl": { + _default: { $2: "null" }, + open: "if($notnull_1){_=_.concat($item.nest($1,$2));}" + // tmpl target parameter can be of type function, so use $1, not $1a (so not auto detection of functions) + // This means that {{tmpl foo}} treats foo as a template (which IS a function). + // Explicit parens can be used if foo is a function that returns a template: {{tmpl foo()}}. + }, + "wrap": { + _default: { $2: "null" }, + open: "$item.calls(_,$1,$2);_=[];", + close: "call=$item.calls();_=call._.concat($item.wrap(call,_));" + }, + "each": { + _default: { $2: "$index, $value" }, + open: "if($notnull_1){$.each($1a,function($2){with(this){", + close: "}});}" + }, + "if": { + open: "if(($notnull_1) && $1a){", + close: "}" + }, + "else": { + _default: { $1: "true" }, + open: "}else if(($notnull_1) && $1a){" + }, + "html": { + // Unecoded expression evaluation. + open: "if($notnull_1){_.push($1a);}" + }, + "=": { + // Encoded expression evaluation. Abbreviated form is ${}. + _default: { $1: "$data" }, + open: "if($notnull_1){_.push($.encode($1a));}" + }, + "!": { + // Comment tag. Skipped by parser + open: "" + } + }, + + // This stub can be overridden, e.g. in jquery.tmplPlus for providing rendered events + complete: function( items ) { + newTmplItems = {}; + }, + + // Call this from code which overrides domManip, or equivalent + // Manage cloning/storing template items etc. + afterManip: function afterManip( elem, fragClone, callback ) { + // Provides cloned fragment ready for fixup prior to and after insertion into DOM + var content = fragClone.nodeType === 11 ? + jQuery.makeArray(fragClone.childNodes) : + fragClone.nodeType === 1 ? [fragClone] : []; + + // Return fragment to original caller (e.g. append) for DOM insertion + callback.call( elem, fragClone ); + + // Fragment has been inserted:- Add inserted nodes to tmplItem data structure. Replace inserted element annotations by jQuery.data. + storeTmplItems( content ); + cloneIndex++; + } + }); + + //========================== Private helper functions, used by code above ========================== + + function build( tmplItem, nested, content ) { + // Convert hierarchical content into flat string array + // and finally return array of fragments ready for DOM insertion + var frag, ret = content ? jQuery.map( content, function( item ) { + return (typeof item === "string") ? + // Insert template item annotations, to be converted to jQuery.data( "tmplItem" ) when elems are inserted into DOM. + (tmplItem.key ? item.replace( /(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g, "$1 " + tmplItmAtt + "=\"" + tmplItem.key + "\" $2" ) : item) : + // This is a child template item. Build nested template. + build( item, tmplItem, item._ctnt ); + }) : + // If content is not defined, insert tmplItem directly. Not a template item. May be a string, or a string array, e.g. from {{html $item.html()}}. + tmplItem; + if ( nested ) { + return ret; + } + + // top-level template + ret = ret.join(""); + + // Support templates which have initial or final text nodes, or consist only of text + // Also support HTML entities within the HTML markup. + ret.replace( /^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/, function( all, before, middle, after) { + frag = jQuery( middle ).get(); + + storeTmplItems( frag ); + if ( before ) { + frag = unencode( before ).concat(frag); + } + if ( after ) { + frag = frag.concat(unencode( after )); + } + }); + return frag ? frag : unencode( ret ); + } + + function unencode( text ) { + // Use createElement, since createTextNode will not render HTML entities correctly + var el = document.createElement( "div" ); + el.innerHTML = text; + return jQuery.makeArray(el.childNodes); + } + + // Generate a reusable function that will serve to render a template against data + function buildTmplFn( markup ) { + return new Function("jQuery","$item", + "var $=jQuery,call,_=[],$data=$item.data;" + + + // Introduce the data as local variables using with(){} + "with($data){_.push('" + + + // Convert the template into pure JavaScript + jQuery.trim(markup) + .replace( /([\\'])/g, "\\$1" ) + .replace( /[\r\t\n]/g, " " ) + .replace( /\$\{([^\}]*)\}/g, "{{= $1}}" ) + .replace( /\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g, + function( all, slash, type, fnargs, target, parens, args ) { + var tag = jQuery.tmpl.tag[ type ], def, expr, exprAutoFnDetect; + if ( !tag ) { + throw "Template command not found: " + type; + } + def = tag._default || []; + if ( parens && !/\w$/.test(target)) { + target += parens; + parens = ""; + } + if ( target ) { + target = unescape( target ); + args = args ? ("," + unescape( args ) + ")") : (parens ? ")" : ""); + // Support for target being things like a.toLowerCase(); + // In that case don't call with template item as 'this' pointer. Just evaluate... + expr = parens ? (target.indexOf(".") > -1 ? target + parens : ("(" + target + ").call($item" + args)) : target; + exprAutoFnDetect = parens ? expr : "(typeof(" + target + ")==='function'?(" + target + ").call($item):(" + target + "))"; + } else { + exprAutoFnDetect = expr = def.$1 || "null"; + } + fnargs = unescape( fnargs ); + return "');" + + tag[ slash ? "close" : "open" ] + .split( "$notnull_1" ).join( target ? "typeof(" + target + ")!=='undefined' && (" + target + ")!=null" : "true" ) + .split( "$1a" ).join( exprAutoFnDetect ) + .split( "$1" ).join( expr ) + .split( "$2" ).join( fnargs ? + fnargs.replace( /\s*([^\(]+)\s*(\((.*?)\))?/g, function( all, name, parens, params ) { + params = params ? ("," + params + ")") : (parens ? ")" : ""); + return params ? ("(" + name + ").call($item" + params) : all; + }) + : (def.$2||"") + ) + + "_.push('"; + }) + + "');}return _;" + ); + } + function updateWrapped( options, wrapped ) { + // Build the wrapped content. + options._wrap = build( options, true, + // Suport imperative scenario in which options.wrapped can be set to a selector or an HTML string. + jQuery.isArray( wrapped ) ? wrapped : [htmlExpr.test( wrapped ) ? wrapped : jQuery( wrapped ).html()] + ).join(""); + } + + function unescape( args ) { + return args ? args.replace( /\\'/g, "'").replace(/\\\\/g, "\\" ) : null; + } + function outerHtml( elem ) { + var div = document.createElement("div"); + div.appendChild( elem.cloneNode(true) ); + return div.innerHTML; + } + + // Store template items in jQuery.data(), ensuring a unique tmplItem data data structure for each rendered template instance. + function storeTmplItems( content ) { + var keySuffix = "_" + cloneIndex, elem, elems, newClonedItems = {}, i, l, m; + for ( i = 0, l = content.length; i < l; i++ ) { + if ( (elem = content[i]).nodeType !== 1 ) { + continue; + } + elems = elem.getElementsByTagName("*"); + for ( m = elems.length - 1; m >= 0; m-- ) { + processItemKey( elems[m] ); + } + processItemKey( elem ); + } + function processItemKey( el ) { + var pntKey, pntNode = el, pntItem, tmplItem, key; + // Ensure that each rendered template inserted into the DOM has its own template item, + if ( (key = el.getAttribute( tmplItmAtt ))) { + while ( pntNode.parentNode && (pntNode = pntNode.parentNode).nodeType === 1 && !(pntKey = pntNode.getAttribute( tmplItmAtt ))) { } + if ( pntKey !== key ) { + // The next ancestor with a _tmplitem expando is on a different key than this one. + // So this is a top-level element within this template item + // Set pntNode to the key of the parentNode, or to 0 if pntNode.parentNode is null, or pntNode is a fragment. + pntNode = pntNode.parentNode ? (pntNode.nodeType === 11 ? 0 : (pntNode.getAttribute( tmplItmAtt ) || 0)) : 0; + if ( !(tmplItem = newTmplItems[key]) ) { + // The item is for wrapped content, and was copied from the temporary parent wrappedItem. + tmplItem = wrappedItems[key]; + tmplItem = newTmplItem( tmplItem, newTmplItems[pntNode]||wrappedItems[pntNode], null, true ); + tmplItem.key = ++itemKey; + newTmplItems[itemKey] = tmplItem; + } + if ( cloneIndex ) { + cloneTmplItem( key ); + } + } + el.removeAttribute( tmplItmAtt ); + } else if ( cloneIndex && (tmplItem = jQuery.data( el, "tmplItem" )) ) { + // This was a rendered element, cloned during append or appendTo etc. + // TmplItem stored in jQuery data has already been cloned in cloneCopyEvent. We must replace it with a fresh cloned tmplItem. + cloneTmplItem( tmplItem.key ); + newTmplItems[tmplItem.key] = tmplItem; + pntNode = jQuery.data( el.parentNode, "tmplItem" ); + pntNode = pntNode ? pntNode.key : 0; + } + if ( tmplItem ) { + pntItem = tmplItem; + // Find the template item of the parent element. + // (Using !=, not !==, since pntItem.key is number, and pntNode may be a string) + while ( pntItem && pntItem.key != pntNode ) { + // Add this element as a top-level node for this rendered template item, as well as for any + // ancestor items between this item and the item of its parent element + pntItem.nodes.push( el ); + pntItem = pntItem.parent; + } + // Delete content built during rendering - reduce API surface area and memory use, and avoid exposing of stale data after rendering... + delete tmplItem._ctnt; + delete tmplItem._wrap; + // Store template item as jQuery data on the element + jQuery.data( el, "tmplItem", tmplItem ); + } + function cloneTmplItem( key ) { + key = key + keySuffix; + tmplItem = newClonedItems[key] = + (newClonedItems[key] || newTmplItem( tmplItem, newTmplItems[tmplItem.parent.key + keySuffix] || tmplItem.parent, null, true )); + } + } + } + + //---- Helper functions for template item ---- + + function tiCalls( content, tmpl, data, options ) { + if ( !content ) { + return stack.pop(); + } + stack.push({ _: content, tmpl: tmpl, item:this, data: data, options: options }); + } + + function tiNest( tmpl, data, options ) { + // nested template, using {{tmpl}} tag + return jQuery.tmpl( jQuery.template( tmpl ), data, options, this ); + } + + function tiWrap( call, wrapped ) { + // nested template, using {{wrap}} tag + var options = call.options || {}; + options.wrapped = wrapped; + // Apply the template, which may incorporate wrapped content, + return jQuery.tmpl( jQuery.template( call.tmpl ), call.data, options, call.item ); + } + + function tiHtml( filter, textOnly ) { + var wrapped = this._wrap; + return jQuery.map( + jQuery( jQuery.isArray( wrapped ) ? wrapped.join("") : wrapped ).filter( filter || "*" ), + function(e) { + return textOnly ? + e.innerText || e.textContent : + e.outerHTML || outerHtml(e); + }); + } + + function tiUpdate() { + var coll = this.nodes; + jQuery.tmpl( null, null, null, this).insertBefore( coll[0] ); + jQuery( coll ).remove(); + } +})( jQuery ); diff --git a/SignalrKayakGateDemo/public/Scripts/jQuery.tmpl.min.js b/SignalrKayakGateDemo/public/Scripts/jQuery.tmpl.min.js new file mode 100644 index 000000000..f08e81dcc --- /dev/null +++ b/SignalrKayakGateDemo/public/Scripts/jQuery.tmpl.min.js @@ -0,0 +1 @@ +(function(a){var r=a.fn.domManip,d="_tmplitem",q=/^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /,b={},f={},e,p={key:0,data:{}},h=0,c=0,l=[];function g(e,d,g,i){var c={data:i||(d?d.data:{}),_wrap:d?d._wrap:null,tmpl:null,parent:d||null,nodes:[],calls:u,nest:w,wrap:x,html:v,update:t};e&&a.extend(c,e,{nodes:[],parent:d});if(g){c.tmpl=g;c._ctnt=c._ctnt||c.tmpl(a,c);c.key=++h;(l.length?f:b)[h]=c}return c}a.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(f,d){a.fn[f]=function(n){var g=[],i=a(n),k,h,m,l,j=this.length===1&&this[0].parentNode;e=b||{};if(j&&j.nodeType===11&&j.childNodes.length===1&&i.length===1){i[d](this[0]);g=this}else{for(h=0,m=i.length;h0?this.clone(true):this).get();a.fn[d].apply(a(i[h]),k);g=g.concat(k)}c=0;g=this.pushStack(g,f,i.selector)}l=e;e=null;a.tmpl.complete(l);return g}});a.fn.extend({tmpl:function(d,c,b){return a.tmpl(this[0],d,c,b)},tmplItem:function(){return a.tmplItem(this[0])},template:function(b){return a.template(b,this[0])},domManip:function(d,l,j){if(d[0]&&d[0].nodeType){var f=a.makeArray(arguments),g=d.length,i=0,h;while(i1)f[0]=[a.makeArray(d)];if(h&&c)f[2]=function(b){a.tmpl.afterManip(this,b,j)};r.apply(this,f)}else r.apply(this,arguments);c=0;!e&&a.tmpl.complete(b);return this}});a.extend({tmpl:function(d,h,e,c){var j,k=!c;if(k){c=p;d=a.template[d]||a.template(null,d);f={}}else if(!d){d=c.tmpl;b[c.key]=c;c.nodes=[];c.wrapped&&n(c,c.wrapped);return a(i(c,null,c.tmpl(a,c)))}if(!d)return[];if(typeof h==="function")h=h.call(c||{});e&&e.wrapped&&n(e,e.wrapped);j=a.isArray(h)?a.map(h,function(a){return a?g(e,c,d,a):null}):[g(e,c,d,h)];return k?a(i(c,null,j)):j},tmplItem:function(b){var c;if(b instanceof a)b=b[0];while(b&&b.nodeType===1&&!(c=a.data(b,"tmplItem"))&&(b=b.parentNode));return c||p},template:function(c,b){if(b){if(typeof b==="string")b=o(b);else if(b instanceof a)b=b[0]||{};if(b.nodeType)b=a.data(b,"tmpl")||a.data(b,"tmpl",o(b.innerHTML));return typeof c==="string"?(a.template[c]=b):b}return c?typeof c!=="string"?a.template(null,c):a.template[c]||a.template(null,q.test(c)?c:a(c)):null},encode:function(a){return(""+a).split("<").join("<").split(">").join(">").split('"').join(""").split("'").join("'")}});a.extend(a.tmpl,{tag:{tmpl:{_default:{$2:"null"},open:"if($notnull_1){_=_.concat($item.nest($1,$2));}"},wrap:{_default:{$2:"null"},open:"$item.calls(_,$1,$2);_=[];",close:"call=$item.calls();_=call._.concat($item.wrap(call,_));"},each:{_default:{$2:"$index, $value"},open:"if($notnull_1){$.each($1a,function($2){with(this){",close:"}});}"},"if":{open:"if(($notnull_1) && $1a){",close:"}"},"else":{_default:{$1:"true"},open:"}else if(($notnull_1) && $1a){"},html:{open:"if($notnull_1){_.push($1a);}"},"=":{_default:{$1:"$data"},open:"if($notnull_1){_.push($.encode($1a));}"},"!":{open:""}},complete:function(){b={}},afterManip:function(f,b,d){var e=b.nodeType===11?a.makeArray(b.childNodes):b.nodeType===1?[b]:[];d.call(f,b);m(e);c++}});function i(e,g,f){var b,c=f?a.map(f,function(a){return typeof a==="string"?e.key?a.replace(/(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g,"$1 "+d+'="'+e.key+'" $2'):a:i(a,e,a._ctnt)}):e;if(g)return c;c=c.join("");c.replace(/^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/,function(f,c,e,d){b=a(e).get();m(b);if(c)b=j(c).concat(b);if(d)b=b.concat(j(d))});return b?b:j(c)}function j(c){var b=document.createElement("div");b.innerHTML=c;return a.makeArray(b.childNodes)}function o(b){return new Function("jQuery","$item","var $=jQuery,call,_=[],$data=$item.data;with($data){_.push('"+a.trim(b).replace(/([\\'])/g,"\\$1").replace(/[\r\t\n]/g," ").replace(/\$\{([^\}]*)\}/g,"{{= $1}}").replace(/\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g,function(m,l,j,d,b,c,e){var i=a.tmpl.tag[j],h,f,g;if(!i)throw"Template command not found: "+j;h=i._default||[];if(c&&!/\w$/.test(b)){b+=c;c=""}if(b){b=k(b);e=e?","+k(e)+")":c?")":"";f=c?b.indexOf(".")>-1?b+c:"("+b+").call($item"+e:b;g=c?f:"(typeof("+b+")==='function'?("+b+").call($item):("+b+"))"}else g=f=h.$1||"null";d=k(d);return"');"+i[l?"close":"open"].split("$notnull_1").join(b?"typeof("+b+")!=='undefined' && ("+b+")!=null":"true").split("$1a").join(g).split("$1").join(f).split("$2").join(d?d.replace(/\s*([^\(]+)\s*(\((.*?)\))?/g,function(d,c,b,a){a=a?","+a+")":b?")":"";return a?"("+c+").call($item"+a:d}):h.$2||"")+"_.push('"})+"');}return _;")}function n(c,b){c._wrap=i(c,true,a.isArray(b)?b:[q.test(b)?b:a(b).html()]).join("")}function k(a){return a?a.replace(/\\'/g,"'").replace(/\\\\/g,"\\"):null}function s(b){var a=document.createElement("div");a.appendChild(b.cloneNode(true));return a.innerHTML}function m(o){var n="_"+c,k,j,l={},e,p,i;for(e=0,p=o.length;e=0;i--)m(j[i]);m(k)}function m(j){var p,i=j,k,e,m;if(m=j.getAttribute(d)){while(i.parentNode&&(i=i.parentNode).nodeType===1&&!(p=i.getAttribute(d)));if(p!==m){i=i.parentNode?i.nodeType===11?0:i.getAttribute(d)||0:0;if(!(e=b[m])){e=f[m];e=g(e,b[i]||f[i],null,true);e.key=++h;b[h]=e}c&&o(m)}j.removeAttribute(d)}else if(c&&(e=a.data(j,"tmplItem"))){o(e.key);b[e.key]=e;i=a.data(j.parentNode,"tmplItem");i=i?i.key:0}if(e){k=e;while(k&&k.key!=i){k.nodes.push(j);k=k.parent}delete e._ctnt;delete e._wrap;a.data(j,"tmplItem",e)}function o(a){a=a+n;e=l[a]=l[a]||g(e,b[e.parent.key+n]||e.parent,null,true)}}}function u(a,d,c,b){if(!a)return l.pop();l.push({_:a,tmpl:d,item:this,data:c,options:b})}function w(d,c,b){return a.tmpl(a.template(d),c,b,this)}function x(b,d){var c=b.options||{};c.wrapped=d;return a.tmpl(a.template(b.tmpl),b.data,c,b.item)}function v(d,c){var b=this._wrap;return a.map(a(a.isArray(b)?b.join(""):b).filter(d||"*"),function(a){return c?a.innerText||a.textContent:a.outerHTML||s(a)})}function t(){var b=this.nodes;a.tmpl(null,null,null,this).insertBefore(b[0]);a(b).remove()}})(jQuery) \ No newline at end of file diff --git a/SignalrKayakGateDemo/public/Scripts/jquery-1.6.2-vsdoc.js b/SignalrKayakGateDemo/public/Scripts/jquery-1.6.2-vsdoc.js new file mode 100644 index 000000000..7b4fad84a --- /dev/null +++ b/SignalrKayakGateDemo/public/Scripts/jquery-1.6.2-vsdoc.js @@ -0,0 +1,6987 @@ +/* +* This file has been generated to support Visual Studio IntelliSense. +* You should not use this file at runtime inside the browser--it is only +* intended to be used only for design-time IntelliSense. Please use the +* standard jQuery library for all production use. +* +* Comment version: 1.6.2 +*/ + +/*! +* jQuery JavaScript Library v1.6.2 +* http://jquery.com/ +* +* Distributed in whole under the terms of the MIT +* +* Copyright 2010, John Resig +* +* 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. +* +* Includes Sizzle.js +* http://sizzlejs.com/ +* Copyright 2010, The Dojo Foundation +* Released under the MIT and BSD Licenses. +*/ + +(function ( window, undefined ) { +var jQuery = function( selector, context ) { +/// +/// 1: Accepts a string containing a CSS selector which is then used to match a set of elements. +/// 1.1 - $(selector, context) +/// 1.2 - $(element) +/// 1.3 - $(elementArray) +/// 1.4 - $(jQuery object) +/// 1.5 - $() +/// 2: Creates DOM elements on the fly from the provided string of raw HTML. +/// 2.1 - $(html, ownerDocument) +/// 2.2 - $(html, props) +/// 3: Binds a function to be executed when the DOM has finished loading. +/// 3.1 - $(callback) +/// +/// +/// A string containing a selector expression +/// +/// +/// A DOM Element, Document, or jQuery to use as context +/// +/// + + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }; +jQuery.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 ]( 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; + }; +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; +}; +jQuery._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; + }; +jQuery._data = function( elem, name, data ) { + + return jQuery.data( elem, name, data, true ); + }; +jQuery._mark = function( elem, type ) { + + if ( elem ) { + type = (type || "fx") + "mark"; + jQuery.data( elem, type, (jQuery.data(elem,type,undefined,true) || 0) + 1, true ); + } + }; +jQuery._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" ); + } + } + }; +jQuery.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.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; + }; +jQuery.active = 0; +jQuery.ajax = function( url, options ) { +/// +/// Perform an asynchronous HTTP (Ajax) request. +/// 1 - jQuery.ajax(url, settings) +/// 2 - jQuery.ajax(settings) +/// +/// +/// A string containing the URL to which the request is sent. +/// +/// +/// A set of key/value pairs that configure the Ajax request. All settings are optional. A default can be set for any option with $.ajaxSetup(). See jQuery.ajax( settings ) below for a complete list of all settings. +/// + + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + // Callbacks context + callbackContext = s.context || s, + // Context for global events + // It's the callbackContext if one was provided in the options + // and if it's a DOM node or a jQuery collection + globalEventContext = callbackContext !== s && + ( callbackContext.nodeType || callbackContext instanceof jQuery ) ? + jQuery( callbackContext ) : jQuery.event, + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery._Deferred(), + // Status-dependent callbacks + statusCode = s.statusCode || {}, + // ifModified key + ifModifiedKey, + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + // Response headers + responseHeadersString, + responseHeaders, + // transport + transport, + // timeout handle + timeoutTimer, + // Cross-domain detection vars + parts, + // The jqXHR state + state = 0, + // To know if global events are to be dispatched + fireGlobals, + // Loop variable + i, + // Fake xhr + jqXHR = { + + readyState: 0, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( !state ) { + var lname = name.toLowerCase(); + name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Raw string + getAllResponseHeaders: function() { + return state === 2 ? responseHeadersString : null; + }, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( state === 2 ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[1].toLowerCase() ] = match[ 2 ]; + } + } + match = responseHeaders[ key.toLowerCase() ]; + } + return match === undefined ? null : match; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( !state ) { + s.mimeType = type; + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + statusText = statusText || "abort"; + if ( transport ) { + transport.abort( statusText ); + } + done( 0, statusText ); + return this; + } + }; + + // Callback for when everything is done + // It is defined here because jslint complains if it is declared + // at the end of the function (which would be more logical and readable) + function done( status, statusText, responses, headers ) { + + // Called once + if ( state === 2 ) { + return; + } + + // State is "done" now + state = 2; + + // Clear timeout if it exists + if ( timeoutTimer ) { + clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status ? 4 : 0; + + var isSuccess, + success, + error, + response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined, + lastModified, + etag; + + // If successful, handle type chaining + if ( status >= 200 && status < 300 || status === 304 ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + + if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) { + jQuery.lastModified[ ifModifiedKey ] = lastModified; + } + if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) { + jQuery.etag[ ifModifiedKey ] = etag; + } + } + + // If not modified + if ( status === 304 ) { + + statusText = "notmodified"; + isSuccess = true; + + // If we have data + } else { + + try { + success = ajaxConvert( s, response ); + statusText = "success"; + isSuccess = true; + } catch(e) { + // We have a parsererror + statusText = "parsererror"; + error = e; + } + } + } else { + // We extract error from statusText + // then normalize statusText and status for non-aborts + error = statusText; + if( !statusText || status ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = statusText; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ), + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.resolveWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s] ); + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + // Attach deferreds + deferred.promise( jqXHR ); + jqXHR.success = jqXHR.done; + jqXHR.error = jqXHR.fail; + jqXHR.complete = completeDeferred.done; + + // Status-dependent callbacks + jqXHR.statusCode = function( map ) { + if ( map ) { + var tmp; + if ( state < 2 ) { + for( tmp in map ) { + statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ]; + } + } else { + tmp = map[ jqXHR.status ]; + jqXHR.then( tmp, tmp ); + } + } + return this; + }; + + // Remove hash character (#7531: and string promotion) + // Add protocol if not provided (#5866: IE7 issue with protocol-less urls) + // We also use the url parameter if available + s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); + + // Extract dataTypes list + s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax ); + + // Determine if a cross-domain request is in order + if ( s.crossDomain == null ) { + parts = rurl.exec( s.url.toLowerCase() ); + s.crossDomain = !!( parts && + ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] || + ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) != + ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) ) + ); + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefiler, stop there + if ( state === 2 ) { + return false; + } + + // We can fire global events as of now if asked to + fireGlobals = s.global; + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // If data is available, append data to url + if ( s.data ) { + s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data; + } + + // Get ifModifiedKey before adding the anti-cache parameter + ifModifiedKey = s.url; + + // Add anti-cache in url if needed + if ( s.cache === false ) { + + var ts = jQuery.now(), + // try replacing _= if it is there + ret = s.url.replace( rts, "$1_=" + ts ); + + // if nothing was replaced, add timestamp to the end + s.url = ret + ( (ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + ifModifiedKey = ifModifiedKey || s.url; + if ( jQuery.lastModified[ ifModifiedKey ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] ); + } + if ( jQuery.etag[ ifModifiedKey ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] ); + } + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ? + s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", */*; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { + // Abort if not done already + jqXHR.abort(); + return false; + + } + + // Install callbacks on deferreds + for ( i in { success: 1, error: 1, complete: 1 } ) { + jqXHR[ i ]( s[ i ] ); + } + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = setTimeout( function(){ + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + state = 1; + transport.send( requestHeaders, done ); + } catch (e) { + // Propagate exception as error if not done + if ( status < 2 ) { + done( -1, e ); + // Simply rethrow otherwise + } else { + jQuery.error( e ); + } + } + } + + return jqXHR; + }; +jQuery.ajaxPrefilter = function( dataTypeExpression, func ) { +/// +/// Handle custom Ajax options or modify existing options before each request is sent and before they are processed by $.ajax(). +/// +/// +/// An optional string containing one or more space-separated dataTypes +/// +/// +/// A handler to set default values for future Ajax requests. +/// +/// + + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + if ( jQuery.isFunction( func ) ) { + var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ), + i = 0, + length = dataTypes.length, + dataType, + list, + placeBefore; + + // For each dataType in the dataTypeExpression + for(; i < length; i++ ) { + dataType = dataTypes[ i ]; + // We control if we're asked to add before + // any existing element + placeBefore = /^\+/.test( dataType ); + if ( placeBefore ) { + dataType = dataType.substr( 1 ) || "*"; + } + list = structure[ dataType ] = structure[ dataType ] || []; + // then we add to the structure accordingly + list[ placeBefore ? "unshift" : "push" ]( func ); + } + } + }; +jQuery.ajaxSettings = { "url": 'http://localhost:25813/?ver=1.6.2&newLineMethod=para', +"isLocal": false, +"global": true, +"type": 'GET', +"contentType": 'application/x-www-form-urlencoded', +"processData": true, +"async": true, +"accepts": {}, +"contents": {}, +"responseFields": {}, +"converters": {}, +"jsonp": 'callback' }; +jQuery.ajaxSetup = function ( target, settings ) { +/// +/// Set default values for future Ajax requests. +/// +/// +/// A set of key/value pairs that configure the default Ajax request. All options are optional. +/// + + if ( !settings ) { + // Only one parameter, we extend ajaxSettings + settings = target; + target = jQuery.extend( true, jQuery.ajaxSettings, settings ); + } else { + // target was provided, we extend into it + jQuery.extend( true, target, jQuery.ajaxSettings, settings ); + } + // Flatten fields we don't want deep extended + for( var field in { context: 1, url: 1 } ) { + if ( field in settings ) { + target[ field ] = settings[ field ]; + } else if( field in jQuery.ajaxSettings ) { + target[ field ] = jQuery.ajaxSettings[ field ]; + } + } + return target; + }; +jQuery.ajaxTransport = function( dataTypeExpression, func ) { + + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + if ( jQuery.isFunction( func ) ) { + var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ), + i = 0, + length = dataTypes.length, + dataType, + list, + placeBefore; + + // For each dataType in the dataTypeExpression + for(; i < length; i++ ) { + dataType = dataTypes[ i ]; + // We control if we're asked to add before + // any existing element + placeBefore = /^\+/.test( dataType ); + if ( placeBefore ) { + dataType = dataType.substr( 1 ) || "*"; + } + list = structure[ dataType ] = structure[ dataType ] || []; + // then we add to the structure accordingly + list[ placeBefore ? "unshift" : "push" ]( func ); + } + } + }; +jQuery.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 formHook for forms and if the name contains certain characters + } else if ( formHook && name !== "className" && + (jQuery.nodeName( elem, "form" ) || rinvalidChar.test( name )) ) { + + hooks = formHook; + } + } + } + + 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; + } + }; +jQuery.attrFix = { "tabindex": 'tabIndex' }; +jQuery.attrFn = { "val": true, +"css": true, +"html": true, +"text": true, +"data": true, +"width": true, +"height": true, +"offset": true, +"blur": true, +"focus": true, +"focusin": true, +"focusout": true, +"load": true, +"resize": true, +"scroll": true, +"unload": true, +"click": true, +"dblclick": true, +"mousedown": true, +"mouseup": true, +"mousemove": true, +"mouseover": true, +"mouseout": true, +"mouseenter": true, +"mouseleave": true, +"change": true, +"select": true, +"submit": true, +"keydown": true, +"keypress": true, +"keyup": true, +"error": true }; +jQuery.attrHooks = { "type": {}, +"tabIndex": {}, +"value": {} }; +jQuery.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(); + } + } + }; +jQuery.boxModel = true; +jQuery.browser = { "msie": true, +"version": '9.0' }; +jQuery.buildFragment = function( args, nodes, scripts ) { + + var fragment, cacheable, cacheresults, doc; + + // nodes may contain either an explicit document object, + // a jQuery collection or context object. + // If nodes[0] contains a valid object to assign to doc + if ( nodes && nodes[0] ) { + doc = nodes[0].ownerDocument || nodes[0]; + } + + // Ensure that an attr object doesn't incorrectly stand in as a document object + // Chrome and Firefox seem to allow this to occur and will throw exception + // Fixes #8950 + if ( !doc.createDocumentFragment ) { + doc = document; + } + + // Only cache "small" (1/2 KB) HTML strings that are associated with the main document + // Cloning options loses the selected state, so don't cache them + // IE 6 doesn't like it when you put or elements in a fragment + // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache + if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document && + args[0].charAt(0) === "<" && !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) { + + cacheable = true; + + cacheresults = jQuery.fragments[ args[0] ]; + if ( cacheresults && cacheresults !== 1 ) { + fragment = cacheresults; + } + } + + if ( !fragment ) { + fragment = doc.createDocumentFragment(); + jQuery.clean( args, doc, fragment, scripts ); + } + + if ( cacheable ) { + jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1; + } + + return { fragment: fragment, cacheable: cacheable }; +}; +jQuery.cache = {}; +jQuery.camelCase = function( string ) { + + return string.replace( rdashAlpha, fcamelCase ); + }; +jQuery.clean = function( elems, context, fragment, scripts ) { + + var checkScriptType; + + context = context || document; + + // !context.createElement fails in IE with an error but returns typeof 'object' + if ( typeof context.createElement === "undefined" ) { + context = context.ownerDocument || context[0] && context[0].ownerDocument || document; + } + + var ret = [], j; + + for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { + if ( typeof elem === "number" ) { + elem += ""; + } + + if ( !elem ) { + continue; + } + + // Convert html string into DOM nodes + if ( typeof elem === "string" ) { + if ( !rhtml.test( elem ) ) { + elem = context.createTextNode( elem ); + } else { + // Fix "XHTML"-style tags in all browsers + elem = elem.replace(rxhtmlTag, "<$1>"); + + // Trim whitespace, otherwise indexOf won't work as expected + var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(), + wrap = wrapMap[ tag ] || wrapMap._default, + depth = wrap[0], + div = context.createElement("div"); + + // Go to html and back, then peel off extra wrappers + div.innerHTML = wrap[1] + elem + wrap[2]; + + // Move to the right depth + while ( depth-- ) { + div = div.lastChild; + } + + // Remove IE's autoinserted from table fragments + if ( !jQuery.support.tbody ) { + + // String was a , *may* have spurious + var hasBody = rtbody.test(elem), + tbody = tag === "table" && !hasBody ? + div.firstChild && div.firstChild.childNodes : + + // String was a bare or + wrap[1] === "
    " && !hasBody ? + div.childNodes : + []; + + for ( j = tbody.length - 1; j >= 0 ; --j ) { + if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) { + tbody[ j ].parentNode.removeChild( tbody[ j ] ); + } + } + } + + // IE completely kills leading whitespace when innerHTML is used + if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild ); + } + + elem = div.childNodes; + } + } + + // Resets defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + var len; + if ( !jQuery.support.appendChecked ) { + if ( elem[0] && typeof (len = elem.length) === "number" ) { + for ( j = 0; j < len; j++ ) { + findInputs( elem[j] ); + } + } else { + findInputs( elem ); + } + } + + if ( elem.nodeType ) { + ret.push( elem ); + } else { + ret = jQuery.merge( ret, elem ); + } + } + + if ( fragment ) { + checkScriptType = function( elem ) { + return !elem.type || rscriptType.test( elem.type ); + }; + for ( i = 0; ret[i]; i++ ) { + if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) { + scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] ); + + } else { + if ( ret[i].nodeType === 1 ) { + var jsTags = jQuery.grep( ret[i].getElementsByTagName( "script" ), checkScriptType ); + + ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) ); + } + fragment.appendChild( ret[i] ); + } + } + } + + return ret; + }; +jQuery.cleanData = function( elems ) { + + var data, id, cache = jQuery.cache, internalKey = jQuery.expando, special = jQuery.event.special, + deleteExpando = jQuery.support.deleteExpando; + + for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { + if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) { + continue; + } + + id = elem[ jQuery.expando ]; + + if ( id ) { + data = cache[ id ] && cache[ id ][ internalKey ]; + + if ( data && data.events ) { + for ( var type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + + // Null the DOM reference to avoid IE6/7/8 leak (#7054) + if ( data.handle ) { + data.handle.elem = null; + } + } + + if ( deleteExpando ) { + delete elem[ jQuery.expando ]; + + } else if ( elem.removeAttribute ) { + elem.removeAttribute( jQuery.expando ); + } + + delete cache[ id ]; + } + } + }; +jQuery.clone = function( elem, dataAndEvents, deepDataAndEvents ) { + + var clone = elem.cloneNode(true), + srcElements, + destElements, + i; + + if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + // IE copies events bound via attachEvent when using cloneNode. + // Calling detachEvent on the clone will also remove the events + // from the original. In order to get around this, we use some + // proprietary methods to clear the events. Thanks to MooTools + // guys for this hotness. + + cloneFixAttributes( elem, clone ); + + // Using Sizzle here is crazy slow, so we use getElementsByTagName + // instead + srcElements = getAll( elem ); + destElements = getAll( clone ); + + // Weird iteration because IE will replace the length property + // with an element if you are cloning the body and one of the + // elements on the page has a name or id of "length" + for ( i = 0; srcElements[i]; ++i ) { + cloneFixAttributes( srcElements[i], destElements[i] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + cloneCopyEvent( elem, clone ); + + if ( deepDataAndEvents ) { + srcElements = getAll( elem ); + destElements = getAll( clone ); + + for ( i = 0; srcElements[i]; ++i ) { + cloneCopyEvent( srcElements[i], destElements[i] ); + } + } + } + + srcElements = destElements = null; + + // Return the cloned set + return clone; + }; +jQuery.contains = function( a, b ) { +/// +/// Check to see if a DOM node is within another DOM node. +/// +/// +/// The DOM element that may contain the other element. +/// +/// +/// The DOM node that may be contained by the other element. +/// +/// + + return a !== b && (a.contains ? a.contains(b) : true); + }; +jQuery.css = function( elem, name, extra ) { + + var ret, hooks; + + // Make sure that we're working with the right name + name = jQuery.camelCase( name ); + hooks = jQuery.cssHooks[ name ]; + name = jQuery.cssProps[ name ] || name; + + // cssFloat needs a special treatment + if ( name === "cssFloat" ) { + name = "float"; + } + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) { + return ret; + + // Otherwise, if a way to get the computed value exists, use that + } else if ( curCSS ) { + return curCSS( elem, name ); + } + }; +jQuery.cssHooks = { "opacity": {}, +"height": {}, +"width": {} }; +jQuery.cssNumber = { "fillOpacity": true, +"fontWeight": true, +"lineHeight": true, +"opacity": true, +"orphans": true, +"widows": true, +"zIndex": true, +"zoom": true }; +jQuery.cssProps = { "float": 'cssFloat' }; +jQuery.curCSS = function( elem, name, extra ) { + + var ret, hooks; + + // Make sure that we're working with the right name + name = jQuery.camelCase( name ); + hooks = jQuery.cssHooks[ name ]; + name = jQuery.cssProps[ name ] || name; + + // cssFloat needs a special treatment + if ( name === "cssFloat" ) { + name = "float"; + } + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) { + return ret; + + // Otherwise, if a way to get the computed value exists, use that + } else if ( curCSS ) { + return curCSS( elem, name ); + } + }; +jQuery.data = function( elem, name, data, pvt /* Internal Use Only */ ) { +/// +/// 1: Store arbitrary data associated with the specified element. Returns the value that was set. +/// 1.1 - jQuery.data(element, key, value) +/// 2: Returns value at named data store for the element, as set by jQuery.data(element, name, value), or the full data store for the element. +/// 2.1 - jQuery.data(element, key) +/// 2.2 - jQuery.data(element) +/// +/// +/// The DOM element to associate with the data. +/// +/// +/// A string naming the piece of data to set. +/// +/// +/// The new data value. +/// +/// + + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache, + + // 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 ][ 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; + } + + return getByName ? + // Check for both converted-to-camel and non-converted data property names + thisCache[ jQuery.camelCase( name ) ] || thisCache[ name ] : + thisCache; + }; +jQuery.dequeue = function( elem, type ) { +/// +/// Execute the next function on the queue for the matched element. +/// +/// +/// A DOM element from which to remove and execute a queued function. +/// +/// +/// A string containing the name of the queue. Defaults to fx, the standard effects queue. +/// +/// + + 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.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; + }; +jQuery.each = function( object, callback, args ) { +/// +/// A generic iterator function, which can be used to seamlessly iterate over both objects and arrays. Arrays and array-like objects with a length property (such as a function's arguments object) are iterated by numeric index, from 0 to length-1. Other objects are iterated via their named properties. +/// +/// +/// The object or array to iterate over. +/// +/// +/// The function that will be executed on every object. +/// +/// + + 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; + }; +jQuery.easing = {}; +jQuery.error = function( msg ) { +/// +/// Takes a string and throws an exception containing it. +/// +/// +/// The message to send out. +/// + + throw msg; + }; +jQuery.etag = {}; +jQuery.event = { "global": {}, +"customEvent": {}, +"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'], +"guid": 100000000, +"special": {}, +"triggered": }; +jQuery.expr = { "order": ['ID','CLASS','NAME','TAG'], +"match": {}, +"leftMatch": {}, +"attrMap": {}, +"attrHandle": {}, +"relative": {}, +"find": {}, +"preFilter": {}, +"filters": {}, +"setFilters": {}, +"filter": {}, +":": {} }; +jQuery.extend = function() { +/// +/// Merge the contents of two or more objects together into the first object. +/// 1 - jQuery.extend(target, object1, objectN) +/// 2 - jQuery.extend(deep, target, object1, objectN) +/// +/// +/// If true, the merge becomes recursive (aka. deep copy). +/// +/// +/// The object to extend. It will receive the new properties. +/// +/// +/// An object containing additional properties to merge in. +/// +/// +/// Additional objects containing properties to merge in. +/// +/// + + 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.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); + }; +jQuery.find = 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); + }; +jQuery.fn = { "selector": '', +"jquery": '1.6.2', +"length": 0 }; +jQuery.fragments = {}; +jQuery.fx = function( elem, options, prop ) { + + this.options = options; + this.elem = elem; + this.prop = prop; + + options.orig = options.orig || {}; + }; +jQuery.get = function( url, data, callback, type ) { +/// +/// Load data from the server using a HTTP GET request. +/// +/// +/// A string containing the URL to which the request is sent. +/// +/// +/// A map or string that is sent to the server with the request. +/// +/// +/// A callback function that is executed if the request succeeds. +/// +/// +/// The type of data expected from the server. Default: Intelligent Guess (xml, json, script, or html). +/// + + // shift arguments if data argument was omitted + if ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + return jQuery.ajax({ + type: method, + url: url, + data: data, + success: callback, + dataType: type + }); + }; +jQuery.getJSON = function( url, data, callback ) { +/// +/// Load JSON-encoded data from the server using a GET HTTP request. +/// +/// +/// A string containing the URL to which the request is sent. +/// +/// +/// A map or string that is sent to the server with the request. +/// +/// +/// A callback function that is executed if the request succeeds. +/// + + return jQuery.get( url, data, callback, "json" ); + }; +jQuery.getScript = function( url, callback ) { +/// +/// Load a JavaScript file from the server using a GET HTTP request, then execute it. +/// +/// +/// A string containing the URL to which the request is sent. +/// +/// +/// A callback function that is executed if the request succeeds. +/// +/// + + return jQuery.get( url, undefined, callback, "script" ); + }; +jQuery.globalEval = function( data ) { +/// +/// Execute some JavaScript code globally. +/// +/// +/// The JavaScript code to execute. +/// + + 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 ); + } + }; +jQuery.grep = function( elems, callback, inv ) { +/// +/// Finds the elements of an array which satisfy a filter function. The original array is not affected. +/// +/// +/// The array to search through. +/// +/// +/// The function to process each item against. The first argument to the function is the item, and the second argument is the index. The function should return a Boolean value. this will be the global window object. +/// +/// +/// If "invert" is false, or not provided, then the function returns an array consisting of all elements for which "callback" returns true. If "invert" is true, then the function returns an array consisting of all elements for which "callback" returns false. +/// +/// + + 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; + }; +jQuery.guid = 1; +jQuery.hasData = function( elem ) { +/// +/// Determine whether an element has any jQuery data associated with it. +/// +/// +/// A DOM element to be checked for data. +/// +/// + + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + + return !!elem && !isEmptyDataObject( elem ); + }; +jQuery.holdReady = function( hold ) { +/// +/// Holds or releases the execution of jQuery's ready event. +/// +/// +/// Indicates whether the ready hold is being requested or released +/// +/// + + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }; +jQuery.inArray = function( elem, array ) { +/// +/// Search for a specified value within an array and return its index (or -1 if not found). +/// +/// +/// The value to search for. +/// +/// +/// An array through which to search. +/// +/// + + + 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; + }; +jQuery.isEmptyObject = function( obj ) { +/// +/// Check to see if an object is empty (contains no properties). +/// +/// +/// The object that will be checked to see if it's empty. +/// +/// + + for ( var name in obj ) { + return false; + } + return true; + }; +jQuery.isFunction = function( obj ) { +/// +/// Determine if the argument passed is a Javascript function object. +/// +/// +/// Object to test whether or not it is a function. +/// +/// + + return jQuery.type(obj) === "function"; + }; +jQuery.isNaN = function( obj ) { + + return obj == null || !rdigit.test( obj ) || isNaN( obj ); + }; +jQuery.isPlainObject = function( obj ) { +/// +/// Check to see if an object is a plain object (created using "{}" or "new Object"). +/// +/// +/// The object that will be checked to see if it's a plain object. +/// +/// + + // 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; + } + + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + 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 ); + }; +jQuery.isReady = true; +jQuery.isWindow = function( obj ) { +/// +/// Determine whether the argument is a window. +/// +/// +/// Object to test whether or not it is a window. +/// +/// + + return obj && typeof obj === "object" && "setInterval" in obj; + }; +jQuery.isXMLDoc = function( elem ) { +/// +/// Check to see if a DOM node is within an XML document (or is an XML document). +/// +/// +/// The DOM node that will be checked to see if it's in an XML document. +/// +/// + + // 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; +}; +jQuery.lastModified = {}; +jQuery.makeArray = function( array, results ) { +/// +/// Convert an array-like object into a true JavaScript array. +/// +/// +/// Any object to turn into a native Array. +/// +/// + + 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; + }; +jQuery.map = function( elems, callback, arg ) { +/// +/// Translate all items in an array or object to new array of items. +/// 1 - jQuery.map(array, callback(elementOfArray, indexInArray)) +/// 2 - jQuery.map(arrayOrObject, callback( value, indexOrKey )) +/// +/// +/// The Array to translate. +/// +/// +/// The function to process each item against. The first argument to the function is the array item, the second argument is the index in array The function can return any value. Within the function, this refers to the global (window) object. +/// +/// + + 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 ); + }; +jQuery.merge = function( first, second ) { +/// +/// Merge the contents of two arrays together into the first array. +/// +/// +/// The first array to merge, the elements of second added. +/// +/// +/// The second array to merge into the first, unaltered. +/// +/// + + 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; + }; +jQuery.noConflict = function( deep ) { +/// +/// Relinquish jQuery's control of the $ variable. +/// +/// +/// A Boolean indicating whether to remove all jQuery variables from the global scope (including jQuery itself). +/// +/// + + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }; +jQuery.noData = { "embed": true, +"object": 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000', +"applet": true }; +jQuery.nodeName = function( elem, name ) { + + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }; +jQuery.noop = function() { +/// +/// An empty function. +/// +/// +}; +jQuery.now = function() { +/// +/// Return a number representing the current time. +/// +/// + + return (new Date()).getTime(); + }; +jQuery.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; + }; +jQuery.offset = {}; +jQuery.param = function( a, traditional ) { +/// +/// Create a serialized representation of an array or object, suitable for use in a URL query string or Ajax request. +/// 1 - jQuery.param(obj) +/// 2 - jQuery.param(obj, traditional) +/// +/// +/// An array or object to serialize. +/// +/// +/// A Boolean indicating whether to perform a traditional "shallow" serialization. +/// +/// + + var s = [], + add = function( key, value ) { + // If value is a function, invoke it and return its value + value = jQuery.isFunction( value ) ? value() : value; + s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value ); + }; + + // Set traditional to true for jQuery <= 1.3.2 behavior. + if ( traditional === undefined ) { + traditional = jQuery.ajaxSettings.traditional; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + }); + + } else { + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( var prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ).replace( r20, "+" ); + }; +jQuery.parseJSON = function( data ) { +/// +/// Takes a well-formed JSON string and returns the resulting JavaScript object. +/// +/// +/// The JSON string to parse. +/// +/// + + 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 ); + }; +jQuery.parseXML = function( data , xml , tmp ) { +/// +/// Parses a string into an XML document. +/// +/// +/// a well-formed XML string to be parsed +/// +/// + + + 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 ); + } + + tmp = xml.documentElement; + + if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) { + jQuery.error( "Invalid XML: " + data ); + } + + return xml; + }; +jQuery.post = function( url, data, callback, type ) { +/// +/// Load data from the server using a HTTP POST request. +/// +/// +/// A string containing the URL to which the request is sent. +/// +/// +/// A map or string that is sent to the server with the request. +/// +/// +/// A callback function that is executed if the request succeeds. +/// +/// +/// The type of data expected from the server. Default: Intelligent Guess (xml, json, script, or html). +/// + + // shift arguments if data argument was omitted + if ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + return jQuery.ajax({ + type: method, + url: url, + data: data, + success: callback, + dataType: type + }); + }; +jQuery.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 )) !== undefined ) { + return ret; + + } else { + return elem[ name ]; + } + } + }; +jQuery.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' }; +jQuery.propHooks = { "selected": {} }; +jQuery.proxy = function( fn, context ) { +/// +/// Takes a function and returns a new one that will always have a particular context. +/// 1 - jQuery.proxy(function, context) +/// 2 - jQuery.proxy(context, name) +/// +/// +/// The function whose context will be changed. +/// +/// +/// The object to which the context (this) of the function should be set. +/// +/// + + 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; + }; +jQuery.queue = function( elem, type, data ) { +/// +/// 1: Show the queue of functions to be executed on the matched element. +/// 1.1 - jQuery.queue(element, queueName) +/// 2: Manipulate the queue of functions to be executed on the matched element. +/// 2.1 - jQuery.queue(element, queueName, newQueue) +/// 2.2 - jQuery.queue(element, queueName, callback()) +/// +/// +/// A DOM element where the array of queued functions is attached. +/// +/// +/// A string containing the name of the queue. Defaults to fx, the standard effects queue. +/// +/// +/// An array of functions to replace the current queue contents. +/// +/// + + 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 || []; + } + }; +jQuery.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" ); + } + } + }; +jQuery.readyWait = 0; +jQuery.removeAttr = function( elem, name ) { + + var propName; + if ( elem.nodeType === 1 ) { + name = jQuery.attrFix[ name ] || name; + + if ( jQuery.support.getSetAttribute ) { + // Use removeAttribute in browsers that support it + elem.removeAttribute( name ); + } else { + jQuery.attr( elem, name, "" ); + elem.removeAttributeNode( elem.getAttributeNode( name ) ); + } + + // Set corresponding property to false for boolean attributes + if ( rboolean.test( name ) && (propName = jQuery.propFix[ name ] || name) in elem ) { + elem[ propName ] = false; + } + } + }; +jQuery.removeData = function( elem, name, pvt /* Internal Use Only */ ) { +/// +/// Remove a previously-stored piece of data. +/// +/// +/// A DOM element from which to remove data. +/// +/// +/// A string naming the piece of data to remove. +/// +/// + + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var 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 ) { + var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ]; + + if ( thisCache ) { + 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 + if ( jQuery.support.deleteExpando || cache != window ) { + 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; + } + } + }; +jQuery.removeEvent = function( elem, type, handle ) { + + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + }; +jQuery.sibling = function( n, elem ) { + + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + }; +jQuery.speed = function( speed, easing, fn ) { + + var opt = speed && typeof speed === "object" ? jQuery.extend({}, speed) : { + complete: fn || !fn && easing || + jQuery.isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !jQuery.isFunction(easing) && easing + }; + + opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : + opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[opt.duration] : jQuery.fx.speeds._default; + + // Queueing + opt.old = opt.complete; + opt.complete = function( noUnmark ) { + if ( jQuery.isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue !== false ) { + jQuery.dequeue( this ); + } else if ( noUnmark !== false ) { + jQuery._unmark( this ); + } + }; + + return opt; + }; +jQuery.style = function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, origName = jQuery.camelCase( name ), + style = elem.style, hooks = jQuery.cssHooks[ origName ]; + + name = jQuery.cssProps[ origName ] || origName; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Make sure that NaN and null values aren't set. See: #7116 + if ( type === "number" && isNaN( value ) || value == null ) { + return; + } + + // convert relative number strings (+= or -=) to relative numbers. #7345 + if ( type === "string" && rrelNum.test( value ) ) { + value = +value.replace( rrelNumFilter, "" ) + parseFloat( jQuery.css( elem, name ) ); + // Fixes bug #9237 + type = "number"; + } + + // If a number was passed in, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) { + // Wrapped to prevent IE from throwing errors when 'invalid' values are provided + // Fixes bug #5509 + try { + style[ name ] = value; + } catch(e) {} + } + + } else { + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }; +jQuery.sub = function() { +/// +/// Creates a new copy of jQuery whose properties and methods can be modified without affecting the original jQuery object. +/// +/// + + 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; + }; +jQuery.support = { "leadingWhitespace": true, +"tbody": true, +"htmlSerialize": true, +"style": true, +"hrefNormalized": true, +"opacity": true, +"cssFloat": true, +"checkOn": true, +"optSelected": false, +"getSetAttribute": true, +"submitBubbles": true, +"changeBubbles": true, +"focusinBubbles": true, +"deleteExpando": true, +"noCloneEvent": true, +"inlineBlockNeedsLayout": false, +"shrinkWrapBlocks": false, +"reliableMarginRight": true, +"noCloneChecked": false, +"optDisabled": true, +"radioValue": false, +"checkClone": , +"appendChecked": true, +"boxModel": true, +"reliableHiddenOffsets": true, +"ajax": true, +"cors": false }; +jQuery.swap = function( elem, options, callback ) { + + var old = {}; + + // Remember the old values, and insert the new ones + for ( var name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + }; +jQuery.text = 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; +}; +jQuery.trim = function( text ) { +/// +/// Remove the whitespace from the beginning and end of a string. +/// +/// +/// The string to trim. +/// +/// + + return text == null ? + "" : + trim.call( text ); + }; +jQuery.type = function( obj ) { +/// +/// Determine the internal JavaScript [[Class]] of an object. +/// +/// +/// Object to get the internal JavaScript [[Class]] of. +/// +/// + + return obj == null ? + String( obj ) : + class2type[ toString.call(obj) ] || "object"; + }; +jQuery.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" }; + }; +jQuery.unique = function( results ) { +/// +/// Sorts an array of DOM elements, in place, with the duplicates removed. Note that this only works on arrays of DOM elements, not strings or numbers. +/// +/// +/// The Array of DOM elements. +/// +/// + + 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; +}; +jQuery.uuid = 0; +jQuery.valHooks = { "option": {}, +"select": {}, +"radio": {}, +"checkbox": {} }; +jQuery.when = function( firstParam ) { +/// +/// Provides a way to execute callback functions based on one or more objects, usually Deferred objects that represent asynchronous events. +/// +/// +/// One or more Deferred objects, or plain JavaScript objects. +/// +/// + + 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.Event.prototype.isDefaultPrevented = function returnFalse() { +/// +/// Returns whether event.preventDefault() was ever called on this event object. +/// +/// + + return false; +}; +jQuery.Event.prototype.isImmediatePropagationStopped = function returnFalse() { +/// +/// Returns whether event.stopImmediatePropagation() was ever called on this event object. +/// +/// + + return false; +}; +jQuery.Event.prototype.isPropagationStopped = function returnFalse() { +/// +/// Returns whether event.stopPropagation() was ever called on this event object. +/// +/// + + return false; +}; +jQuery.Event.prototype.preventDefault = function() { +/// +/// If this method is called, the default action of the event will not be triggered. +/// +/// + + 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; + } + }; +jQuery.Event.prototype.stopImmediatePropagation = function() { +/// +/// Keeps the rest of the handlers from being executed and prevents the event from bubbling up the DOM tree. +/// + + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }; +jQuery.Event.prototype.stopPropagation = function() { +/// +/// Prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event. +/// + + 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; + }; +jQuery.prototype._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 ); + }; +jQuery.prototype.add = function( selector, context ) { +/// +/// Add elements to the set of matched elements. +/// 1 - add(selector) +/// 2 - add(elements) +/// 3 - add(html) +/// 4 - add(jQuery object) +/// 5 - add(selector, context) +/// +/// +/// A string representing a selector expression to find additional elements to add to the set of matched elements. +/// +/// +/// The point in the document at which the selector should begin matching; similar to the context argument of the $(selector, context) method. +/// +/// + + 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 ) ); + }; +jQuery.prototype.addClass = function( value ) { +/// +/// Adds the specified class(es) to each of the set of matched elements. +/// 1 - addClass(className) +/// 2 - addClass(function(index, currentClass)) +/// +/// +/// One or more class names to be added to the class attribute of each matched element. +/// +/// + + 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; + }; +jQuery.prototype.after = function() { +/// +/// Insert content, specified by the parameter, after each element in the set of matched elements. +/// 1 - after(content, content) +/// 2 - after(function(index)) +/// +/// +/// HTML string, DOM element, or jQuery object to insert after each element in the set of matched elements. +/// +/// +/// One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert after each element in the set of matched elements. +/// +/// + + if ( this[0] && this[0].parentNode ) { + return this.domManip(arguments, false, function( elem ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + }); + } else if ( arguments.length ) { + var set = this.pushStack( this, "after", arguments ); + set.push.apply( set, jQuery(arguments[0]).toArray() ); + return set; + } + }; +jQuery.prototype.ajaxComplete = function( f ){ +/// +/// Register a handler to be called when Ajax requests complete. This is an Ajax Event. +/// +/// +/// The function to be invoked. +/// +/// + + return this.bind( o, f ); + }; +jQuery.prototype.ajaxError = function( f ){ +/// +/// Register a handler to be called when Ajax requests complete with an error. This is an Ajax Event. +/// +/// +/// The function to be invoked. +/// +/// + + return this.bind( o, f ); + }; +jQuery.prototype.ajaxSend = function( f ){ +/// +/// Attach a function to be executed before an Ajax request is sent. This is an Ajax Event. +/// +/// +/// The function to be invoked. +/// +/// + + return this.bind( o, f ); + }; +jQuery.prototype.ajaxStart = function( f ){ +/// +/// Register a handler to be called when the first Ajax request begins. This is an Ajax Event. +/// +/// +/// The function to be invoked. +/// +/// + + return this.bind( o, f ); + }; +jQuery.prototype.ajaxStop = function( f ){ +/// +/// Register a handler to be called when all Ajax requests have completed. This is an Ajax Event. +/// +/// +/// The function to be invoked. +/// +/// + + return this.bind( o, f ); + }; +jQuery.prototype.ajaxSuccess = function( f ){ +/// +/// Attach a function to be executed whenever an Ajax request completes successfully. This is an Ajax Event. +/// +/// +/// The function to be invoked. +/// +/// + + return this.bind( o, f ); + }; +jQuery.prototype.andSelf = function() { +/// +/// Add the previous set of elements on the stack to the current set. +/// +/// + + return this.add( this.prevObject ); + }; +jQuery.prototype.animate = function( prop, speed, easing, callback ) { +/// +/// Perform a custom animation of a set of CSS properties. +/// 1 - animate(properties, duration, easing, complete) +/// 2 - animate(properties, options) +/// +/// +/// A map of CSS properties that the animation will move toward. +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + var optall = jQuery.speed(speed, easing, callback); + + if ( jQuery.isEmptyObject( prop ) ) { + return this.each( optall.complete, [ false ] ); + } + + // Do not change referenced properties as per-property easing will be lost + prop = jQuery.extend( {}, prop ); + + return this[ optall.queue === false ? "each" : "queue" ](function() { + // XXX 'this' does not always have a nodeName when running the + // test suite + + if ( optall.queue === false ) { + jQuery._mark( this ); + } + + var opt = jQuery.extend( {}, optall ), + isElement = this.nodeType === 1, + hidden = isElement && jQuery(this).is(":hidden"), + name, val, p, + display, e, + parts, start, end, unit; + + // will store per property easing and be used to determine when an animation is complete + opt.animatedProperties = {}; + + for ( p in prop ) { + + // property name normalization + name = jQuery.camelCase( p ); + if ( p !== name ) { + prop[ name ] = prop[ p ]; + delete prop[ p ]; + } + + val = prop[ name ]; + + // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default) + if ( jQuery.isArray( val ) ) { + opt.animatedProperties[ name ] = val[ 1 ]; + val = prop[ name ] = val[ 0 ]; + } else { + opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing'; + } + + if ( val === "hide" && hidden || val === "show" && !hidden ) { + return opt.complete.call( this ); + } + + if ( isElement && ( name === "height" || name === "width" ) ) { + // Make sure that nothing sneaks out + // Record all 3 overflow attributes because IE does not + // change the overflow attribute when overflowX and + // overflowY are set to the same value + opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ]; + + // Set display property to inline-block for height/width + // animations on inline elements that are having width/height + // animated + if ( jQuery.css( this, "display" ) === "inline" && + jQuery.css( this, "float" ) === "none" ) { + if ( !jQuery.support.inlineBlockNeedsLayout ) { + this.style.display = "inline-block"; + + } else { + display = defaultDisplay( this.nodeName ); + + // inline-level elements accept inline-block; + // block-level elements need to be inline with layout + if ( display === "inline" ) { + this.style.display = "inline-block"; + + } else { + this.style.display = "inline"; + this.style.zoom = 1; + } + } + } + } + } + + if ( opt.overflow != null ) { + this.style.overflow = "hidden"; + } + + for ( p in prop ) { + e = new jQuery.fx( this, opt, p ); + val = prop[ p ]; + + if ( rfxtypes.test(val) ) { + e[ val === "toggle" ? hidden ? "show" : "hide" : val ](); + + } else { + parts = rfxnum.exec( val ); + start = e.cur(); + + if ( parts ) { + end = parseFloat( parts[2] ); + unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" ); + + // We need to compute starting value + if ( unit !== "px" ) { + jQuery.style( this, p, (end || 1) + unit); + start = ((end || 1) / e.cur()) * start; + jQuery.style( this, p, start + unit); + } + + // If a +=/-= token was provided, we're doing a relative animation + if ( parts[1] ) { + end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start; + } + + e.custom( start, end, unit ); + + } else { + e.custom( start, val, "" ); + } + } + } + + // For JS strict compliance + return true; + }); + }; +jQuery.prototype.append = function() { +/// +/// Insert content, specified by the parameter, to the end of each element in the set of matched elements. +/// 1 - append(content, content) +/// 2 - append(function(index, html)) +/// +/// +/// DOM element, HTML string, or jQuery object to insert at the end of each element in the set of matched elements. +/// +/// +/// One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert at the end of each element in the set of matched elements. +/// +/// + + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 ) { + this.appendChild( elem ); + } + }); + }; +jQuery.prototype.appendTo = function( selector ) { +/// +/// Insert every element in the set of matched elements to the end of the target. +/// +/// +/// A selector, element, HTML string, or jQuery object; the matched set of elements will be inserted at the end of the element(s) specified by this parameter. +/// +/// + + var ret = [], + insert = jQuery( selector ), + parent = this.length === 1 && this[0].parentNode; + + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + return this; + + } else { + for ( var i = 0, l = insert.length; i < l; i++ ) { + var elems = (i > 0 ? this.clone(true) : this).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +jQuery.prototype.attr = function( name, value ) { +/// +/// 1: Get the value of an attribute for the first element in the set of matched elements. +/// 1.1 - attr(attributeName) +/// 2: Set one or more attributes for the set of matched elements. +/// 2.1 - attr(attributeName, value) +/// 2.2 - attr(map) +/// 2.3 - attr(attributeName, function(index, attr)) +/// +/// +/// The name of the attribute to set. +/// +/// +/// A value to set for the attribute. +/// +/// + + return jQuery.access( this, name, value, true, jQuery.attr ); + }; +jQuery.prototype.before = function() { +/// +/// Insert content, specified by the parameter, before each element in the set of matched elements. +/// 1 - before(content, content) +/// 2 - before(function) +/// +/// +/// HTML string, DOM element, or jQuery object to insert before each element in the set of matched elements. +/// +/// +/// One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert before each element in the set of matched elements. +/// +/// + + if ( this[0] && this[0].parentNode ) { + return this.domManip(arguments, false, function( elem ) { + this.parentNode.insertBefore( elem, this ); + }); + } else if ( arguments.length ) { + var set = jQuery(arguments[0]); + set.push.apply( set, this.toArray() ); + return this.pushStack( set, "before", arguments ); + } + }; +jQuery.prototype.bind = function( type, data, fn ) { +/// +/// Attach a handler to an event for the elements. +/// 1 - bind(eventType, eventData, handler(eventObject)) +/// 2 - bind(eventType, eventData, false) +/// 3 - bind(events) +/// +/// +/// A string containing one or more JavaScript event types, such as "click" or "submit," or custom event names. +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + 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.prototype.blur = function( data, fn ) { +/// +/// Bind an event handler to the "blur" JavaScript event, or trigger that event on an element. +/// 1 - blur(handler(eventObject)) +/// 2 - blur(eventData, handler(eventObject)) +/// 3 - blur() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.change = function( data, fn ) { +/// +/// Bind an event handler to the "change" JavaScript event, or trigger that event on an element. +/// 1 - change(handler(eventObject)) +/// 2 - change(eventData, handler(eventObject)) +/// 3 - change() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.children = function( until, selector ) { +/// +/// Get the children of each element in the set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.clearQueue = function( type ) { +/// +/// Remove from the queue all items that have not yet been run. +/// +/// +/// A string containing the name of the queue. Defaults to fx, the standard effects queue. +/// +/// + + return this.queue( type || "fx", [] ); + }; +jQuery.prototype.click = function( data, fn ) { +/// +/// Bind an event handler to the "click" JavaScript event, or trigger that event on an element. +/// 1 - click(handler(eventObject)) +/// 2 - click(eventData, handler(eventObject)) +/// 3 - click() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.clone = function( dataAndEvents, deepDataAndEvents ) { +/// +/// Create a deep copy of the set of matched elements. +/// 1 - clone(withDataAndEvents) +/// 2 - clone(withDataAndEvents, deepWithDataAndEvents) +/// +/// +/// A Boolean indicating whether event handlers and data should be copied along with the elements. The default value is false. *For 1.5.0 the default value is incorrectly true. This will be changed back to false in 1.5.1 and up. +/// +/// +/// A Boolean indicating whether event handlers and data for all children of the cloned element should be copied. By default its value matches the first argument's value (which defaults to false). +/// +/// + + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function () { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }; +jQuery.prototype.closest = function( selectors, context ) { +/// +/// 1: Get the first ancestor element that matches the selector, beginning at the current element and progressing up through the DOM tree. +/// 1.1 - closest(selector) +/// 1.2 - closest(selector, context) +/// 1.3 - closest(jQuery object) +/// 1.4 - closest(element) +/// 2: Gets an array of all the elements and selectors matched against the current element up through the DOM tree. +/// 2.1 - closest(selectors, context) +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// +/// A DOM element within which a matching element may be found. If no context is passed in then the context of the jQuery set will be used instead. +/// +/// + + 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 ); + }; +jQuery.prototype.constructor = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }; +jQuery.prototype.contents = function( until, selector ) { +/// +/// Get the children of each element in the set of matched elements, including text and comment nodes. +/// +/// + + 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.prototype.css = function( name, value ) { +/// +/// 1: Get the value of a style property for the first element in the set of matched elements. +/// 1.1 - css(propertyName) +/// 2: Set one or more CSS properties for the set of matched elements. +/// 2.1 - css(propertyName, value) +/// 2.2 - css(propertyName, function(index, value)) +/// 2.3 - css(map) +/// +/// +/// A CSS property name. +/// +/// +/// A value to set for the property. +/// +/// + + // Setting 'undefined' is a no-op + if ( arguments.length === 2 && value === undefined ) { + return this; + } + + return jQuery.access( this, name, value, true, function( elem, name, value ) { + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }); +}; +jQuery.prototype.data = function( key, value ) { +/// +/// 1: Store arbitrary data associated with the matched elements. +/// 1.1 - data(key, value) +/// 1.2 - data(obj) +/// 2: Returns value at named data store for the first element in the jQuery collection, as set by data(name, value). +/// 2.1 - data(key) +/// 2.2 - data() +/// +/// +/// A string naming the piece of data to set. +/// +/// +/// The new data value; it can be any Javascript type including Array or Object. +/// +/// + + 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 ); + }); + } + }; +jQuery.prototype.dblclick = function( data, fn ) { +/// +/// Bind an event handler to the "dblclick" JavaScript event, or trigger that event on an element. +/// 1 - dblclick(handler(eventObject)) +/// 2 - dblclick(eventData, handler(eventObject)) +/// 3 - dblclick() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.delay = function( time, type ) { +/// +/// Set a timer to delay execution of subsequent items in the queue. +/// +/// +/// An integer indicating the number of milliseconds to delay execution of the next item in the queue. +/// +/// +/// A string containing the name of the queue. Defaults to fx, the standard effects queue. +/// +/// + + 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 ); + }); + }; +jQuery.prototype.delegate = function( selector, types, data, fn ) { +/// +/// Attach a handler to one or more events for all elements that match the selector, now or in the future, based on a specific set of root elements. +/// 1 - delegate(selector, eventType, handler) +/// 2 - delegate(selector, eventType, eventData, handler) +/// 3 - delegate(selector, events) +/// +/// +/// A selector to filter the elements that trigger the event. +/// +/// +/// A string containing one or more space-separated JavaScript event types, such as "click" or "keydown," or custom event names. +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute at the time the event is triggered. +/// +/// + + return this.live( types, data, fn, selector ); + }; +jQuery.prototype.dequeue = function( type ) { +/// +/// Execute the next function on the queue for the matched elements. +/// +/// +/// A string containing the name of the queue. Defaults to fx, the standard effects queue. +/// +/// + + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }; +jQuery.prototype.detach = function( selector ) { +/// +/// Remove the set of matched elements from the DOM. +/// +/// +/// A selector expression that filters the set of matched elements to be removed. +/// +/// + + return this.remove( selector, true ); + }; +jQuery.prototype.die = function( types, data, fn, origSelector /* Internal Use Only */ ) { +/// +/// 1: Remove all event handlers previously attached using .live() from the elements. +/// 1.1 - die() +/// 2: Remove an event handler previously attached using .live() from the elements. +/// 2.1 - die(eventType, handler) +/// 2.2 - die(eventTypes) +/// +/// +/// A string containing a JavaScript event type, such as click or keydown. +/// +/// +/// The function that is no longer to be executed. +/// +/// + + 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; + }; +jQuery.prototype.domManip = function( args, table, callback ) { + + var results, first, fragment, parent, + value = args[0], + scripts = []; + + // We can't cloneNode fragments that contain checked, in WebKit + if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) { + return this.each(function() { + jQuery(this).domManip( args, table, callback, true ); + }); + } + + if ( jQuery.isFunction(value) ) { + return this.each(function(i) { + var self = jQuery(this); + args[0] = value.call(this, i, table ? self.html() : undefined); + self.domManip( args, table, callback ); + }); + } + + if ( this[0] ) { + parent = value && value.parentNode; + + // If we're in a fragment, just use that instead of building a new one + if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) { + results = { fragment: parent }; + + } else { + results = jQuery.buildFragment( args, this, scripts ); + } + + fragment = results.fragment; + + if ( fragment.childNodes.length === 1 ) { + first = fragment = fragment.firstChild; + } else { + first = fragment.firstChild; + } + + if ( first ) { + table = table && jQuery.nodeName( first, "tr" ); + + for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) { + callback.call( + table ? + root(this[i], first) : + this[i], + // Make sure that we do not leak memory by inadvertently discarding + // the original fragment (which might have attached data) instead of + // using it; in addition, use the original fragment object for the last + // item instead of first because it can end up being emptied incorrectly + // in certain situations (Bug #8070). + // Fragments from the fragment cache must always be cloned and never used + // in place. + results.cacheable || (l > 1 && i < lastIndex) ? + jQuery.clone( fragment, true, true ) : + fragment + ); + } + } + + if ( scripts.length ) { + jQuery.each( scripts, evalScript ); + } + } + + return this; + }; +jQuery.prototype.each = function( callback, args ) { +/// +/// Iterate over a jQuery object, executing a function for each matched element. +/// +/// +/// A function to execute for each matched element. +/// +/// + + return jQuery.each( this, callback, args ); + }; +jQuery.prototype.empty = function() { +/// +/// Remove all child nodes of the set of matched elements from the DOM. +/// +/// + + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName("*") ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + } + + return this; + }; +jQuery.prototype.end = function() { +/// +/// End the most recent filtering operation in the current chain and return the set of matched elements to its previous state. +/// +/// + + return this.prevObject || this.constructor(null); + }; +jQuery.prototype.eq = function( i ) { +/// +/// Reduce the set of matched elements to the one at the specified index. +/// 1 - eq(index) +/// 2 - eq(-index) +/// +/// +/// An integer indicating the 0-based position of the element. +/// +/// + + return i === -1 ? + this.slice( i ) : + this.slice( i, +i + 1 ); + }; +jQuery.prototype.error = function( data, fn ) { +/// +/// Bind an event handler to the "error" JavaScript event. +/// 1 - error(handler(eventObject)) +/// 2 - error(eventData, handler(eventObject)) +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.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.prototype.fadeIn = function( speed, easing, callback ) { +/// +/// Display the matched elements by fading them to opaque. +/// 1 - fadeIn(duration, callback) +/// 2 - fadeIn(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.animate( props, speed, easing, callback ); + }; +jQuery.prototype.fadeOut = function( speed, easing, callback ) { +/// +/// Hide the matched elements by fading them to transparent. +/// 1 - fadeOut(duration, callback) +/// 2 - fadeOut(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.animate( props, speed, easing, callback ); + }; +jQuery.prototype.fadeTo = function( speed, to, easing, callback ) { +/// +/// Adjust the opacity of the matched elements. +/// 1 - fadeTo(duration, opacity, callback) +/// 2 - fadeTo(duration, opacity, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A number between 0 and 1 denoting the target opacity. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.filter(":hidden").css("opacity", 0).show().end() + .animate({opacity: to}, speed, easing, callback); + }; +jQuery.prototype.fadeToggle = function( speed, easing, callback ) { +/// +/// Display or hide the matched elements by animating their opacity. +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.animate( props, speed, easing, callback ); + }; +jQuery.prototype.filter = function( selector ) { +/// +/// Reduce the set of matched elements to those that match the selector or pass the function's test. +/// 1 - filter(selector) +/// 2 - filter(function(index)) +/// 3 - filter(element) +/// 4 - filter(jQuery object) +/// +/// +/// A string containing a selector expression to match the current set of elements against. +/// +/// + + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }; +jQuery.prototype.find = function( selector ) { +/// +/// Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element. +/// 1 - find(selector) +/// 2 - find(jQuery object) +/// 3 - find(element) +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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; + }; +jQuery.prototype.first = function() { +/// +/// Reduce the set of matched elements to the first in the set. +/// +/// + + return this.eq( 0 ); + }; +jQuery.prototype.focus = function( data, fn ) { +/// +/// Bind an event handler to the "focus" JavaScript event, or trigger that event on an element. +/// 1 - focus(handler(eventObject)) +/// 2 - focus(eventData, handler(eventObject)) +/// 3 - focus() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.focusin = function( data, fn ) { +/// +/// Bind an event handler to the "focusin" JavaScript event. +/// 1 - focusin(handler(eventObject)) +/// 2 - focusin(eventData, handler(eventObject)) +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.focusout = function( data, fn ) { +/// +/// Bind an event handler to the "focusout" JavaScript event. +/// 1 - focusout(handler(eventObject)) +/// 2 - focusout(eventData, handler(eventObject)) +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.get = function( num ) { +/// +/// Retrieve the DOM elements matched by the jQuery object. +/// +/// +/// A zero-based integer indicating which element to retrieve. +/// +/// + + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }; +jQuery.prototype.has = function( target ) { +/// +/// Reduce the set of matched elements to those that have a descendant that matches the selector or DOM element. +/// 1 - has(selector) +/// 2 - has(contained) +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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; + } + } + }); + }; +jQuery.prototype.hasClass = function( selector ) { +/// +/// Determine whether any of the matched elements are assigned the given class. +/// +/// +/// The class name to search for. +/// +/// + + var className = " " + selector + " "; + for ( var i = 0, l = this.length; i < l; i++ ) { + if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }; +jQuery.prototype.height = function( size ) { +/// +/// 1: Get the current computed height for the first element in the set of matched elements. +/// 1.1 - height() +/// 2: Set the CSS height of every matched element. +/// 2.1 - height(value) +/// 2.2 - height(function(index, height)) +/// +/// +/// An integer representing the number of pixels, or an integer with an optional unit of measure appended (as a string). +/// +/// + + // Get window width or height + var elem = this[0]; + if ( !elem ) { + return size == null ? null : this; + } + + if ( jQuery.isFunction( size ) ) { + return this.each(function( i ) { + var self = jQuery( this ); + self[ type ]( size.call( this, i, self[ type ]() ) ); + }); + } + + if ( jQuery.isWindow( elem ) ) { + // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode + // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat + var docElemProp = elem.document.documentElement[ "client" + name ]; + return elem.document.compatMode === "CSS1Compat" && docElemProp || + elem.document.body[ "client" + name ] || docElemProp; + + // Get document width or height + } else if ( elem.nodeType === 9 ) { + // Either scroll[Width/Height] or offset[Width/Height], whichever is greater + return Math.max( + elem.documentElement["client" + name], + elem.body["scroll" + name], elem.documentElement["scroll" + name], + elem.body["offset" + name], elem.documentElement["offset" + name] + ); + + // Get or set width or height on the element + } else if ( size === undefined ) { + var orig = jQuery.css( elem, type ), + ret = parseFloat( orig ); + + return jQuery.isNaN( ret ) ? orig : ret; + + // Set the width or height on the element (default to pixels if value is unitless) + } else { + return this.css( type, typeof size === "string" ? size : size + "px" ); + } + }; +jQuery.prototype.hide = function( speed, easing, callback ) { +/// +/// Hide the matched elements. +/// 1 - hide() +/// 2 - hide(duration, callback) +/// 3 - hide(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + if ( speed || speed === 0 ) { + return this.animate( genFx("hide", 3), speed, easing, callback); + + } else { + for ( var i = 0, j = this.length; i < j; i++ ) { + if ( this[i].style ) { + var display = jQuery.css( this[i], "display" ); + + if ( display !== "none" && !jQuery._data( this[i], "olddisplay" ) ) { + jQuery._data( this[i], "olddisplay", display ); + } + } + } + + // Set the display of the elements in a second loop + // to avoid the constant reflow + for ( i = 0; i < j; i++ ) { + if ( this[i].style ) { + this[i].style.display = "none"; + } + } + + return this; + } + }; +jQuery.prototype.hover = function( fnOver, fnOut ) { +/// +/// 1: Bind two handlers to the matched elements, to be executed when the mouse pointer enters and leaves the elements. +/// 1.1 - hover(handlerIn(eventObject), handlerOut(eventObject)) +/// 2: Bind a single handler to the matched elements, to be executed when the mouse pointer enters or leaves the elements. +/// 2.1 - hover(handlerInOut(eventObject)) +/// +/// +/// A function to execute when the mouse pointer enters the element. +/// +/// +/// A function to execute when the mouse pointer leaves the element. +/// +/// + + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + }; +jQuery.prototype.html = function( value ) { +/// +/// 1: Get the HTML contents of the first element in the set of matched elements. +/// 1.1 - html() +/// 2: Set the HTML contents of each element in the set of matched elements. +/// 2.1 - html(htmlString) +/// 2.2 - html(function(index, oldhtml)) +/// +/// +/// A string of HTML to set as the content of each matched element. +/// +/// + + if ( value === undefined ) { + return this[0] && this[0].nodeType === 1 ? + this[0].innerHTML.replace(rinlinejQuery, "") : + null; + + // See if we can take a shortcut and just use innerHTML + } else if ( typeof value === "string" && !rnocache.test( value ) && + (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) && + !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) { + + value = value.replace(rxhtmlTag, "<$1>"); + + try { + for ( var i = 0, l = this.length; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + if ( this[i].nodeType === 1 ) { + jQuery.cleanData( this[i].getElementsByTagName("*") ); + this[i].innerHTML = value; + } + } + + // If using innerHTML throws an exception, use the fallback method + } catch(e) { + this.empty().append( value ); + } + + } else if ( jQuery.isFunction( value ) ) { + this.each(function(i){ + var self = jQuery( this ); + + self.html( value.call(this, i, self.html()) ); + }); + + } else { + this.empty().append( value ); + } + + return this; + }; +jQuery.prototype.index = function( elem ) { +/// +/// Search for a given element from among the matched elements. +/// 1 - index() +/// 2 - index(selector) +/// 3 - index(element) +/// +/// +/// A selector representing a jQuery collection in which to look for an element. +/// +/// + + if ( !elem || typeof elem === "string" ) { + return jQuery.inArray( this[0], + // If it receives a string, the selector is used + // If it receives nothing, the siblings are used + elem ? jQuery( elem ) : this.parent().children() ); + } + // 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 ); + }; +jQuery.prototype.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 ); + }; +jQuery.prototype.innerHeight = function() { +/// +/// Get the current computed height for the first element in the set of matched elements, including padding but not border. +/// +/// + + var elem = this[0]; + return elem && elem.style ? + parseFloat( jQuery.css( elem, type, "padding" ) ) : + null; + }; +jQuery.prototype.innerWidth = function() { +/// +/// Get the current computed width for the first element in the set of matched elements, including padding but not border. +/// +/// + + var elem = this[0]; + return elem && elem.style ? + parseFloat( jQuery.css( elem, type, "padding" ) ) : + null; + }; +jQuery.prototype.insertAfter = function( selector ) { +/// +/// Insert every element in the set of matched elements after the target. +/// +/// +/// A selector, element, HTML string, or jQuery object; the matched set of elements will be inserted after the element(s) specified by this parameter. +/// +/// + + var ret = [], + insert = jQuery( selector ), + parent = this.length === 1 && this[0].parentNode; + + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + return this; + + } else { + for ( var i = 0, l = insert.length; i < l; i++ ) { + var elems = (i > 0 ? this.clone(true) : this).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +jQuery.prototype.insertBefore = function( selector ) { +/// +/// Insert every element in the set of matched elements before the target. +/// +/// +/// A selector, element, HTML string, or jQuery object; the matched set of elements will be inserted before the element(s) specified by this parameter. +/// +/// + + var ret = [], + insert = jQuery( selector ), + parent = this.length === 1 && this[0].parentNode; + + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + return this; + + } else { + for ( var i = 0, l = insert.length; i < l; i++ ) { + var elems = (i > 0 ? this.clone(true) : this).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +jQuery.prototype.is = function( selector ) { +/// +/// Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments. +/// 1 - is(selector) +/// 2 - is(function(index)) +/// 3 - is(jQuery object) +/// 4 - is(element) +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + return !!selector && ( typeof selector === "string" ? + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }; +jQuery.prototype.keydown = function( data, fn ) { +/// +/// Bind an event handler to the "keydown" JavaScript event, or trigger that event on an element. +/// 1 - keydown(handler(eventObject)) +/// 2 - keydown(eventData, handler(eventObject)) +/// 3 - keydown() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.keypress = function( data, fn ) { +/// +/// Bind an event handler to the "keypress" JavaScript event, or trigger that event on an element. +/// 1 - keypress(handler(eventObject)) +/// 2 - keypress(eventData, handler(eventObject)) +/// 3 - keypress() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.keyup = function( data, fn ) { +/// +/// Bind an event handler to the "keyup" JavaScript event, or trigger that event on an element. +/// 1 - keyup(handler(eventObject)) +/// 2 - keyup(eventData, handler(eventObject)) +/// 3 - keyup() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.last = function() { +/// +/// Reduce the set of matched elements to the final one in the set. +/// +/// + + return this.eq( -1 ); + }; +jQuery.prototype.length = 0; +jQuery.prototype.live = function( types, data, fn, origSelector /* Internal Use Only */ ) { +/// +/// Attach a handler to the event for all elements which match the current selector, now and in the future. +/// 1 - live(eventType, handler) +/// 2 - live(eventType, eventData, handler) +/// 3 - live(events) +/// +/// +/// A string containing a JavaScript event type, such as "click" or "keydown." As of jQuery 1.4 the string can contain multiple, space-separated event types or custom event names, as well. +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute at the time the event is triggered. +/// +/// + + 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; + }; +jQuery.prototype.load = function( url, params, callback ) { +/// +/// 1: Bind an event handler to the "load" JavaScript event. +/// 1.1 - load(handler(eventObject)) +/// 1.2 - load(eventData, handler(eventObject)) +/// 2: Load data from the server and place the returned HTML into the matched element. +/// 2.1 - load(url, data, complete(responseText, textStatus, XMLHttpRequest)) +/// +/// +/// A string containing the URL to which the request is sent. +/// +/// +/// A map or string that is sent to the server with the request. +/// +/// +/// A callback function that is executed when the request completes. +/// +/// + + if ( typeof url !== "string" && _load ) { + return _load.apply( this, arguments ); + + // Don't do a request if no elements are being requested + } else if ( !this.length ) { + return this; + } + + var off = url.indexOf( " " ); + if ( off >= 0 ) { + var selector = url.slice( off, url.length ); + url = url.slice( 0, off ); + } + + // Default to a GET request + var type = "GET"; + + // If the second parameter was provided + if ( params ) { + // If it's a function + if ( jQuery.isFunction( params ) ) { + // We assume that it's the callback + callback = params; + params = undefined; + + // Otherwise, build a param string + } else if ( typeof params === "object" ) { + params = jQuery.param( params, jQuery.ajaxSettings.traditional ); + type = "POST"; + } + } + + var self = this; + + // Request the remote document + jQuery.ajax({ + url: url, + type: type, + dataType: "html", + data: params, + // Complete callback (responseText is used internally) + complete: function( jqXHR, status, responseText ) { + // Store the response as specified by the jqXHR object + responseText = jqXHR.responseText; + // If successful, inject the HTML into all the matched elements + if ( jqXHR.isResolved() ) { + // #4825: Get the actual response in case + // a dataFilter is present in ajaxSettings + jqXHR.done(function( r ) { + responseText = r; + }); + // See if a selector was specified + self.html( selector ? + // Create a dummy div to hold the results + jQuery("
    ") + // inject the contents of the document in, removing the scripts + // to avoid any 'Permission Denied' errors in IE + .append(responseText.replace(rscript, "")) + + // Locate the specified elements + .find(selector) : + + // If not, just inject the full result + responseText ); + } + + if ( callback ) { + self.each( callback, [ responseText, status, jqXHR ] ); + } + } + }); + + return this; + }; +jQuery.prototype.map = function( callback ) { +/// +/// Pass each element in the current matched set through a function, producing a new jQuery object containing the return values. +/// +/// +/// A function object that will be invoked for each element in the current set. +/// +/// + + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }; +jQuery.prototype.mousedown = function( data, fn ) { +/// +/// Bind an event handler to the "mousedown" JavaScript event, or trigger that event on an element. +/// 1 - mousedown(handler(eventObject)) +/// 2 - mousedown(eventData, handler(eventObject)) +/// 3 - mousedown() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.mouseenter = function( data, fn ) { +/// +/// Bind an event handler to be fired when the mouse enters an element, or trigger that handler on an element. +/// 1 - mouseenter(handler(eventObject)) +/// 2 - mouseenter(eventData, handler(eventObject)) +/// 3 - mouseenter() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.mouseleave = function( data, fn ) { +/// +/// Bind an event handler to be fired when the mouse leaves an element, or trigger that handler on an element. +/// 1 - mouseleave(handler(eventObject)) +/// 2 - mouseleave(eventData, handler(eventObject)) +/// 3 - mouseleave() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.mousemove = function( data, fn ) { +/// +/// Bind an event handler to the "mousemove" JavaScript event, or trigger that event on an element. +/// 1 - mousemove(handler(eventObject)) +/// 2 - mousemove(eventData, handler(eventObject)) +/// 3 - mousemove() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.mouseout = function( data, fn ) { +/// +/// Bind an event handler to the "mouseout" JavaScript event, or trigger that event on an element. +/// 1 - mouseout(handler(eventObject)) +/// 2 - mouseout(eventData, handler(eventObject)) +/// 3 - mouseout() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.mouseover = function( data, fn ) { +/// +/// Bind an event handler to the "mouseover" JavaScript event, or trigger that event on an element. +/// 1 - mouseover(handler(eventObject)) +/// 2 - mouseover(eventData, handler(eventObject)) +/// 3 - mouseover() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.mouseup = function( data, fn ) { +/// +/// Bind an event handler to the "mouseup" JavaScript event, or trigger that event on an element. +/// 1 - mouseup(handler(eventObject)) +/// 2 - mouseup(eventData, handler(eventObject)) +/// 3 - mouseup() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.next = function( until, selector ) { +/// +/// Get the immediately following sibling of each element in the set of matched elements. If a selector is provided, it retrieves the next sibling only if it matches that selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.nextAll = function( until, selector ) { +/// +/// Get all following siblings of each element in the set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.nextUntil = function( until, selector ) { +/// +/// Get all following siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object passed. +/// 1 - nextUntil(selector, filter) +/// 2 - nextUntil(element, filter) +/// +/// +/// A string containing a selector expression to indicate where to stop matching following sibling elements. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.not = function( selector ) { +/// +/// Remove elements from the set of matched elements. +/// 1 - not(selector) +/// 2 - not(elements) +/// 3 - not(function(index)) +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + return this.pushStack( winnow(this, selector, false), "not", selector); + }; +jQuery.prototype.offset = function( options ) { +/// +/// 1: Get the current coordinates of the first element in the set of matched elements, relative to the document. +/// 1.1 - offset() +/// 2: Set the current coordinates of every element in the set of matched elements, relative to the document. +/// 2.1 - offset(coordinates) +/// 2.2 - offset(function(index, coords)) +/// +/// +/// An object containing the properties top and left, which are integers indicating the new top and left coordinates for the elements. +/// +/// + + var elem = this[0], box; + + if ( options ) { + return this.each(function( i ) { + jQuery.offset.setOffset( this, options, i ); + }); + } + + if ( !elem || !elem.ownerDocument ) { + return null; + } + + if ( elem === elem.ownerDocument.body ) { + return jQuery.offset.bodyOffset( elem ); + } + + try { + box = elem.getBoundingClientRect(); + } catch(e) {} + + var doc = elem.ownerDocument, + docElem = doc.documentElement; + + // Make sure we're not dealing with a disconnected DOM node + if ( !box || !jQuery.contains( docElem, elem ) ) { + return box ? { top: box.top, left: box.left } : { top: 0, left: 0 }; + } + + var body = doc.body, + win = getWindow(doc), + clientTop = docElem.clientTop || body.clientTop || 0, + clientLeft = docElem.clientLeft || body.clientLeft || 0, + scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop, + scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft, + top = box.top + scrollTop - clientTop, + left = box.left + scrollLeft - clientLeft; + + return { top: top, left: left }; + }; +jQuery.prototype.offsetParent = function() { +/// +/// Get the closest ancestor element that is positioned. +/// +/// + + return this.map(function() { + var offsetParent = this.offsetParent || document.body; + while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) { + offsetParent = offsetParent.offsetParent; + } + return offsetParent; + }); + }; +jQuery.prototype.one = function( type, data, fn ) { +/// +/// Attach a handler to an event for the elements. The handler is executed at most once per element. +/// +/// +/// A string containing one or more JavaScript event types, such as "click" or "submit," or custom event names. +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute at the time the event is triggered. +/// +/// + + 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.prototype.outerHeight = function( margin ) { +/// +/// Get the current computed height for the first element in the set of matched elements, including padding, border, and optionally margin. +/// +/// +/// A Boolean indicating whether to include the element's margin in the calculation. +/// +/// + + var elem = this[0]; + return elem && elem.style ? + parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) : + null; + }; +jQuery.prototype.outerWidth = function( margin ) { +/// +/// Get the current computed width for the first element in the set of matched elements, including padding and border. +/// +/// +/// A Boolean indicating whether to include the element's margin in the calculation. +/// +/// + + var elem = this[0]; + return elem && elem.style ? + parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) : + null; + }; +jQuery.prototype.parent = function( until, selector ) { +/// +/// Get the parent of each element in the current set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.parents = function( until, selector ) { +/// +/// Get the ancestors of each element in the current set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.parentsUntil = function( until, selector ) { +/// +/// Get the ancestors of each element in the current set of matched elements, up to but not including the element matched by the selector, DOM node, or jQuery object. +/// 1 - parentsUntil(selector, filter) +/// 2 - parentsUntil(element, filter) +/// +/// +/// A string containing a selector expression to indicate where to stop matching ancestor elements. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.position = function() { +/// +/// Get the current coordinates of the first element in the set of matched elements, relative to the offset parent. +/// +/// + + if ( !this[0] ) { + return null; + } + + var elem = this[0], + + // Get *real* offsetParent + offsetParent = this.offsetParent(), + + // Get correct offsets + offset = this.offset(), + parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset(); + + // Subtract element margins + // note: when an element has margin: auto the offsetLeft and marginLeft + // are the same in Safari causing offset.left to incorrectly be 0 + offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0; + offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0; + + // Add offsetParent borders + parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0; + parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0; + + // Subtract the two offsets + return { + top: offset.top - parentOffset.top, + left: offset.left - parentOffset.left + }; + }; +jQuery.prototype.prepend = function() { +/// +/// Insert content, specified by the parameter, to the beginning of each element in the set of matched elements. +/// 1 - prepend(content, content) +/// 2 - prepend(function(index, html)) +/// +/// +/// DOM element, array of elements, HTML string, or jQuery object to insert at the beginning of each element in the set of matched elements. +/// +/// +/// One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert at the beginning of each element in the set of matched elements. +/// +/// + + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 ) { + this.insertBefore( elem, this.firstChild ); + } + }); + }; +jQuery.prototype.prependTo = function( selector ) { +/// +/// Insert every element in the set of matched elements to the beginning of the target. +/// +/// +/// A selector, element, HTML string, or jQuery object; the matched set of elements will be inserted at the beginning of the element(s) specified by this parameter. +/// +/// + + var ret = [], + insert = jQuery( selector ), + parent = this.length === 1 && this[0].parentNode; + + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + return this; + + } else { + for ( var i = 0, l = insert.length; i < l; i++ ) { + var elems = (i > 0 ? this.clone(true) : this).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +jQuery.prototype.prev = function( until, selector ) { +/// +/// Get the immediately preceding sibling of each element in the set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.prevAll = function( until, selector ) { +/// +/// Get all preceding siblings of each element in the set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.prevUntil = function( until, selector ) { +/// +/// Get all preceding siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object. +/// 1 - prevUntil(selector, filter) +/// 2 - prevUntil(element, filter) +/// +/// +/// A string containing a selector expression to indicate where to stop matching preceding sibling elements. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.promise = function( type, object ) { +/// +/// Return a Promise object to observe when all actions of a certain type bound to the collection, queued or not, have finished. +/// +/// +/// The type of queue that needs to be observed. +/// +/// +/// Object onto which the promise methods have to be attached +/// +/// + + 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(); + }; +jQuery.prototype.prop = function( name, value ) { +/// +/// 1: Get the value of a property for the first element in the set of matched elements. +/// 1.1 - prop(propertyName) +/// 2: Set one or more properties for the set of matched elements. +/// 2.1 - prop(propertyName, value) +/// 2.2 - prop(map) +/// 2.3 - prop(propertyName, function(index, oldPropertyValue)) +/// +/// +/// The name of the property to set. +/// +/// +/// A value to set for the property. +/// +/// + + return jQuery.access( this, name, value, true, jQuery.prop ); + }; +jQuery.prototype.pushStack = function( elems, name, selector ) { +/// +/// Add a collection of DOM elements onto the jQuery stack. +/// 1 - pushStack(elements) +/// 2 - pushStack(elements, name, arguments) +/// +/// +/// An array of elements to push onto the stack and make into a new jQuery object. +/// +/// +/// The name of a jQuery method that generated the array of elements. +/// +/// +/// The arguments that were passed in to the jQuery method (for serialization). +/// +/// + + // 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; + }; +jQuery.prototype.queue = function( type, data ) { +/// +/// 1: Show the queue of functions to be executed on the matched elements. +/// 1.1 - queue(queueName) +/// 2: Manipulate the queue of functions to be executed on the matched elements. +/// 2.1 - queue(queueName, newQueue) +/// 2.2 - queue(queueName, callback( next )) +/// +/// +/// A string containing the name of the queue. Defaults to fx, the standard effects queue. +/// +/// +/// An array of functions to replace the current queue contents. +/// +/// + + 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 ); + } + }); + }; +jQuery.prototype.ready = function( fn ) { +/// +/// Specify a function to execute when the DOM is fully loaded. +/// +/// +/// A function to execute after the DOM is ready. +/// +/// + + // Attach the listeners + jQuery.bindReady(); + + // Add the callback + readyList.done( fn ); + + return this; + }; +jQuery.prototype.remove = function( selector, keepData ) { +/// +/// Remove the set of matched elements from the DOM. +/// +/// +/// A selector expression that filters the set of matched elements to be removed. +/// +/// + + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + if ( !selector || jQuery.filter( selector, [ elem ] ).length ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName("*") ); + jQuery.cleanData( [ elem ] ); + } + + if ( elem.parentNode ) { + elem.parentNode.removeChild( elem ); + } + } + } + + return this; + }; +jQuery.prototype.removeAttr = function( name ) { +/// +/// Remove an attribute from each element in the set of matched elements. +/// +/// +/// An attribute to remove. +/// +/// + + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }; +jQuery.prototype.removeClass = function( value ) { +/// +/// Remove a single class, multiple classes, or all classes from each element in the set of matched elements. +/// 1 - removeClass(className) +/// 2 - removeClass(function(index, class)) +/// +/// +/// One or more space-separated classes to be removed from the class attribute of each matched element. +/// +/// + + 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; + }; +jQuery.prototype.removeData = function( key ) { +/// +/// Remove a previously-stored piece of data. +/// +/// +/// A string naming the piece of data to delete. +/// +/// + + return this.each(function() { + jQuery.removeData( this, key ); + }); + }; +jQuery.prototype.removeProp = function( name ) { +/// +/// Remove a property for the set of matched elements. +/// +/// +/// The name of the property to set. +/// +/// + + 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 ) {} + }); + }; +jQuery.prototype.replaceAll = function( selector ) { +/// +/// Replace each target element with the set of matched elements. +/// +/// +/// A selector expression indicating which element(s) to replace. +/// +/// + + var ret = [], + insert = jQuery( selector ), + parent = this.length === 1 && this[0].parentNode; + + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + return this; + + } else { + for ( var i = 0, l = insert.length; i < l; i++ ) { + var elems = (i > 0 ? this.clone(true) : this).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +jQuery.prototype.replaceWith = function( value ) { +/// +/// Replace each element in the set of matched elements with the provided new content. +/// 1 - replaceWith(newContent) +/// 2 - replaceWith(function) +/// +/// +/// The content to insert. May be an HTML string, DOM element, or jQuery object. +/// +/// + + if ( this[0] && this[0].parentNode ) { + // Make sure that the elements are removed from the DOM before they are inserted + // this can help fix replacing a parent with child elements + if ( jQuery.isFunction( value ) ) { + return this.each(function(i) { + var self = jQuery(this), old = self.html(); + self.replaceWith( value.call( this, i, old ) ); + }); + } + + if ( typeof value !== "string" ) { + value = jQuery( value ).detach(); + } + + return this.each(function() { + var next = this.nextSibling, + parent = this.parentNode; + + jQuery( this ).remove(); + + if ( next ) { + jQuery(next).before( value ); + } else { + jQuery(parent).append( value ); + } + }); + } else { + return this.length ? + this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) : + this; + } + }; +jQuery.prototype.resize = function( data, fn ) { +/// +/// Bind an event handler to the "resize" JavaScript event, or trigger that event on an element. +/// 1 - resize(handler(eventObject)) +/// 2 - resize(eventData, handler(eventObject)) +/// 3 - resize() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.scroll = function( data, fn ) { +/// +/// Bind an event handler to the "scroll" JavaScript event, or trigger that event on an element. +/// 1 - scroll(handler(eventObject)) +/// 2 - scroll(eventData, handler(eventObject)) +/// 3 - scroll() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.scrollLeft = function( val ) { +/// +/// 1: Get the current horizontal position of the scroll bar for the first element in the set of matched elements. +/// 1.1 - scrollLeft() +/// 2: Set the current horizontal position of the scroll bar for each of the set of matched elements. +/// 2.1 - scrollLeft(value) +/// +/// +/// An integer indicating the new position to set the scroll bar to. +/// +/// + + var elem, win; + + if ( val === undefined ) { + elem = this[ 0 ]; + + if ( !elem ) { + return null; + } + + win = getWindow( elem ); + + // Return the scroll offset + return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] : + jQuery.support.boxModel && win.document.documentElement[ method ] || + win.document.body[ method ] : + elem[ method ]; + } + + // Set the scroll offset + return this.each(function() { + win = getWindow( this ); + + if ( win ) { + win.scrollTo( + !i ? val : jQuery( win ).scrollLeft(), + i ? val : jQuery( win ).scrollTop() + ); + + } else { + this[ method ] = val; + } + }); + }; +jQuery.prototype.scrollTop = function( val ) { +/// +/// 1: Get the current vertical position of the scroll bar for the first element in the set of matched elements. +/// 1.1 - scrollTop() +/// 2: Set the current vertical position of the scroll bar for each of the set of matched elements. +/// 2.1 - scrollTop(value) +/// +/// +/// An integer indicating the new position to set the scroll bar to. +/// +/// + + var elem, win; + + if ( val === undefined ) { + elem = this[ 0 ]; + + if ( !elem ) { + return null; + } + + win = getWindow( elem ); + + // Return the scroll offset + return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] : + jQuery.support.boxModel && win.document.documentElement[ method ] || + win.document.body[ method ] : + elem[ method ]; + } + + // Set the scroll offset + return this.each(function() { + win = getWindow( this ); + + if ( win ) { + win.scrollTo( + !i ? val : jQuery( win ).scrollLeft(), + i ? val : jQuery( win ).scrollTop() + ); + + } else { + this[ method ] = val; + } + }); + }; +jQuery.prototype.select = function( data, fn ) { +/// +/// Bind an event handler to the "select" JavaScript event, or trigger that event on an element. +/// 1 - select(handler(eventObject)) +/// 2 - select(eventData, handler(eventObject)) +/// 3 - select() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.serialize = function() { +/// +/// Encode a set of form elements as a string for submission. +/// +/// + + return jQuery.param( this.serializeArray() ); + }; +jQuery.prototype.serializeArray = function() { +/// +/// Encode a set of form elements as an array of names and values. +/// +/// + + return this.map(function(){ + return this.elements ? jQuery.makeArray( this.elements ) : this; + }) + .filter(function(){ + return this.name && !this.disabled && + ( this.checked || rselectTextarea.test( this.nodeName ) || + rinput.test( this.type ) ); + }) + .map(function( i, elem ){ + var val = jQuery( this ).val(); + + return val == null ? + null : + jQuery.isArray( val ) ? + jQuery.map( val, function( val, i ){ + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }) : + { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }).get(); + }; +jQuery.prototype.show = function( speed, easing, callback ) { +/// +/// Display the matched elements. +/// 1 - show() +/// 2 - show(duration, callback) +/// 3 - show(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + var elem, display; + + if ( speed || speed === 0 ) { + return this.animate( genFx("show", 3), speed, easing, callback); + + } else { + for ( var i = 0, j = this.length; i < j; i++ ) { + elem = this[i]; + + if ( elem.style ) { + display = elem.style.display; + + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !jQuery._data(elem, "olddisplay") && display === "none" ) { + display = elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( display === "" && jQuery.css( elem, "display" ) === "none" ) { + jQuery._data(elem, "olddisplay", defaultDisplay(elem.nodeName)); + } + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( i = 0; i < j; i++ ) { + elem = this[i]; + + if ( elem.style ) { + display = elem.style.display; + + if ( display === "" || display === "none" ) { + elem.style.display = jQuery._data(elem, "olddisplay") || ""; + } + } + } + + return this; + } + }; +jQuery.prototype.siblings = function( until, selector ) { +/// +/// Get the siblings of each element in the set of matched elements, optionally filtered by a selector. +/// +/// +/// A string containing a selector expression to match elements against. +/// +/// + + 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.prototype.size = function() { +/// +/// Return the number of elements in the jQuery object. +/// +/// + + return this.length; + }; +jQuery.prototype.slice = function() { +/// +/// Reduce the set of matched elements to a subset specified by a range of indices. +/// +/// +/// An integer indicating the 0-based position at which the elements begin to be selected. If negative, it indicates an offset from the end of the set. +/// +/// +/// An integer indicating the 0-based position at which the elements stop being selected. If negative, it indicates an offset from the end of the set. If omitted, the range continues until the end of the set. +/// +/// + + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }; +jQuery.prototype.slideDown = function( speed, easing, callback ) { +/// +/// Display the matched elements with a sliding motion. +/// 1 - slideDown(duration, callback) +/// 2 - slideDown(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.animate( props, speed, easing, callback ); + }; +jQuery.prototype.slideToggle = function( speed, easing, callback ) { +/// +/// Display or hide the matched elements with a sliding motion. +/// 1 - slideToggle(duration, callback) +/// 2 - slideToggle(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.animate( props, speed, easing, callback ); + }; +jQuery.prototype.slideUp = function( speed, easing, callback ) { +/// +/// Hide the matched elements with a sliding motion. +/// 1 - slideUp(duration, callback) +/// 2 - slideUp(duration, easing, callback) +/// +/// +/// A string or number determining how long the animation will run. +/// +/// +/// A string indicating which easing function to use for the transition. +/// +/// +/// A function to call once the animation is complete. +/// +/// + + return this.animate( props, speed, easing, callback ); + }; +jQuery.prototype.stop = function( clearQueue, gotoEnd ) { +/// +/// Stop the currently-running animation on the matched elements. +/// +/// +/// A Boolean indicating whether to remove queued animation as well. Defaults to false. +/// +/// +/// A Boolean indicating whether to complete the current animation immediately. Defaults to false. +/// +/// + + if ( clearQueue ) { + this.queue([]); + } + + this.each(function() { + var timers = jQuery.timers, + i = timers.length; + // clear marker counters if we know they won't be + if ( !gotoEnd ) { + jQuery._unmark( true, this ); + } + while ( i-- ) { + if ( timers[i].elem === this ) { + if (gotoEnd) { + // force the next step to be the last + timers[i](true); + } + + timers.splice(i, 1); + } + } + }); + + // start the next in the queue if the last step wasn't forced + if ( !gotoEnd ) { + this.dequeue(); + } + + return this; + }; +jQuery.prototype.submit = function( data, fn ) { +/// +/// Bind an event handler to the "submit" JavaScript event, or trigger that event on an element. +/// 1 - submit(handler(eventObject)) +/// 2 - submit(eventData, handler(eventObject)) +/// 3 - submit() +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.text = function( text ) { +/// +/// 1: Get the combined text contents of each element in the set of matched elements, including their descendants. +/// 1.1 - text() +/// 2: Set the content of each element in the set of matched elements to the specified text. +/// 2.1 - text(textString) +/// 2.2 - text(function(index, text)) +/// +/// +/// A string of text to set as the content of each matched element. +/// +/// + + if ( jQuery.isFunction(text) ) { + return this.each(function(i) { + var self = jQuery( this ); + + self.text( text.call(this, i, self.text()) ); + }); + } + + if ( typeof text !== "object" && text !== undefined ) { + return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); + } + + return jQuery.text( this ); + }; +jQuery.prototype.toArray = function() { +/// +/// Retrieve all the DOM elements contained in the jQuery set, as an array. +/// +/// + + return slice.call( this, 0 ); + }; +jQuery.prototype.toggle = function( fn, fn2, callback ) { +/// +/// 1: Bind two or more handlers to the matched elements, to be executed on alternate clicks. +/// 1.1 - toggle(handler(eventObject), handler(eventObject), handler(eventObject)) +/// 2: Display or hide the matched elements. +/// 2.1 - toggle(duration, callback) +/// 2.2 - toggle(duration, easing, callback) +/// 2.3 - toggle(showOrHide) +/// +/// +/// A function to execute every even time the element is clicked. +/// +/// +/// A function to execute every odd time the element is clicked. +/// +/// +/// Additional handlers to cycle through after clicks. +/// +/// + + var bool = typeof fn === "boolean"; + + if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) { + this._toggle.apply( this, arguments ); + + } else if ( fn == null || bool ) { + this.each(function() { + var state = bool ? fn : jQuery(this).is(":hidden"); + jQuery(this)[ state ? "show" : "hide" ](); + }); + + } else { + this.animate(genFx("toggle", 3), fn, fn2, callback); + } + + return this; + }; +jQuery.prototype.toggleClass = function( value, stateVal ) { +/// +/// Add or remove one or more classes from each element in the set of matched elements, depending on either the class's presence or the value of the switch argument. +/// 1 - toggleClass(className) +/// 2 - toggleClass(className, switch) +/// 3 - toggleClass(function(index, class, switch), switch) +/// +/// +/// One or more class names (separated by spaces) to be toggled for each element in the matched set. +/// +/// +/// A Boolean (not just truthy/falsy) value to determine whether the class should be added or removed. +/// +/// + + 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__" ) || ""; + } + }); + }; +jQuery.prototype.trigger = function( type, data ) { +/// +/// Execute all handlers and behaviors attached to the matched elements for the given event type. +/// 1 - trigger(eventType, extraParameters) +/// 2 - trigger(event) +/// +/// +/// A string containing a JavaScript event type, such as click or submit. +/// +/// +/// Additional parameters to pass along to the event handler. +/// +/// + + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }; +jQuery.prototype.triggerHandler = function( type, data ) { +/// +/// Execute all handlers attached to an element for an event. +/// +/// +/// A string containing a JavaScript event type, such as click or submit. +/// +/// +/// An array of additional parameters to pass along to the event handler. +/// +/// + + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }; +jQuery.prototype.unbind = function( type, fn ) { +/// +/// Remove a previously-attached event handler from the elements. +/// 1 - unbind(eventType, handler(eventObject)) +/// 2 - unbind(eventType, false) +/// 3 - unbind(event) +/// +/// +/// A string containing a JavaScript event type, such as click or submit. +/// +/// +/// The function that is to be no longer executed. +/// +/// + + // 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; + }; +jQuery.prototype.undelegate = function( selector, types, fn ) { +/// +/// Remove a handler from the event for all elements which match the current selector, now or in the future, based upon a specific set of root elements. +/// 1 - undelegate() +/// 2 - undelegate(selector, eventType) +/// 3 - undelegate(selector, eventType, handler) +/// 4 - undelegate(selector, events) +/// 5 - undelegate(namespace) +/// +/// +/// A selector which will be used to filter the event results. +/// +/// +/// A string containing a JavaScript event type, such as "click" or "keydown" +/// +/// +/// A function to execute at the time the event is triggered. +/// +/// + + if ( arguments.length === 0 ) { + return this.unbind( "live" ); + + } else { + return this.die( types, null, fn, selector ); + } + }; +jQuery.prototype.unload = function( data, fn ) { +/// +/// Bind an event handler to the "unload" JavaScript event. +/// 1 - unload(handler(eventObject)) +/// 2 - unload(eventData, handler(eventObject)) +/// +/// +/// A map of data that will be passed to the event handler. +/// +/// +/// A function to execute each time the event is triggered. +/// +/// + + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; +jQuery.prototype.unwrap = function() { +/// +/// Remove the parents of the set of matched elements from the DOM, leaving the matched elements in their place. +/// +/// + + return this.parent().each(function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + }).end(); + }; +jQuery.prototype.val = function( value ) { +/// +/// 1: Get the current value of the first element in the set of matched elements. +/// 1.1 - val() +/// 2: Set the value of each element in the set of matched elements. +/// 2.1 - val(value) +/// 2.2 - val(function(index, value)) +/// +/// +/// A string of text or an array of strings corresponding to the value of each matched element to set as selected/checked. +/// +/// + + 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.prototype.width = function( size ) { +/// +/// 1: Get the current computed width for the first element in the set of matched elements. +/// 1.1 - width() +/// 2: Set the CSS width of each element in the set of matched elements. +/// 2.1 - width(value) +/// 2.2 - width(function(index, width)) +/// +/// +/// An integer representing the number of pixels, or an integer along with an optional unit of measure appended (as a string). +/// +/// + + // Get window width or height + var elem = this[0]; + if ( !elem ) { + return size == null ? null : this; + } + + if ( jQuery.isFunction( size ) ) { + return this.each(function( i ) { + var self = jQuery( this ); + self[ type ]( size.call( this, i, self[ type ]() ) ); + }); + } + + if ( jQuery.isWindow( elem ) ) { + // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode + // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat + var docElemProp = elem.document.documentElement[ "client" + name ]; + return elem.document.compatMode === "CSS1Compat" && docElemProp || + elem.document.body[ "client" + name ] || docElemProp; + + // Get document width or height + } else if ( elem.nodeType === 9 ) { + // Either scroll[Width/Height] or offset[Width/Height], whichever is greater + return Math.max( + elem.documentElement["client" + name], + elem.body["scroll" + name], elem.documentElement["scroll" + name], + elem.body["offset" + name], elem.documentElement["offset" + name] + ); + + // Get or set width or height on the element + } else if ( size === undefined ) { + var orig = jQuery.css( elem, type ), + ret = parseFloat( orig ); + + return jQuery.isNaN( ret ) ? orig : ret; + + // Set the width or height on the element (default to pixels if value is unitless) + } else { + return this.css( type, typeof size === "string" ? size : size + "px" ); + } + }; +jQuery.prototype.wrap = function( html ) { +/// +/// Wrap an HTML structure around each element in the set of matched elements. +/// 1 - wrap(wrappingElement) +/// 2 - wrap(function(index)) +/// +/// +/// An HTML snippet, selector expression, jQuery object, or DOM element specifying the structure to wrap around the matched elements. +/// +/// + + return this.each(function() { + jQuery( this ).wrapAll( html ); + }); + }; +jQuery.prototype.wrapAll = function( html ) { +/// +/// Wrap an HTML structure around all elements in the set of matched elements. +/// +/// +/// An HTML snippet, selector expression, jQuery object, or DOM element specifying the structure to wrap around the matched elements. +/// +/// + + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapAll( html.call(this, i) ); + }); + } + + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); + + if ( this[0].parentNode ) { + wrap.insertBefore( this[0] ); + } + + wrap.map(function() { + var elem = this; + + while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { + elem = elem.firstChild; + } + + return elem; + }).append( this ); + } + + return this; + }; +jQuery.prototype.wrapInner = function( html ) { +/// +/// Wrap an HTML structure around the content of each element in the set of matched elements. +/// 1 - wrapInner(wrappingElement) +/// 2 - wrapInner(wrappingFunction) +/// +/// +/// An HTML snippet, selector expression, jQuery object, or DOM element specifying the structure to wrap around the content of the matched elements. +/// +/// + + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapInner( html.call(this, i) ); + }); + } + + return this.each(function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + }); + }; +jQuery.fn = jQuery.prototype; +jQuery.fn.init.prototype = jQuery.fn; +window.jQuery = window.$ = jQuery; +})(window); \ No newline at end of file diff --git a/SignalrKayakGateDemo/public/Scripts/jquery-1.6.2.js b/SignalrKayakGateDemo/public/Scripts/jquery-1.6.2.js new file mode 100644 index 000000000..f3201aacb --- /dev/null +++ b/SignalrKayakGateDemo/public/Scripts/jquery-1.6.2.js @@ -0,0 +1,8981 @@ +/*! + * jQuery JavaScript Library v1.6.2 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Thu Jun 30 14:16:56 2011 -0400 + */ +(function( window, undefined ) { + +// Use the correct document accordingly with window argument (sandbox) +var document = window.document, + navigator = window.navigator, + location = window.location; +var jQuery = (function() { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // (both of which we optimize for) + 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])/ig, + + // 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.2", + + // 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; + } + + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + 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 + // (xml & tmp used internally) + parseXML: function( data , xml , tmp ) { + + 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 ); + } + + tmp = xml.documentElement; + + if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) { + 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 ); + } + }, + + // Converts a dashed string to camelCased string; + // Used by both the css and data modules + camelCase: function( string ) { + return string.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 ( 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 ]( 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 + }; + if ( body ) { + jQuery.extend( testElementStyle, { + position: "absolute", + left: -1000, + top: -1000 + }); + } + 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 internalKey = jQuery.expando, getByName = typeof name === "string", thisCache, + + // 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 ][ 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; + } + + return getByName ? + // Check for both converted-to-camel and non-converted data property names + thisCache[ jQuery.camelCase( name ) ] || thisCache[ name ] : + thisCache; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var 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 ) { + var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ]; + + if ( thisCache ) { + 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 + if ( jQuery.support.deleteExpando || cache != window ) { + 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, + rinvalidChar = /\:|^on/, + formHook, 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].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 formHook for forms and if the name contains certain characters + } else if ( formHook && name !== "className" && + (jQuery.nodeName( elem, "form" ) || rinvalidChar.test( name )) ) { + + hooks = formHook; + } + } + } + + 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; + + if ( jQuery.support.getSetAttribute ) { + // Use removeAttribute in browsers that support it + elem.removeAttribute( name ); + } else { + jQuery.attr( elem, name, "" ); + elem.removeAttributeNode( elem.getAttributeNode( 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; + } + } + }, + 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; + } + }, + // Use the value property for back compat + // Use the formHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( formHook && jQuery.nodeName( elem, "button" ) ) { + return formHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( formHook && jQuery.nodeName( elem, "button" ) ) { + return formHook.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 )) !== undefined ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: {} +}); + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + return jQuery.prop( elem, name ) ? + 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 ) { + + // propFix is more comprehensive and contains all fixes + jQuery.attrFix = jQuery.propFix; + + // Use this for any attribute on a form in IE6/7 + formHook = jQuery.attrHooks.name = jQuery.attrHooks.title = 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 ) { + // Check form objects in IE (multiple bugs related) + // Only use nodeValue if the attribute node exists on the form + var ret = elem.getAttributeNode( name ); + if ( ret ) { + ret.nodeValue = value; + return 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; + } + } + } + }); +} + +// 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 = 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 = 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 = 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 ) { + if ( !elem || typeof elem === "string" ) { + return jQuery.inArray( this[0], + // If it receives a string, the selector is used + // If it receives nothing, the siblings are used + elem ? jQuery( elem ) : this.parent().children() ); + } + // 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 = /", "" ], + legend: [ 1, "
    ", "
    " ], + thead: [ 1, "", "
    " ], + 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 ' ); + + iframe_doc.close(); + + // Update the Iframe's hash, for great justice. + iframe.location.hash = hash; + } + }; + + })(); + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + // ^^^^^^^^^^^^^^^^^^^ REMOVE IF NOT SUPPORTING IE6/7/8 ^^^^^^^^^^^^^^^^^^^ + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + return self; + })(); + +})(jQuery,this); +/* +* jQuery Mobile Framework : "page" plugin +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +(function( $, undefined ) { + +$.widget( "mobile.page", $.mobile.widget, { + options: { + theme: "c", + domCache: false + }, + + _create: function() { + var $elem = this.element, + o = this.options; + + if ( this._trigger( "beforeCreate" ) === false ) { + return; + } + + $elem.addClass( "ui-page ui-body-" + o.theme ); + } +}); + +})( jQuery ); +/*! + * jQuery Mobile v@VERSION + * http://jquerymobile.com/ + * + * Copyright 2010, jQuery Project + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + */ + +(function( $, window, undefined ) { + + // jQuery.mobile configurable options + $.extend( $.mobile, { + + // Namespace used framework-wide for data-attrs. Default is no namespace + ns: "", + + // Define the url parameter used for referencing widget-generated sub-pages. + // Translates to to example.html&ui-page=subpageIdentifier + // hash segment before &ui-page= is used to make Ajax request + subPageUrlKey: "ui-page", + + // Class assigned to page currently in view, and during transitions + activePageClass: "ui-page-active", + + // Class used for "active" button state, from CSS framework + activeBtnClass: "ui-btn-active", + + // Automatically handle clicks and form submissions through Ajax, when same-domain + ajaxEnabled: true, + + // Automatically load and show pages based on location.hash + hashListeningEnabled: true, + + // Set default page transition - 'none' for no transitions + defaultPageTransition: "slide", + + // Minimum scroll distance that will be remembered when returning to a page + minScrollBack: screen.height / 2, + + // Set default dialog transition - 'none' for no transitions + defaultDialogTransition: "pop", + + // Show loading message during Ajax requests + // if false, message will not appear, but loading classes will still be toggled on html el + loadingMessage: "loading", + + // Error response message - appears when an Ajax page request fails + pageLoadErrorMessage: "Error Loading Page", + + //automatically initialize the DOM when it's ready + autoInitializePage: true, + + // Support conditions that must be met in order to proceed + // default enhanced qualifications are media query support OR IE 7+ + gradeA: function(){ + return $.support.mediaquery || $.mobile.browser.ie && $.mobile.browser.ie >= 7; + }, + + // TODO might be useful upstream in jquery itself ? + keyCode: { + ALT: 18, + BACKSPACE: 8, + CAPS_LOCK: 20, + COMMA: 188, + COMMAND: 91, + COMMAND_LEFT: 91, // COMMAND + COMMAND_RIGHT: 93, + CONTROL: 17, + DELETE: 46, + DOWN: 40, + END: 35, + ENTER: 13, + ESCAPE: 27, + HOME: 36, + INSERT: 45, + LEFT: 37, + MENU: 93, // COMMAND_RIGHT + NUMPAD_ADD: 107, + NUMPAD_DECIMAL: 110, + NUMPAD_DIVIDE: 111, + NUMPAD_ENTER: 108, + NUMPAD_MULTIPLY: 106, + NUMPAD_SUBTRACT: 109, + PAGE_DOWN: 34, + PAGE_UP: 33, + PERIOD: 190, + RIGHT: 39, + SHIFT: 16, + SPACE: 32, + TAB: 9, + UP: 38, + WINDOWS: 91 // COMMAND + }, + + // Scroll page vertically: scroll to 0 to hide iOS address bar, or pass a Y value + silentScroll: function( ypos ) { + if ( $.type( ypos ) !== "number" ) { + ypos = $.mobile.defaultHomeScroll; + } + + // prevent scrollstart and scrollstop events + $.event.special.scrollstart.enabled = false; + + setTimeout(function() { + window.scrollTo( 0, ypos ); + $( document ).trigger( "silentscroll", { x: 0, y: ypos }); + }, 20 ); + + setTimeout(function() { + $.event.special.scrollstart.enabled = true; + }, 150 ); + }, + + // Take a data attribute property, prepend the namespace + // and then camel case the attribute string + nsNormalize: function( prop ) { + if ( !prop ) { + return; + } + + return $.camelCase( $.mobile.ns + prop ); + } + }); + + // Mobile version of data and removeData and hasData methods + // ensures all data is set and retrieved using jQuery Mobile's data namespace + $.fn.jqmData = function( prop, value ) { + return this.data( prop ? $.mobile.nsNormalize( prop ) : prop, value ); + }; + + $.jqmData = function( elem, prop, value ) { + return $.data( elem, $.mobile.nsNormalize( prop ), value ); + }; + + $.fn.jqmRemoveData = function( prop ) { + return this.removeData( $.mobile.nsNormalize( prop ) ); + }; + + $.jqmRemoveData = function( elem, prop ) { + return $.removeData( elem, $.mobile.nsNormalize( prop ) ); + }; + + $.jqmHasData = function( elem, prop ) { + return $.hasData( elem, $.mobile.nsNormalize( prop ) ); + }; + + // Monkey-patching Sizzle to filter the :jqmData selector + var oldFind = $.find; + + $.find = function( selector, context, ret, extra ) { + selector = selector.replace(/:jqmData\(([^)]*)\)/g, "[data-" + ( $.mobile.ns || "" ) + "$1]"); + + return oldFind.call( this, selector, context, ret, extra ); + }; + + $.extend( $.find, oldFind ); + + $.find.matches = function( expr, set ) { + return $.find( expr, null, null, set ); + }; + + $.find.matchesSelector = function( node, expr ) { + return $.find( expr, null, null, [ node ] ).length > 0; + }; +})( jQuery, this ); +/* +* jQuery Mobile Framework : core utilities for auto ajax navigation, base tag mgmt, +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ +( function( $, undefined ) { + + //define vars for interal use + var $window = $( window ), + $html = $( 'html' ), + $head = $( 'head' ), + + //url path helpers for use in relative url management + path = { + + // This scary looking regular expression parses an absolute URL or its relative + // variants (protocol, site, document, query, and hash), into the various + // components (protocol, host, path, query, fragment, etc that make up the + // URL as well as some other commonly used sub-parts. When used with RegExp.exec() + // or String.match, it parses the URL into a results array that looks like this: + // + // [0]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread#msg-content + // [1]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread + // [2]: http://jblas:password@mycompany.com:8080/mail/inbox + // [3]: http://jblas:password@mycompany.com:8080 + // [4]: http: + // [5]: jblas:password@mycompany.com:8080 + // [6]: jblas:password + // [7]: jblas + // [8]: password + // [9]: mycompany.com:8080 + // [10]: mycompany.com + // [11]: 8080 + // [12]: /mail/inbox + // [13]: /mail/ + // [14]: inbox + // [15]: ?msg=1234&type=unread + // [16]: #msg-content + // + urlParseRE: /^(((([^:\/#\?]+:)?(?:\/\/((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?]+)(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/, + + //Parse a URL into a structure that allows easy access to + //all of the URL components by name. + parseUrl: function( url ) { + // If we're passed an object, we'll assume that it is + // a parsed url object and just return it back to the caller. + if ( $.type( url ) === "object" ) { + return url; + } + + var u = url || "", + matches = path.urlParseRE.exec( url ), + results; + if ( matches ) { + // Create an object that allows the caller to access the sub-matches + // by name. Note that IE returns an empty string instead of undefined, + // like all other browsers do, so we normalize everything so its consistent + // no matter what browser we're running on. + results = { + href: matches[0] || "", + hrefNoHash: matches[1] || "", + hrefNoSearch: matches[2] || "", + domain: matches[3] || "", + protocol: matches[4] || "", + authority: matches[5] || "", + username: matches[7] || "", + password: matches[8] || "", + host: matches[9] || "", + hostname: matches[10] || "", + port: matches[11] || "", + pathname: matches[12] || "", + directory: matches[13] || "", + filename: matches[14] || "", + search: matches[15] || "", + hash: matches[16] || "" + }; + } + return results || {}; + }, + + //Turn relPath into an asbolute path. absPath is + //an optional absolute path which describes what + //relPath is relative to. + makePathAbsolute: function( relPath, absPath ) { + if ( relPath && relPath.charAt( 0 ) === "/" ) { + return relPath; + } + + relPath = relPath || ""; + absPath = absPath ? absPath.replace( /^\/|(\/[^\/]*|[^\/]+)$/g, "" ) : ""; + + var absStack = absPath ? absPath.split( "/" ) : [], + relStack = relPath.split( "/" ); + for ( var i = 0; i < relStack.length; i++ ) { + var d = relStack[ i ]; + switch ( d ) { + case ".": + break; + case "..": + if ( absStack.length ) { + absStack.pop(); + } + break; + default: + absStack.push( d ); + break; + } + } + return "/" + absStack.join( "/" ); + }, + + //Returns true if both urls have the same domain. + isSameDomain: function( absUrl1, absUrl2 ) { + return path.parseUrl( absUrl1 ).domain === path.parseUrl( absUrl2 ).domain; + }, + + //Returns true for any relative variant. + isRelativeUrl: function( url ) { + // All relative Url variants have one thing in common, no protocol. + return path.parseUrl( url ).protocol === ""; + }, + + //Returns true for an absolute url. + isAbsoluteUrl: function( url ) { + return path.parseUrl( url ).protocol !== ""; + }, + + //Turn the specified realtive URL into an absolute one. This function + //can handle all relative variants (protocol, site, document, query, fragment). + makeUrlAbsolute: function( relUrl, absUrl ) { + if ( !path.isRelativeUrl( relUrl ) ) { + return relUrl; + } + + var relObj = path.parseUrl( relUrl ), + absObj = path.parseUrl( absUrl ), + protocol = relObj.protocol || absObj.protocol, + authority = relObj.authority || absObj.authority, + hasPath = relObj.pathname !== "", + pathname = path.makePathAbsolute( relObj.pathname || absObj.filename, absObj.pathname ), + search = relObj.search || ( !hasPath && absObj.search ) || "", + hash = relObj.hash; + + return protocol + "//" + authority + pathname + search + hash; + }, + + //Add search (aka query) params to the specified url. + addSearchParams: function( url, params ) { + var u = path.parseUrl( url ), + p = ( typeof params === "object" ) ? $.param( params ) : params, + s = u.search || "?"; + return u.hrefNoSearch + s + ( s.charAt( s.length - 1 ) !== "?" ? "&" : "" ) + p + ( u.hash || "" ); + }, + + convertUrlToDataUrl: function( absUrl ) { + var u = path.parseUrl( absUrl ); + if ( path.isEmbeddedPage( u ) ) { + // For embedded pages, remove the dialog hash key as in getFilePath(), + // otherwise the Data Url won't match the id of the embedded Page. + return u.hash.split( dialogHashKey )[0].replace( /^#/, "" ); + } else if ( path.isSameDomain( u, documentBase ) ) { + return u.hrefNoHash.replace( documentBase.domain, "" ); + } + return absUrl; + }, + + //get path from current hash, or from a file path + get: function( newPath ) { + if( newPath === undefined ) { + newPath = location.hash; + } + return path.stripHash( newPath ).replace( /[^\/]*\.[^\/*]+$/, '' ); + }, + + //return the substring of a filepath before the sub-page key, for making a server request + getFilePath: function( path ) { + var splitkey = '&' + $.mobile.subPageUrlKey; + return path && path.split( splitkey )[0].split( dialogHashKey )[0]; + }, + + //set location hash to path + set: function( path ) { + location.hash = path; + }, + + //test if a given url (string) is a path + //NOTE might be exceptionally naive + isPath: function( url ) { + return ( /\// ).test( url ); + }, + + //return a url path with the window's location protocol/hostname/pathname removed + clean: function( url ) { + return url.replace( documentBase.domain, "" ); + }, + + //just return the url without an initial # + stripHash: function( url ) { + return url.replace( /^#/, "" ); + }, + + //remove the preceding hash, any query params, and dialog notations + cleanHash: function( hash ) { + return path.stripHash( hash.replace( /\?.*$/, "" ).replace( dialogHashKey, "" ) ); + }, + + //check whether a url is referencing the same domain, or an external domain or different protocol + //could be mailto, etc + isExternal: function( url ) { + var u = path.parseUrl( url ); + return u.protocol && u.domain !== documentUrl.domain ? true : false; + }, + + hasProtocol: function( url ) { + return ( /^(:?\w+:)/ ).test( url ); + }, + + isEmbeddedPage: function( url ) { + var u = path.parseUrl( url ); + + //if the path is absolute, then we need to compare the url against + //both the documentUrl and the documentBase. The main reason for this + //is that links embedded within external documents will refer to the + //application document, whereas links embedded within the application + //document will be resolved against the document base. + if ( u.protocol !== "" ) { + return ( u.hash && ( u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ) ) ); + } + return (/^#/).test( u.href ); + } + }, + + //will be defined when a link is clicked and given an active class + $activeClickedLink = null, + + //urlHistory is purely here to make guesses at whether the back or forward button was clicked + //and provide an appropriate transition + urlHistory = { + // Array of pages that are visited during a single page load. + // Each has a url and optional transition, title, and pageUrl (which represents the file path, in cases where URL is obscured, such as dialogs) + stack: [], + + //maintain an index number for the active page in the stack + activeIndex: 0, + + //get active + getActive: function() { + return urlHistory.stack[ urlHistory.activeIndex ]; + }, + + getPrev: function() { + return urlHistory.stack[ urlHistory.activeIndex - 1 ]; + }, + + getNext: function() { + return urlHistory.stack[ urlHistory.activeIndex + 1 ]; + }, + + // addNew is used whenever a new page is added + addNew: function( url, transition, title, pageUrl ) { + //if there's forward history, wipe it + if( urlHistory.getNext() ) { + urlHistory.clearForward(); + } + + urlHistory.stack.push( {url : url, transition: transition, title: title, pageUrl: pageUrl } ); + + urlHistory.activeIndex = urlHistory.stack.length - 1; + }, + + //wipe urls ahead of active index + clearForward: function() { + urlHistory.stack = urlHistory.stack.slice( 0, urlHistory.activeIndex + 1 ); + }, + + directHashChange: function( opts ) { + var back , forward, newActiveIndex; + + // check if url isp in history and if it's ahead or behind current page + $.each( urlHistory.stack, function( i, historyEntry ) { + + //if the url is in the stack, it's a forward or a back + if( opts.currentUrl === historyEntry.url ) { + //define back and forward by whether url is older or newer than current page + back = i < urlHistory.activeIndex; + forward = !back; + newActiveIndex = i; + } + }); + + // save new page index, null check to prevent falsey 0 result + this.activeIndex = newActiveIndex !== undefined ? newActiveIndex : this.activeIndex; + + if( back ) { + opts.isBack(); + } else if( forward ) { + opts.isForward(); + } + }, + + //disable hashchange event listener internally to ignore one change + //toggled internally when location.hash is updated to match the url of a successful page load + ignoreNextHashChange: false + }, + + //define first selector to receive focus when a page is shown + focusable = "[tabindex],a,button:visible,select:visible,input", + + //queue to hold simultanious page transitions + pageTransitionQueue = [], + + //indicates whether or not page is in process of transitioning + isPageTransitioning = false, + + //nonsense hash change key for dialogs, so they create a history entry + dialogHashKey = "&ui-state=dialog", + + //existing base tag? + $base = $head.children( "base" ), + + //tuck away the original document URL minus any fragment. + documentUrl = path.parseUrl( location.href ), + + //if the document has an embedded base tag, documentBase is set to its + //initial value. If a base tag does not exist, then we default to the documentUrl. + documentBase = $base.length ? path.parseUrl( path.makeUrlAbsolute( $base.attr( "href" ), documentUrl.href ) ) : documentUrl, + + //cache the comparison once. + documentBaseDiffers = ( documentUrl.hrefNoHash !== documentBase.hrefNoHash ); + + //base element management, defined depending on dynamic base tag support + var base = $.support.dynamicBaseTag ? { + + //define base element, for use in routing asset urls that are referenced in Ajax-requested markup + element: ( $base.length ? $base : $( "", { href: documentBase.hrefNoHash } ).prependTo( $head ) ), + + //set the generated BASE element's href attribute to a new page's base path + set: function( href ) { + base.element.attr( "href", path.makeUrlAbsolute( href, documentBase ) ); + }, + + //set the generated BASE element's href attribute to a new page's base path + reset: function() { + base.element.attr( "href", documentBase.hrefNoHash ); + } + + } : undefined; + +/* + internal utility functions +--------------------------------------*/ + + + //direct focus to the page title, or otherwise first focusable element + function reFocus( page ) { + var lastClicked = page.jqmData( "lastClicked" ); + + if( lastClicked && lastClicked.length ) { + lastClicked.focus(); + } + else { + var pageTitle = page.find( ".ui-title:eq(0)" ); + + if( pageTitle.length ) { + pageTitle.focus(); + } + else{ + page.find( focusable ).eq( 0 ).focus(); + } + } + } + + //remove active classes after page transition or error + function removeActiveLinkClass( forceRemoval ) { + if( !!$activeClickedLink && ( !$activeClickedLink.closest( '.ui-page-active' ).length || forceRemoval ) ) { + $activeClickedLink.removeClass( $.mobile.activeBtnClass ); + } + $activeClickedLink = null; + } + + function releasePageTransitionLock() { + isPageTransitioning = false; + if( pageTransitionQueue.length > 0 ) { + $.mobile.changePage.apply( null, pageTransitionQueue.pop() ); + } + } + + //function for transitioning between two existing pages + function transitionPages( toPage, fromPage, transition, reverse ) { + + //get current scroll distance + var currScroll = $.support.scrollTop ? $window.scrollTop() : true, + toScroll = toPage.data( "lastScroll" ) || $.mobile.defaultHomeScroll, + screenHeight = getScreenHeight(); + + //if scrolled down, scroll to top + if( currScroll ){ + window.scrollTo( 0, $.mobile.defaultHomeScroll ); + } + + //if the Y location we're scrolling to is less than 10px, let it go for sake of smoothness + if( toScroll < $.mobile.minScrollBack ){ + toScroll = 0; + } + + if( fromPage ) { + //set as data for returning to that spot + fromPage + .height( screenHeight + currScroll ) + .jqmData( "lastScroll", currScroll ) + .jqmData( "lastClicked", $activeClickedLink ); + + //trigger before show/hide events + fromPage.data( "page" )._trigger( "beforehide", null, { nextPage: toPage } ); + } + toPage + .height( screenHeight + toScroll ) + .data( "page" )._trigger( "beforeshow", null, { prevPage: fromPage || $( "" ) } ); + + //clear page loader + $.mobile.hidePageLoadingMsg(); + + //find the transition handler for the specified transition. If there + //isn't one in our transitionHandlers dictionary, use the default one. + //call the handler immediately to kick-off the transition. + var th = $.mobile.transitionHandlers[transition || "none"] || $.mobile.defaultTransitionHandler, + promise = th( transition, reverse, toPage, fromPage ); + + promise.done(function() { + //reset toPage height bac + toPage.height( "" ); + + //jump to top or prev scroll, sometimes on iOS the page has not rendered yet. + if( toScroll ){ + $.mobile.silentScroll( toScroll ); + $( document ).one( "silentscroll", function() { reFocus( toPage ); } ); + } + else{ + reFocus( toPage ); + } + + //trigger show/hide events + if( fromPage ) { + fromPage.height("").data( "page" )._trigger( "hide", null, { nextPage: toPage } ); + } + + //trigger pageshow, define prevPage as either fromPage or empty jQuery obj + toPage.data( "page" )._trigger( "show", null, { prevPage: fromPage || $( "" ) } ); + }); + + return promise; + } + + //simply set the active page's minimum height to screen height, depending on orientation + function getScreenHeight(){ + var orientation = jQuery.event.special.orientationchange.orientation(), + port = orientation === "portrait", + winMin = port ? 480 : 320, + screenHeight = port ? screen.availHeight : screen.availWidth, + winHeight = Math.max( winMin, $( window ).height() ), + pageMin = Math.min( screenHeight, winHeight ); + + return pageMin; + } + + //simply set the active page's minimum height to screen height, depending on orientation + function resetActivePageHeight(){ + $( "." + $.mobile.activePageClass ).css( "min-height", getScreenHeight() ); + } + + //shared page enhancements + function enhancePage( $page, role ) { + // If a role was specified, make sure the data-role attribute + // on the page element is in sync. + if( role ) { + $page.attr( "data-" + $.mobile.ns + "role", role ); + } + + //run page plugin + $page.page(); + } + +/* exposed $.mobile methods */ + + //animation complete callback + $.fn.animationComplete = function( callback ) { + if( $.support.cssTransitions ) { + return $( this ).one( 'webkitAnimationEnd', callback ); + } + else{ + // defer execution for consistency between webkit/non webkit + setTimeout( callback, 0 ); + return $( this ); + } + }; + + //update location.hash, with or without triggering hashchange event + //TODO - deprecate this one at 1.0 + $.mobile.updateHash = path.set; + + //expose path object on $.mobile + $.mobile.path = path; + + //expose base object on $.mobile + $.mobile.base = base; + + //url stack, useful when plugins need to be aware of previous pages viewed + //TODO: deprecate this one at 1.0 + $.mobile.urlstack = urlHistory.stack; + + //history stack + $.mobile.urlHistory = urlHistory; + + //default non-animation transition handler + $.mobile.noneTransitionHandler = function( name, reverse, $toPage, $fromPage ) { + if ( $fromPage ) { + $fromPage.removeClass( $.mobile.activePageClass ); + } + $toPage.addClass( $.mobile.activePageClass ); + + return $.Deferred().resolve( name, reverse, $toPage, $fromPage ).promise(); + }; + + //default handler for unknown transitions + $.mobile.defaultTransitionHandler = $.mobile.noneTransitionHandler; + + //transition handler dictionary for 3rd party transitions + $.mobile.transitionHandlers = { + none: $.mobile.defaultTransitionHandler + }; + + //enable cross-domain page support + $.mobile.allowCrossDomainPages = false; + + //return the original document url + $.mobile.getDocumentUrl = function(asParsedObject) { + return asParsedObject ? $.extend( {}, documentUrl ) : documentUrl.href; + }; + + //return the original document base url + $.mobile.getDocumentBase = function(asParsedObject) { + return asParsedObject ? $.extend( {}, documentBase ) : documentBase.href; + }; + + // Load a page into the DOM. + $.mobile.loadPage = function( url, options ) { + // This function uses deferred notifications to let callers + // know when the page is done loading, or if an error has occurred. + var deferred = $.Deferred(), + + // The default loadPage options with overrides specified by + // the caller. + settings = $.extend( {}, $.mobile.loadPage.defaults, options ), + + // The DOM element for the page after it has been loaded. + page = null, + + // If the reloadPage option is true, and the page is already + // in the DOM, dupCachedPage will be set to the page element + // so that it can be removed after the new version of the + // page is loaded off the network. + dupCachedPage = null, + + // determine the current base url + findBaseWithDefault = function(){ + var closestBase = ( $.mobile.activePage && getClosestBaseUrl( $.mobile.activePage ) ); + return closestBase || documentBase.hrefNoHash; + }, + + // The absolute version of the URL passed into the function. This + // version of the URL may contain dialog/subpage params in it. + absUrl = path.makeUrlAbsolute( url, findBaseWithDefault() ); + + + // If the caller provided data, and we're using "get" request, + // append the data to the URL. + if ( settings.data && settings.type === "get" ) { + absUrl = path.addSearchParams( absUrl, settings.data ); + settings.data = undefined; + } + + // The absolute version of the URL minus any dialog/subpage params. + // In otherwords the real URL of the page to be loaded. + var fileUrl = path.getFilePath( absUrl ), + + // The version of the Url actually stored in the data-url attribute of + // the page. For embedded pages, it is just the id of the page. For pages + // within the same domain as the document base, it is the site relative + // path. For cross-domain pages (Phone Gap only) the entire absolute Url + // used to load the page. + dataUrl = path.convertUrlToDataUrl( absUrl ); + + // Make sure we have a pageContainer to work with. + settings.pageContainer = settings.pageContainer || $.mobile.pageContainer; + + // Check to see if the page already exists in the DOM. + page = settings.pageContainer.children( ":jqmData(url='" + dataUrl + "')" ); + + // Reset base to the default document base. + if ( base ) { + base.reset(); + } + + // If the page we are interested in is already in the DOM, + // and the caller did not indicate that we should force a + // reload of the file, we are done. Otherwise, track the + // existing page as a duplicated. + if ( page.length ) { + if ( !settings.reloadPage ) { + enhancePage( page, settings.role ); + deferred.resolve( absUrl, options, page ); + return deferred.promise(); + } + dupCachedPage = page; + } + + if ( settings.showLoadMsg ) { + + // This configurable timeout allows cached pages a brief delay to load without showing a message + var loadMsgDelay = setTimeout(function(){ + $.mobile.showPageLoadingMsg(); + }, settings.loadMsgDelay ), + + // Shared logic for clearing timeout and removing message. + hideMsg = function(){ + + // Stop message show timer + clearTimeout( loadMsgDelay ); + + // Hide loading message + $.mobile.hidePageLoadingMsg(); + }; + } + + if ( !( $.mobile.allowCrossDomainPages || path.isSameDomain( documentUrl, absUrl ) ) ) { + deferred.reject( absUrl, options ); + } else { + // Load the new page. + $.ajax({ + url: fileUrl, + type: settings.type, + data: settings.data, + dataType: "html", + success: function( html ) { + //pre-parse html to check for a data-url, + //use it as the new fileUrl, base path, etc + var all = $( "
    " ), + + //page title regexp + newPageTitle = html.match( /]*>([^<]*)/ ) && RegExp.$1, + + // TODO handle dialogs again + pageElemRegex = new RegExp( ".*(<[^>]+\\bdata-" + $.mobile.ns + "role=[\"']?page[\"']?[^>]*>).*" ), + dataUrlRegex = new RegExp( "\\bdata-" + $.mobile.ns + "url=[\"']?([^\"'>]*)[\"']?" ); + + + // data-url must be provided for the base tag so resource requests can be directed to the + // correct url. loading into a temprorary element makes these requests immediately + if( pageElemRegex.test( html ) + && RegExp.$1 + && dataUrlRegex.test( RegExp.$1 ) + && RegExp.$1 ) { + url = fileUrl = path.getFilePath( RegExp.$1 ); + } + else{ + + } + + if ( base ) { + base.set( fileUrl ); + } + + //workaround to allow scripts to execute when included in page divs + all.get( 0 ).innerHTML = html; + page = all.find( ":jqmData(role='page'), :jqmData(role='dialog')" ).first(); + + //if page elem couldn't be found, create one and insert the body element's contents + if( !page.length ){ + page = $( "
    " + html.split( /<\/?body[^>]*>/gmi )[1] + "
    " ); + } + + if ( newPageTitle && !page.jqmData( "title" ) ) { + page.jqmData( "title", newPageTitle ); + } + + //rewrite src and href attrs to use a base url + if( !$.support.dynamicBaseTag ) { + var newPath = path.get( fileUrl ); + page.find( "[src], link[href], a[rel='external'], :jqmData(ajax='false'), a[target]" ).each(function() { + var thisAttr = $( this ).is( '[href]' ) ? 'href' : + $(this).is('[src]') ? 'src' : 'action', + thisUrl = $( this ).attr( thisAttr ); + + // XXX_jblas: We need to fix this so that it removes the document + // base URL, and then prepends with the new page URL. + //if full path exists and is same, chop it - helps IE out + thisUrl = thisUrl.replace( location.protocol + '//' + location.host + location.pathname, '' ); + + if( !/^(\w+:|#|\/)/.test( thisUrl ) ) { + $( this ).attr( thisAttr, newPath + thisUrl ); + } + }); + } + + //append to page and enhance + page + .attr( "data-" + $.mobile.ns + "url", path.convertUrlToDataUrl( fileUrl ) ) + .appendTo( settings.pageContainer ); + + // wait for page creation to leverage options defined on widget + page.one('pagecreate', function(){ + + // when dom caching is not enabled bind to remove the page on hide + if( !page.data("page").options.domCache ){ + page.bind( "pagehide.remove", function(){ + $(this).remove(); + }); + } + }); + + enhancePage( page, settings.role ); + + // Enhancing the page may result in new dialogs/sub pages being inserted + // into the DOM. If the original absUrl refers to a sub-page, that is the + // real page we are interested in. + if ( absUrl.indexOf( "&" + $.mobile.subPageUrlKey ) > -1 ) { + page = settings.pageContainer.children( ":jqmData(url='" + dataUrl + "')" ); + } + + //bind pageHide to removePage after it's hidden, if the page options specify to do so + + // Remove loading message. + if ( settings.showLoadMsg ) { + hideMsg(); + } + + deferred.resolve( absUrl, options, page, dupCachedPage ); + }, + error: function() { + //set base back to current path + if( base ) { + base.set( path.get() ); + } + + // Remove loading message. + if ( settings.showLoadMsg ) { + + // Remove loading message. + hideMsg(); + + //show error message + $( "

    "+ $.mobile.pageLoadErrorMessage +"

    " ) + .css({ "display": "block", "opacity": 0.96, "top": $window.scrollTop() + 100 }) + .appendTo( settings.pageContainer ) + .delay( 800 ) + .fadeOut( 400, function() { + $( this ).remove(); + }); + } + + deferred.reject( absUrl, options ); + } + }); + } + + return deferred.promise(); + }; + + $.mobile.loadPage.defaults = { + type: "get", + data: undefined, + reloadPage: false, + role: undefined, // By default we rely on the role defined by the @data-role attribute. + showLoadMsg: false, + pageContainer: undefined, + loadMsgDelay: 50 // This delay allows loads that pull from browser cache to occur without showing the loading message. + }; + + // Show a specific page in the page container. + $.mobile.changePage = function( toPage, options ) { + // XXX: REMOVE_BEFORE_SHIPPING_1.0 + // This is temporary code that makes changePage() compatible with previous alpha versions. + if ( typeof options !== "object" ) { + var opts = null; + + // Map old-style call signature for form submit to the new options object format. + if ( typeof toPage === "object" && toPage.url && toPage.type ) { + opts = { + type: toPage.type, + data: toPage.data, + forcePageLoad: true + }; + toPage = toPage.url; + } + + // The arguments passed into the function need to be re-mapped + // to the new options object format. + var len = arguments.length; + if ( len > 1 ) { + var argNames = [ "transition", "reverse", "changeHash", "fromHashChange" ], i; + for ( i = 1; i < len; i++ ) { + var a = arguments[ i ]; + if ( typeof a !== "undefined" ) { + opts = opts || {}; + opts[ argNames[ i - 1 ] ] = a; + } + } + } + + // If an options object was created, then we know changePage() was called + // with an old signature. + if ( opts ) { + return $.mobile.changePage( toPage, opts ); + } + } + // XXX: REMOVE_BEFORE_SHIPPING_1.0 + + // If we are in the midst of a transition, queue the current request. + // We'll call changePage() once we're done with the current transition to + // service the request. + if( isPageTransitioning ) { + pageTransitionQueue.unshift( arguments ); + return; + } + + // Set the isPageTransitioning flag to prevent any requests from + // entering this method while we are in the midst of loading a page + // or transitioning. + + isPageTransitioning = true; + + var settings = $.extend( {}, $.mobile.changePage.defaults, options ); + + // Make sure we have a pageContainer to work with. + settings.pageContainer = settings.pageContainer || $.mobile.pageContainer; + + // If the caller passed us a url, call loadPage() + // to make sure it is loaded into the DOM. We'll listen + // to the promise object it returns so we know when + // it is done loading or if an error ocurred. + if ( typeof toPage == "string" ) { + $.mobile.loadPage( toPage, settings ) + .done(function( url, options, newPage, dupCachedPage ) { + isPageTransitioning = false; + options.duplicateCachedPage = dupCachedPage; + $.mobile.changePage( newPage, options ); + }) + .fail(function( url, options ) { + // XXX_jblas: Fire off changepagefailed notificaiton. + isPageTransitioning = false; + + //clear out the active button state + removeActiveLinkClass( true ); + + //release transition lock so navigation is free again + releasePageTransitionLock(); + settings.pageContainer.trigger("changepagefailed"); + }); + return; + } + + // The caller passed us a real page DOM element. Update our + // internal state and then trigger a transition to the page. + var mpc = settings.pageContainer, + fromPage = $.mobile.activePage, + url = toPage.jqmData( "url" ), + // The pageUrl var is usually the same as url, except when url is obscured as a dialog url. pageUrl always contains the file path + pageUrl = url, + fileUrl = path.getFilePath( url ), + active = urlHistory.getActive(), + activeIsInitialPage = urlHistory.activeIndex === 0, + historyDir = 0, + pageTitle = document.title, + isDialog = settings.role === "dialog" || toPage.jqmData( "role" ) === "dialog"; + + // Let listeners know we're about to change the current page. + mpc.trigger( "beforechangepage" ); + + // If we are trying to transition to the same page that we are currently on ignore the request. + // an illegal same page request is defined by the current page being the same as the url, as long as there's history + // and toPage is not an array or object (those are allowed to be "same") + // + // XXX_jblas: We need to remove this at some point when we allow for transitions + // to the same page. + if( fromPage && fromPage[0] === toPage[0] ) { + isPageTransitioning = false; + mpc.trigger( "changepage" ); + return; + } + + // We need to make sure the page we are given has already been enhanced. + enhancePage( toPage, settings.role ); + + // If the changePage request was sent from a hashChange event, check to see if the + // page is already within the urlHistory stack. If so, we'll assume the user hit + // the forward/back button and will try to match the transition accordingly. + if( settings.fromHashChange ) { + urlHistory.directHashChange({ + currentUrl: url, + isBack: function() { historyDir = -1; }, + isForward: function() { historyDir = 1; } + }); + } + + // Kill the keyboard. + // XXX_jblas: We need to stop crawling the entire document to kill focus. Instead, + // we should be tracking focus with a live() handler so we already have + // the element in hand at this point. + // Wrap this in a try/catch block since IE9 throw "Unspecified error" if document.activeElement + // is undefined when we are in an IFrame. + try { + $( document.activeElement || "" ).add( "input:focus, textarea:focus, select:focus" ).blur(); + } catch(e) {} + + // If we're displaying the page as a dialog, we don't want the url + // for the dialog content to be used in the hash. Instead, we want + // to append the dialogHashKey to the url of the current page. + if ( isDialog && active ) { + url = active.url + dialogHashKey; + } + + // Set the location hash. + if( settings.changeHash !== false && url ) { + //disable hash listening temporarily + urlHistory.ignoreNextHashChange = true; + //update hash and history + path.set( url ); + } + + //if title element wasn't found, try the page div data attr too + var newPageTitle = toPage.jqmData( "title" ) || toPage.children(":jqmData(role='header')").find(".ui-title" ).text(); + if( !!newPageTitle && pageTitle == document.title ) { + pageTitle = newPageTitle; + } + + //add page to history stack if it's not back or forward + if( !historyDir ) { + urlHistory.addNew( url, settings.transition, pageTitle, pageUrl ); + } + + //set page title + document.title = urlHistory.getActive().title; + + //set "toPage" as activePage + $.mobile.activePage = toPage; + + // Make sure we have a transition defined. + settings.transition = settings.transition + || ( ( historyDir && !activeIsInitialPage ) ? active.transition : undefined ) + || ( isDialog ? $.mobile.defaultDialogTransition : $.mobile.defaultPageTransition ); + + // If we're navigating back in the URL history, set reverse accordingly. + settings.reverse = settings.reverse || historyDir < 0; + + transitionPages( toPage, fromPage, settings.transition, settings.reverse ) + .done(function() { + removeActiveLinkClass(); + + //if there's a duplicateCachedPage, remove it from the DOM now that it's hidden + if ( settings.duplicateCachedPage ) { + settings.duplicateCachedPage.remove(); + } + + //remove initial build class (only present on first pageshow) + $html.removeClass( "ui-mobile-rendering" ); + + releasePageTransitionLock(); + + // Let listeners know we're all done changing the current page. + mpc.trigger( "changepage" ); + }); + }; + + $.mobile.changePage.defaults = { + transition: undefined, + reverse: false, + changeHash: true, + fromHashChange: false, + role: undefined, // By default we rely on the role defined by the @data-role attribute. + duplicateCachedPage: undefined, + pageContainer: undefined, + showLoadMsg: true //loading message shows by default when pages are being fetched during changePage + }; + +/* Event Bindings - hashchange, submit, and click */ + function findClosestLink( ele ) + { + while ( ele ) { + if ( ele.nodeName.toLowerCase() == "a" ) { + break; + } + ele = ele.parentNode; + } + return ele; + } + + // The base URL for any given element depends on the page it resides in. + function getClosestBaseUrl( ele ) + { + // Find the closest page and extract out its url. + var url = $( ele ).closest( ".ui-page" ).jqmData( "url" ), + base = documentBase.hrefNoHash; + + if ( !url || !path.isPath( url ) ) { + url = base; + } + + return path.makeUrlAbsolute( url, base); + } + + + //The following event bindings should be bound after mobileinit has been triggered + //the following function is called in the init file + $.mobile._registerInternalEvents = function(){ + + //bind to form submit events, handle with Ajax + $( "form" ).live('submit', function( event ) { + var $this = $( this ); + if( !$.mobile.ajaxEnabled || + $this.is( ":jqmData(ajax='false')" ) ) { + return; + } + + var type = $this.attr( "method" ), + target = $this.attr( "target" ), + url = $this.attr( "action" ); + + // If no action is specified, browsers default to using the + // URL of the document containing the form. Since we dynamically + // pull in pages from external documents, the form should submit + // to the URL for the source document of the page containing + // the form. + if ( !url ) { + // Get the @data-url for the page containing the form. + url = getClosestBaseUrl( $this ); + if ( url === documentBase.hrefNoHash ) { + // The url we got back matches the document base, + // which means the page must be an internal/embedded page, + // so default to using the actual document url as a browser + // would. + url = documentUrl.hrefNoSearch; + } + } + + url = path.makeUrlAbsolute( url, getClosestBaseUrl($this) ); + + //external submits use regular HTTP + if( path.isExternal( url ) || target ) { + return; + } + + $.mobile.changePage( + url, + { + type: type && type.length && type.toLowerCase() || "get", + data: $this.serialize(), + transition: $this.jqmData( "transition" ), + direction: $this.jqmData( "direction" ), + reloadPage: true + } + ); + event.preventDefault(); + }); + + //add active state on vclick + $( document ).bind( "vclick", function( event ) { + var link = findClosestLink( event.target ); + if ( link ) { + if ( path.parseUrl( link.getAttribute( "href" ) || "#" ).hash !== "#" ) { + $( link ).closest( ".ui-btn" ).not( ".ui-disabled" ).addClass( $.mobile.activeBtnClass ); + $( "." + $.mobile.activePageClass + " .ui-btn" ).not( link ).blur(); + } + } + }); + + // click routing - direct to HTTP or Ajax, accordingly + $( document ).bind( "click", function( event ) { + var link = findClosestLink( event.target ); + if ( !link ) { + return; + } + + var $link = $( link ), + //remove active link class if external (then it won't be there if you come back) + httpCleanup = function(){ + window.setTimeout( function() { removeActiveLinkClass( true ); }, 200 ); + }; + + //if there's a data-rel=back attr, go back in history + if( $link.is( ":jqmData(rel='back')" ) ) { + window.history.back(); + return false; + } + + //if ajax is disabled, exit early + if( !$.mobile.ajaxEnabled ){ + httpCleanup(); + //use default click handling + return; + } + + var baseUrl = getClosestBaseUrl( $link ), + + //get href, if defined, otherwise default to empty hash + href = path.makeUrlAbsolute( $link.attr( "href" ) || "#", baseUrl ); + + // XXX_jblas: Ideally links to application pages should be specified as + // an url to the application document with a hash that is either + // the site relative path or id to the page. But some of the + // internal code that dynamically generates sub-pages for nested + // lists and select dialogs, just write a hash in the link they + // create. This means the actual URL path is based on whatever + // the current value of the base tag is at the time this code + // is called. For now we are just assuming that any url with a + // hash in it is an application page reference. + if ( href.search( "#" ) != -1 ) { + href = href.replace( /[^#]*#/, "" ); + if ( !href ) { + //link was an empty hash meant purely + //for interaction, so we ignore it. + event.preventDefault(); + return; + } else if ( path.isPath( href ) ) { + //we have apath so make it the href we want to load. + href = path.makeUrlAbsolute( href, baseUrl ); + } else { + //we have a simple id so use the documentUrl as its base. + href = path.makeUrlAbsolute( "#" + href, documentUrl.hrefNoHash ); + } + } + + // Should we handle this link, or let the browser deal with it? + var useDefaultUrlHandling = $link.is( "[rel='external']" ) || $link.is( ":jqmData(ajax='false')" ) || $link.is( "[target]" ), + + // Some embedded browsers, like the web view in Phone Gap, allow cross-domain XHR + // requests if the document doing the request was loaded via the file:// protocol. + // This is usually to allow the application to "phone home" and fetch app specific + // data. We normally let the browser handle external/cross-domain urls, but if the + // allowCrossDomainPages option is true, we will allow cross-domain http/https + // requests to go through our page loading logic. + isCrossDomainPageLoad = ( $.mobile.allowCrossDomainPages && documentUrl.protocol === "file:" && href.search( /^https?:/ ) != -1 ), + + //check for protocol or rel and its not an embedded page + //TODO overlap in logic from isExternal, rel=external check should be + // moved into more comprehensive isExternalLink + isExternal = useDefaultUrlHandling || ( path.isExternal( href ) && !isCrossDomainPageLoad ); + + $activeClickedLink = $link.closest( ".ui-btn" ); + + if( isExternal ) { + httpCleanup(); + //use default click handling + return; + } + + //use ajax + var transition = $link.jqmData( "transition" ), + direction = $link.jqmData( "direction" ), + reverse = ( direction && direction === "reverse" ) || + // deprecated - remove by 1.0 + $link.jqmData( "back" ), + + //this may need to be more specific as we use data-rel more + role = $link.attr( "data-" + $.mobile.ns + "rel" ) || undefined; + + $.mobile.changePage( href, { transition: transition, reverse: reverse, role: role } ); + event.preventDefault(); + }); + + //prefetch pages when anchors with data-prefetch are encountered + $( ".ui-page" ).live( "pageshow.prefetch", function(){ + var urls = []; + $( this ).find( "a:jqmData(prefetch)" ).each(function(){ + var url = $( this ).attr( "href" ); + if ( url && $.inArray( url, urls ) === -1 ) { + urls.push( url ); + $.mobile.loadPage( url ); + } + }); + } ); + + //hashchange event handler + $window.bind( "hashchange", function( e, triggered ) { + //find first page via hash + var to = path.stripHash( location.hash ), + //transition is false if it's the first page, undefined otherwise (and may be overridden by default) + transition = $.mobile.urlHistory.stack.length === 0 ? "none" : undefined; + + //if listening is disabled (either globally or temporarily), or it's a dialog hash + if( !$.mobile.hashListeningEnabled || urlHistory.ignoreNextHashChange ) { + urlHistory.ignoreNextHashChange = false; + return; + } + + // special case for dialogs + if( urlHistory.stack.length > 1 && + to.indexOf( dialogHashKey ) > -1 ) { + + // If current active page is not a dialog skip the dialog and continue + // in the same direction + if(!$.mobile.activePage.is( ".ui-dialog" )) { + //determine if we're heading forward or backward and continue accordingly past + //the current dialog + urlHistory.directHashChange({ + currentUrl: to, + isBack: function() { window.history.back(); }, + isForward: function() { window.history.forward(); } + }); + + // prevent changepage + return; + } else { + var setTo = function() { to = $.mobile.urlHistory.getActive().pageUrl; }; + // if the current active page is a dialog and we're navigating + // to a dialog use the dialog objected saved in the stack + urlHistory.directHashChange({ currentUrl: to, isBack: setTo, isForward: setTo }); + } + } + + //if to is defined, load it + if ( to ) { + to = ( typeof to === "string" && !path.isPath( to ) ) ? ( '#' + to ) : to; + $.mobile.changePage( to, { transition: transition, changeHash: false, fromHashChange: true } ); + } + //there's no hash, go to the first page in the dom + else { + $.mobile.changePage( $.mobile.firstPage, { transition: transition, changeHash: false, fromHashChange: true } ); + } + }); + + //set page min-heights to be device specific + $( document ).bind( "pageshow", resetActivePageHeight ); + $( window ).bind( "throttledresize", resetActivePageHeight ); + + };//_registerInternalEvents callback + +})( jQuery ); +/*! + * jQuery Mobile v@VERSION + * http://jquerymobile.com/ + * + * Copyright 2010, jQuery Project + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + */ + +(function( $, window, undefined ) { + +function css3TransitionHandler( name, reverse, $to, $from ) { + + var deferred = new $.Deferred(), + reverseClass = reverse ? " reverse" : "", + viewportClass = "ui-mobile-viewport-transitioning viewport-" + name, + doneFunc = function() { + + $to.add( $from ).removeClass( "out in reverse " + name ); + + if ( $from ) { + $from.removeClass( $.mobile.activePageClass ); + } + + $to.parent().removeClass( viewportClass ); + + deferred.resolve( name, reverse, $to, $from ); + }; + + $to.animationComplete( doneFunc ); + + $to.parent().addClass( viewportClass ); + + if ( $from ) { + $from.addClass( name + " out" + reverseClass ); + } + $to.addClass( $.mobile.activePageClass + " " + name + " in" + reverseClass ); + + return deferred.promise(); +} + +// Make our transition handler public. +$.mobile.css3TransitionHandler = css3TransitionHandler; + +// If the default transition handler is the 'none' handler, replace it with our handler. +if ( $.mobile.defaultTransitionHandler === $.mobile.noneTransitionHandler ) { + $.mobile.defaultTransitionHandler = css3TransitionHandler; +} + +})( jQuery, this ); +/* +* jQuery Mobile Framework : "degradeInputs" plugin - degrades inputs to another type after custom enhancements are made. +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +(function( $, undefined ) { + +$.mobile.page.prototype.options.degradeInputs = { + color: false, + date: false, + datetime: false, + "datetime-local": false, + email: false, + month: false, + number: false, + range: "number", + search: true, + tel: false, + time: false, + url: false, + week: false +}; + +$.mobile.page.prototype.options.keepNative = ":jqmData(role='none'), :jqmData(role='nojs')"; + + +//auto self-init widgets +$( document ).bind( "pagecreate enhance", function( e ){ + + var page = $( e.target ).data( "page" ), + o = page.options; + + // degrade inputs to avoid poorly implemented native functionality + $( e.target ).find( "input" ).not( o.keepNative ).each(function() { + var $this = $( this ), + type = this.getAttribute( "type" ), + optType = o.degradeInputs[ type ] || "text"; + + if ( o.degradeInputs[ type ] ) { + $this.replaceWith( + $( "
    " ).html( $this.clone() ).html() + .replace( /\s+type=["']?\w+['"]?/, " type=\"" + optType + "\" data-" + $.mobile.ns + "type=\"" + type + "\" " ) + ); + } + }); + +}); + +})( jQuery );/* +* jQuery Mobile Framework : "dialog" plugin. +* Copyright (c) jQuery Project +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. +*/ + +(function( $, window, undefined ) { + +$.widget( "mobile.dialog", $.mobile.widget, { + options: { + closeBtnText : "Close", + theme : "a", + initSelector : ":jqmData(role='dialog')" + }, + _create: function() { + var $el = this.element, + pageTheme = $el.attr( "class" ).match( /ui-body-[a-z]/ ); + + if( pageTheme.length ){ + $el.removeClass( pageTheme[ 0 ] ); + } + + $el.addClass( "ui-body-" + this.options.theme ); + + // Class the markup for dialog styling + // Set aria role + $el.attr( "role", "dialog" ) + .addClass( "ui-dialog" ) + .find( ":jqmData(role='header')" ) + .addClass( "ui-corner-top ui-overlay-shadow" ) + .prepend( ""+ this.options.closeBtnText + "" ) + .end() + .find( ":jqmData(role='content'),:jqmData(role='footer')" ) + .last() + .addClass( "ui-corner-bottom ui-overlay-shadow" ); + + /* bind events + - clicks and submits should use the closing transition that the dialog opened with + unless a data-transition is specified on the link/form + - if the click was on the close button, or the link has a data-rel="back" it'll go back in history naturally + */ + $el.bind( "vclick submit", function( event ) { + var $target = $( event.target ).closest( event.type === "vclick" ? "a" : "form" ), + active; + + if ( $target.length && !$target.jqmData( "transition" ) ) { + + active = $.mobile.urlHistory.getActive() || {}; + + $target.attr( "data-" + $.mobile.ns + "transition", ( active.transition || $.mobile.defaultDialogTransition ) ) + .attr( "data-" + $.mobile.ns + "direction", "reverse" ); + } + }) + .bind( "pagehide", function() { + $( this ).find( "." + $.mobile.activeBtnClass ).removeClass( $.mobile.activeBtnClass ); + }); + }, + + // Close method goes back in history + close: function() { + window.history.back(); + } +}); + +//auto self-init widgets +$( $.mobile.dialog.prototype.options.initSelector ).live( "pagecreate", function(){ + $( this ).dialog(); +}); + +})( jQuery, this ); +/* +* jQuery Mobile Framework : This plugin handles theming and layout of headers, footers, and content areas +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +(function( $, undefined ) { + +$.mobile.page.prototype.options.backBtnText = "Back"; +$.mobile.page.prototype.options.addBackBtn = false; +$.mobile.page.prototype.options.backBtnTheme = null; +$.mobile.page.prototype.options.headerTheme = "a"; +$.mobile.page.prototype.options.footerTheme = "a"; +$.mobile.page.prototype.options.contentTheme = null; + +$( ":jqmData(role='page'), :jqmData(role='dialog')" ).live( "pagecreate", function( e ) { + + var $page = $( this ), + o = $page.data( "page" ).options, + pageTheme = o.theme; + + $( ":jqmData(role='header'), :jqmData(role='footer'), :jqmData(role='content')", this ).each(function() { + var $this = $( this ), + role = $this.jqmData( "role" ), + theme = $this.jqmData( "theme" ), + $headeranchors, + leftbtn, + rightbtn, + backBtn; + + $this.addClass( "ui-" + role ); + + //apply theming and markup modifications to page,header,content,footer + if ( role === "header" || role === "footer" ) { + + var thisTheme = theme || ( role === "header" ? o.headerTheme : o.footerTheme ) || pageTheme; + + //add theme class + $this.addClass( "ui-bar-" + thisTheme ); + + // Add ARIA role + $this.attr( "role", role === "header" ? "banner" : "contentinfo" ); + + // Right,left buttons + $headeranchors = $this.children( "a" ); + leftbtn = $headeranchors.hasClass( "ui-btn-left" ); + rightbtn = $headeranchors.hasClass( "ui-btn-right" ); + + if ( !leftbtn ) { + leftbtn = $headeranchors.eq( 0 ).not( ".ui-btn-right" ).addClass( "ui-btn-left" ).length; + } + + if ( !rightbtn ) { + rightbtn = $headeranchors.eq( 1 ).addClass( "ui-btn-right" ).length; + } + + // Auto-add back btn on pages beyond first view + if ( o.addBackBtn && role === "header" && + $( ".ui-page" ).length > 1 && + $this.jqmData( "url" ) !== $.mobile.path.stripHash( location.hash ) && + !leftbtn ) { + + backBtn = $( ""+ o.backBtnText +"" ).prependTo( $this ); + + // If theme is provided, override default inheritance + backBtn.attr( "data-"+ $.mobile.ns +"theme", o.backBtnTheme || thisTheme ); + } + + // Page title + $this.children( "h1, h2, h3, h4, h5, h6" ) + .addClass( "ui-title" ) + // Regardless of h element number in src, it becomes h1 for the enhanced page + .attr({ + "tabindex": "0", + "role": "heading", + "aria-level": "1" + }); + + } else if ( role === "content" ) { + + $this.addClass( "ui-body-" + ( theme || pageTheme || o.contentTheme ) ); + + // Add ARIA role + $this.attr( "role", "main" ); + + } + }); +}); + +})( jQuery );/* +* jQuery Mobile Framework : "collapsible" plugin +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ +(function( $, undefined ) { + +$.widget( "mobile.collapsible", $.mobile.widget, { + options: { + expandCueText: " click to expand contents", + collapseCueText: " click to collapse contents", + collapsed: false, + heading: ">:header,>legend", + theme: null, + iconTheme: "d", + initSelector: ":jqmData(role='collapsible')" + }, + _create: function() { + + var $el = this.element, + o = this.options, + collapsibleContain = $el.addClass( "ui-collapsible-contain" ), + collapsibleHeading = $el.find( o.heading ).eq( 0 ), + collapsibleContent = collapsibleContain.wrapInner( "
    " ).find( ".ui-collapsible-content" ), + collapsibleParent = $el.closest( ":jqmData(role='collapsible-set')" ).addClass( "ui-collapsible-set" ); + + // Replace collapsibleHeading if it's a legend + if ( collapsibleHeading.is( "legend" ) ) { + collapsibleHeading = $( "
    "+ collapsibleHeading.html() +"
    " ).insertBefore( collapsibleHeading ); + collapsibleHeading.next().remove(); + } + + collapsibleHeading + //drop heading in before content + .insertBefore( collapsibleContent ) + //modify markup & attributes + .addClass( "ui-collapsible-heading" ) + .append( "" ) + .wrapInner( "" ) + .find( "a:eq(0)" ) + .buttonMarkup({ + shadow: !collapsibleParent.length, + corners: false, + iconPos: "left", + icon: "plus", + theme: o.theme + }) + .find( ".ui-icon" ) + .removeAttr( "class" ) + .buttonMarkup({ + shadow: true, + corners: true, + iconPos: "notext", + icon: "plus", + theme: o.iconTheme + }); + + if ( !collapsibleParent.length ) { + collapsibleHeading + .find( "a:eq(0)" ) + .addClass( "ui-corner-all" ) + .find( ".ui-btn-inner" ) + .addClass( "ui-corner-all" ); + } else { + if ( collapsibleContain.jqmData( "collapsible-last" ) ) { + collapsibleHeading + .find( "a:eq(0), .ui-btn-inner" ) + .addClass( "ui-corner-bottom" ); + } + } + + //events + collapsibleContain + .bind( "collapse", function( event ) { + if ( ! event.isDefaultPrevented() && + $( event.target ).closest( ".ui-collapsible-contain" ).is( collapsibleContain ) ) { + + event.preventDefault(); + + collapsibleHeading + .addClass( "ui-collapsible-heading-collapsed" ) + .find( ".ui-collapsible-heading-status" ) + .text( o.expandCueText ) + .end() + .find( ".ui-icon" ) + .removeClass( "ui-icon-minus" ) + .addClass( "ui-icon-plus" ); + + collapsibleContent.addClass( "ui-collapsible-content-collapsed" ).attr( "aria-hidden", true ); + + if ( collapsibleContain.jqmData( "collapsible-last" ) ) { + collapsibleHeading + .find( "a:eq(0), .ui-btn-inner" ) + .addClass( "ui-corner-bottom" ); + } + } + }) + .bind( "expand", function( event ) { + if ( !event.isDefaultPrevented() ) { + + event.preventDefault(); + + collapsibleHeading + .removeClass( "ui-collapsible-heading-collapsed" ) + .find( ".ui-collapsible-heading-status" ).text( o.collapseCueText ); + + collapsibleHeading.find( ".ui-icon" ).removeClass( "ui-icon-plus" ).addClass( "ui-icon-minus" ); + + collapsibleContent.removeClass( "ui-collapsible-content-collapsed" ).attr( "aria-hidden", false ); + + if ( collapsibleContain.jqmData( "collapsible-last" ) ) { + + collapsibleHeading + .find( "a:eq(0), .ui-btn-inner" ) + .removeClass( "ui-corner-bottom" ); + } + } + }) + .trigger( o.collapsed ? "collapse" : "expand" ); + + // Close others in a set + if ( collapsibleParent.length && !collapsibleParent.jqmData( "collapsiblebound" ) ) { + + collapsibleParent + .jqmData( "collapsiblebound", true ) + .bind( "expand", function( event ) { + + $( event.target ) + .closest( ".ui-collapsible-contain" ) + .siblings( ".ui-collapsible-contain" ) + .trigger( "collapse" ); + + }); + + var set = collapsibleParent.children( ":jqmData(role='collapsible')" ); + + set.first() + .find( "a:eq(0)" ) + .addClass( "ui-corner-top" ) + .find( ".ui-btn-inner" ) + .addClass( "ui-corner-top" ); + + set.last().jqmData( "collapsible-last", true ); + } + + collapsibleHeading + .bind( "vclick", function( event ) { + + var type = collapsibleHeading.is( ".ui-collapsible-heading-collapsed" ) ? + "expand" : "collapse"; + + collapsibleContain.trigger( type ); + + event.preventDefault(); + }); + } +}); + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + $( $.mobile.collapsible.prototype.options.initSelector, e.target ).collapsible(); +}); + +})( jQuery ); +/* +* jQuery Mobile Framework : "fieldcontain" plugin - simple class additions to make form row separators +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +(function( $, undefined ) { + +$.fn.fieldcontain = function( options ) { + return this.addClass( "ui-field-contain ui-body ui-br" ); +}; + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + $( ":jqmData(role='fieldcontain')", e.target ).fieldcontain(); +}); + +})( jQuery );/* +* jQuery Mobile Framework : plugin for creating CSS grids +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +(function( $, undefined ) { + +$.fn.grid = function( options ) { + return this.each(function() { + + var $this = $( this ), + o = $.extend({ + grid: null + },options), + $kids = $this.children(), + gridCols = {solo:1, a:2, b:3, c:4, d:5}, + grid = o.grid, + iterator; + + if ( !grid ) { + if ( $kids.length <= 5 ) { + for ( var letter in gridCols ) { + if ( gridCols[ letter ] === $kids.length ) { + grid = letter; + } + } + } else { + grid = "a"; + } + } + iterator = gridCols[grid]; + + $this.addClass( "ui-grid-" + grid ); + + $kids.filter( ":nth-child(" + iterator + "n+1)" ).addClass( "ui-block-a" ); + + if ( iterator > 1 ) { + $kids.filter( ":nth-child(" + iterator + "n+2)" ).addClass( "ui-block-b" ); + } + if ( iterator > 2 ) { + $kids.filter( ":nth-child(3n+3)" ).addClass( "ui-block-c" ); + } + if ( iterator > 3 ) { + $kids.filter( ":nth-child(4n+4)" ).addClass( "ui-block-d" ); + } + if ( iterator > 4 ) { + $kids.filter( ":nth-child(5n+5)" ).addClass( "ui-block-e" ); + } + }); +}; +})( jQuery );/* +* jQuery Mobile Framework : "navbar" plugin +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +(function( $, undefined ) { + +$.widget( "mobile.navbar", $.mobile.widget, { + options: { + iconpos: "top", + grid: null, + initSelector: ":jqmData(role='navbar')" + }, + + _create: function(){ + + var $navbar = this.element, + $navbtns = $navbar.find( "a" ), + iconpos = $navbtns.filter( ":jqmData(icon)" ).length ? + this.options.iconpos : undefined; + + $navbar.addClass( "ui-navbar" ) + .attr( "role","navigation" ) + .find( "ul" ) + .grid({ grid: this.options.grid }); + + if ( !iconpos ) { + $navbar.addClass( "ui-navbar-noicons" ); + } + + $navbtns.buttonMarkup({ + corners: false, + shadow: false, + iconpos: iconpos + }); + + $navbar.delegate( "a", "vclick", function( event ) { + $navbtns.not( ".ui-state-persist" ).removeClass( $.mobile.activeBtnClass ); + $( this ).addClass( $.mobile.activeBtnClass ); + }); + } +}); + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + $( $.mobile.navbar.prototype.options.initSelector, e.target ).navbar(); +}); + +})( jQuery ); +/* +* jQuery Mobile Framework : "listview" plugin +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +(function( $, undefined ) { + +//Keeps track of the number of lists per page UID +//This allows support for multiple nested list in the same page +//https://github.com/jquery/jquery-mobile/issues/1617 +var listCountPerPage = {}; + +$.widget( "mobile.listview", $.mobile.widget, { + options: { + theme: "c", + countTheme: "c", + headerTheme: "b", + dividerTheme: "b", + splitIcon: "arrow-r", + splitTheme: "b", + inset: false, + initSelector: ":jqmData(role='listview')" + }, + + _create: function() { + var t = this; + + // create listview markup + t.element.addClass(function( i, orig ) { + return orig + " ui-listview " + ( t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "" ); + }); + + t.refresh(); + }, + + _itemApply: function( $list, item ) { + // TODO class has to be defined in markup + item.find( ".ui-li-count" ) + .addClass( "ui-btn-up-" + ( $list.jqmData( "counttheme" ) || this.options.countTheme ) + " ui-btn-corner-all" ).end() + .find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" ).end() + .find( "p, dl" ).addClass( "ui-li-desc" ).end() + .find( ">img:eq(0), .ui-link-inherit>img:eq(0)" ).addClass( "ui-li-thumb" ).each(function() { + item.addClass( $(this).is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" ); + }).end() + .find( ".ui-li-aside" ).each(function() { + var $this = $(this); + $this.prependTo( $this.parent() ); //shift aside to front for css float + }); + }, + + _removeCorners: function( li, which ) { + var top = "ui-corner-top ui-corner-tr ui-corner-tl", + bot = "ui-corner-bottom ui-corner-br ui-corner-bl"; + + li = li.add( li.find( ".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb" ) ); + + if ( which === "top" ) { + li.removeClass( top ); + } else if ( which === "bottom" ) { + li.removeClass( bot ); + } else { + li.removeClass( top + " " + bot ); + } + }, + + refresh: function( create ) { + this.parentPage = this.element.closest( ".ui-page" ); + this._createSubPages(); + + var o = this.options, + $list = this.element, + self = this, + dividertheme = $list.jqmData( "dividertheme" ) || o.dividerTheme, + listsplittheme = $list.jqmData( "splittheme" ), + listspliticon = $list.jqmData( "spliticon" ), + li = $list.children( "li" ), + counter = $.support.cssPseudoElement || !$.nodeName( $list[ 0 ], "ol" ) ? 0 : 1, + item, itemClass, itemTheme, + a, last, splittheme, countParent, icon; + + if ( counter ) { + $list.find( ".ui-li-dec" ).remove(); + } + + for ( var pos = 0, numli = li.length; pos < numli; pos++ ) { + item = li.eq( pos ); + itemClass = "ui-li"; + + // If we're creating the element, we update it regardless + if ( create || !item.hasClass( "ui-li" ) ) { + itemTheme = item.jqmData("theme") || o.theme; + a = item.children( "a" ); + + if ( a.length ) { + icon = item.jqmData("icon"); + + item.buttonMarkup({ + wrapperEls: "div", + shadow: false, + corners: false, + iconpos: "right", + icon: a.length > 1 || icon === false ? false : icon || "arrow-r", + theme: itemTheme + }); + + a.first().addClass( "ui-link-inherit" ); + + if ( a.length > 1 ) { + itemClass += " ui-li-has-alt"; + + last = a.last(); + splittheme = listsplittheme || last.jqmData( "theme" ) || o.splitTheme; + + last.appendTo(item) + .attr( "title", last.text() ) + .addClass( "ui-li-link-alt" ) + .empty() + .buttonMarkup({ + shadow: false, + corners: false, + theme: itemTheme, + icon: false, + iconpos: false + }) + .find( ".ui-btn-inner" ) + .append( + $( "" ).buttonMarkup({ + shadow: true, + corners: true, + theme: splittheme, + iconpos: "notext", + icon: listspliticon || last.jqmData( "icon" ) || o.splitIcon + }) + ); + } + } else if ( item.jqmData( "role" ) === "list-divider" ) { + + itemClass += " ui-li-divider ui-btn ui-bar-" + dividertheme; + item.attr( "role", "heading" ); + + //reset counter when a divider heading is encountered + if ( counter ) { + counter = 1; + } + + } else { + itemClass += " ui-li-static ui-body-" + itemTheme; + } + } + + if ( o.inset ) { + if ( pos === 0 ) { + itemClass += " ui-corner-top"; + + item.add( item.find( ".ui-btn-inner" ) ) + .find( ".ui-li-link-alt" ) + .addClass( "ui-corner-tr" ) + .end() + .find( ".ui-li-thumb" ) + .addClass( "ui-corner-tl" ); + + if ( item.next().next().length ) { + self._removeCorners( item.next() ); + } + } + + if ( pos === li.length - 1 ) { + itemClass += " ui-corner-bottom"; + + item.add( item.find( ".ui-btn-inner" ) ) + .find( ".ui-li-link-alt" ) + .addClass( "ui-corner-br" ) + .end() + .find( ".ui-li-thumb" ) + .addClass( "ui-corner-bl" ); + + if ( item.prev().prev().length ) { + self._removeCorners( item.prev() ); + } else if ( item.prev().length ) { + self._removeCorners( item.prev(), "bottom" ); + } + } + } + + if ( counter && itemClass.indexOf( "ui-li-divider" ) < 0 ) { + countParent = item.is( ".ui-li-static:first" ) ? item : item.find( ".ui-link-inherit" ); + + countParent.addClass( "ui-li-jsnumbering" ) + .prepend( "" + (counter++) + ". " ); + } + + item.add( item.children( ".ui-btn-inner" ) ).addClass( itemClass ); + + if ( !create ) { + self._itemApply( $list, item ); + } + } + }, + + //create a string for ID/subpage url creation + _idStringEscape: function( str ) { + return str.replace(/[^a-zA-Z0-9]/g, '-'); + }, + + _createSubPages: function() { + var parentList = this.element, + parentPage = parentList.closest( ".ui-page" ), + parentUrl = parentPage.jqmData( "url" ), + parentId = parentUrl || parentPage[ 0 ][ $.expando ], + parentListId = parentList.attr( "id" ), + o = this.options, + dns = "data-" + $.mobile.ns, + self = this, + persistentFooterID = parentPage.find( ":jqmData(role='footer')" ).jqmData( "id" ), + hasSubPages; + + if ( typeof listCountPerPage[ parentId ] === "undefined" ) { + listCountPerPage[ parentId ] = -1; + } + + parentListId = parentListId || ++listCountPerPage[ parentId ]; + + $( parentList.find( "li>ul, li>ol" ).toArray().reverse() ).each(function( i ) { + var self = this, + list = $( this ), + listId = list.attr( "id" ) || parentListId + "-" + i, + parent = list.parent(), + nodeEls = $( list.prevAll().toArray().reverse() ), + nodeEls = nodeEls.length ? nodeEls : $( "" + $.trim(parent.contents()[ 0 ].nodeValue) + "" ), + title = nodeEls.first().text(),//url limits to first 30 chars of text + id = ( parentUrl || "" ) + "&" + $.mobile.subPageUrlKey + "=" + listId, + theme = list.jqmData( "theme" ) || o.theme, + countTheme = list.jqmData( "counttheme" ) || parentList.jqmData( "counttheme" ) || o.countTheme, + newPage, anchor; + + //define hasSubPages for use in later removal + hasSubPages = true; + + newPage = list.detach() + .wrap( "
    " ) + .parent() + .before( "
    " + title + "
    " ) + .after( persistentFooterID ? $( "
    ") : "" ) + .parent() + .appendTo( $.mobile.pageContainer ); + + newPage.page(); + + anchor = parent.find('a:first'); + + if ( !anchor.length ) { + anchor = $( "" ).html( nodeEls || title ).prependTo( parent.empty() ); + } + + anchor.attr( "href", "#" + id ); + + }).listview(); + + //on pagehide, remove any nested pages along with the parent page, as long as they aren't active + if( hasSubPages && parentPage.data("page").options.domCache === false ){ + var newRemove = function( e, ui ){ + var nextPage = ui.nextPage, npURL; + + if( ui.nextPage ){ + npURL = nextPage.jqmData( "url" ); + if( npURL.indexOf( parentUrl + "&" + $.mobile.subPageUrlKey ) !== 0 ){ + self.childPages().remove(); + parentPage.remove(); + } + } + }; + + // unbind the original page remove and replace with our specialized version + parentPage + .unbind( "pagehide.remove" ) + .bind( "pagehide.remove", newRemove); + } + }, + + // TODO sort out a better way to track sub pages of the listview this is brittle + childPages: function(){ + var parentUrl = this.parentPage.jqmData( "url" ); + + return $( ":jqmData(url^='"+ parentUrl + "&" + $.mobile.subPageUrlKey +"')"); + } +}); + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + $( $.mobile.listview.prototype.options.initSelector, e.target ).listview(); +}); + +})( jQuery ); +/* +* jQuery Mobile Framework : "listview" filter extension +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +(function( $, undefined ) { + +$.mobile.listview.prototype.options.filter = false; +$.mobile.listview.prototype.options.filterPlaceholder = "Filter items..."; +$.mobile.listview.prototype.options.filterTheme = "c"; + +$( ":jqmData(role='listview')" ).live( "listviewcreate", function() { + + var list = $( this ), + listview = list.data( "listview" ); + + if ( !listview.options.filter ) { + return; + } + + var wrapper = $( "
    ", { + "class": "ui-listview-filter ui-bar-" + listview.options.filterTheme, + "role": "search" + }), + search = $( "", { + placeholder: listview.options.filterPlaceholder + }) + .attr( "data-" + $.mobile.ns + "type", "search" ) + .jqmData( "lastval", "" ) + .bind( "keyup change", function() { + + var $this = $(this), + val = this.value.toLowerCase(), + listItems = null, + lastval = $this.jqmData( "lastval" ) + "", + childItems = false, + itemtext = "", + item; + + // Change val as lastval for next execution + $this.jqmData( "lastval" , val ); + + change = val.replace( new RegExp( "^" + lastval ) , "" ); + + if ( val.length < lastval.length || change.length != ( val.length - lastval.length ) ) { + + // Removed chars or pasted something totaly different, check all items + listItems = list.children(); + } else { + + // Only chars added, not removed, only use visible subset + listItems = list.children( ":not(.ui-screen-hidden)" ); + } + + if ( val ) { + + // This handles hiding regular rows without the text we search for + // and any list dividers without regular rows shown under it + + for ( var i = listItems.length - 1; i >= 0; i-- ) { + item = $( listItems[ i ] ); + itemtext = item.jqmData( "filtertext" ) || item.text(); + + if ( item.is( "li:jqmData(role=list-divider)" ) ) { + + item.toggleClass( "ui-filter-hidequeue" , !childItems ); + + // New bucket! + childItems = false; + + } else if ( itemtext.toLowerCase().indexOf( val ) === -1 ) { + + //mark to be hidden + item.toggleClass( "ui-filter-hidequeue" , true ); + } else { + + // There"s a shown item in the bucket + childItems = true; + } + } + + // Show items, not marked to be hidden + listItems + .filter( ":not(.ui-filter-hidequeue)" ) + .toggleClass( "ui-screen-hidden", false ); + + // Hide items, marked to be hidden + listItems + .filter( ".ui-filter-hidequeue" ) + .toggleClass( "ui-screen-hidden", true ) + .toggleClass( "ui-filter-hidequeue", false ); + + } else { + + //filtervalue is empty => show all + listItems.toggleClass( "ui-screen-hidden", false ); + } + }) + .appendTo( wrapper ) + .textinput(); + + if ( $( this ).jqmData( "inset" ) ) { + wrapper.addClass( "ui-listview-filter-inset" ); + } + + wrapper.bind( "submit", function() { + return false; + }) + .insertBefore( list ); +}); + +})( jQuery );/* +* jQuery Mobile Framework : "fieldcontain" plugin - simple class additions to make form row separators +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +(function( $, undefined ) { + +$( document ).bind( "pagecreate create", function( e ){ + $( ":jqmData(role='nojs')", e.target ).addClass( "ui-nojs" ); + +}); + +})( jQuery );/* +* jQuery Mobile Framework : "checkboxradio" plugin +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +(function( $, undefined ) { + +$.widget( "mobile.checkboxradio", $.mobile.widget, { + options: { + theme: null, + initSelector: "input[type='checkbox'],input[type='radio']" + }, + _create: function() { + var self = this, + input = this.element, + // NOTE: Windows Phone could not find the label through a selector + // filter works though. + label = input.closest( "form,fieldset,:jqmData(role='page')" ).find( "label" ).filter( "[for='" + input[ 0 ].id + "']"), + inputtype = input.attr( "type" ), + checkedState = inputtype + "-on", + uncheckedState = inputtype + "-off", + icon = input.parents( ":jqmData(type='horizontal')" ).length ? undefined : uncheckedState, + activeBtn = icon ? "" : " " + $.mobile.activeBtnClass, + checkedClass = "ui-" + checkedState + activeBtn, + uncheckedClass = "ui-" + uncheckedState, + checkedicon = "ui-icon-" + checkedState, + uncheckedicon = "ui-icon-" + uncheckedState; + + if ( inputtype !== "checkbox" && inputtype !== "radio" ) { + return; + } + + // Expose for other methods + $.extend( this, { + label: label, + inputtype: inputtype, + checkedClass: checkedClass, + uncheckedClass: uncheckedClass, + checkedicon: checkedicon, + uncheckedicon: uncheckedicon + }); + + // If there's no selected theme... + if( !this.options.theme ) { + this.options.theme = this.element.jqmData( "theme" ); + } + + label.buttonMarkup({ + theme: this.options.theme, + icon: icon, + shadow: false + }); + + // Wrap the input + label in a div + input.add( label ) + .wrapAll( "
    " ); + + label.bind({ + vmouseover: function() { + if ( $( this ).parent().is( ".ui-disabled" ) ) { + return false; + } + }, + + vclick: function( event ) { + if ( input.is( ":disabled" ) ) { + event.preventDefault(); + return; + } + + self._cacheVals(); + + input.prop( "checked", inputtype === "radio" && true || !input.prop( "checked" ) ); + + // Input set for common radio buttons will contain all the radio + // buttons, but will not for checkboxes. clearing the checked status + // of other radios ensures the active button state is applied properly + self._getInputSet().not( input ).prop( "checked", false ); + + self._updateAll(); + return false; + } + + }); + + input + .bind({ + vmousedown: function() { + this._cacheVals(); + }, + + vclick: function() { + + var $this = $(this); + + // Adds checked attribute to checked input when keyboard is used + if ( $this.is( ":checked" ) ) { + + $this.prop( "checked", true); + self._getInputSet().not($this).prop( "checked", false ); + } else { + + $this.prop( "checked", false ); + } + + self._updateAll(); + }, + + focus: function() { + label.addClass( "ui-focus" ); + }, + + blur: function() { + label.removeClass( "ui-focus" ); + } + }); + + this.refresh(); + }, + + _cacheVals: function() { + this._getInputSet().each(function() { + var $this = $(this); + + $this.jqmData( "cacheVal", $this.is( ":checked" ) ); + }); + }, + + //returns either a set of radios with the same name attribute, or a single checkbox + _getInputSet: function(){ + if(this.inputtype == "checkbox") { + return this.element; + } + return this.element.closest( "form,fieldset,:jqmData(role='page')" ) + .find( "input[name='"+ this.element.attr( "name" ) +"'][type='"+ this.inputtype +"']" ); + }, + + _updateAll: function() { + var self = this; + + this._getInputSet().each(function() { + var $this = $(this); + + if ( $this.is( ":checked" ) || self.inputtype === "checkbox" ) { + $this.trigger( "change" ); + } + }) + .checkboxradio( "refresh" ); + }, + + refresh: function() { + var input = this.element, + label = this.label, + icon = label.find( ".ui-icon" ); + + // input[0].checked expando doesn't always report the proper value + // for checked='checked' + if ( $( input[ 0 ] ).prop( "checked" ) ) { + + label.addClass( this.checkedClass ).removeClass( this.uncheckedClass ); + icon.addClass( this.checkedicon ).removeClass( this.uncheckedicon ); + + } else { + + label.removeClass( this.checkedClass ).addClass( this.uncheckedClass ); + icon.removeClass( this.checkedicon ).addClass( this.uncheckedicon ); + } + + if ( input.is( ":disabled" ) ) { + this.disable(); + } else { + this.enable(); + } + }, + + disable: function() { + this.element.prop( "disabled", true ).parent().addClass( "ui-disabled" ); + }, + + enable: function() { + this.element.prop( "disabled", false ).parent().removeClass( "ui-disabled" ); + } +}); + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + $( $.mobile.checkboxradio.prototype.options.initSelector, e.target ) + .not( ":jqmData(role='none'), :jqmData(role='nojs')" ) + .checkboxradio(); +}); + +})( jQuery ); +/* +* jQuery Mobile Framework : "button" plugin - links that proxy to native input/buttons +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +(function( $, undefined ) { + +$.widget( "mobile.button", $.mobile.widget, { + options: { + theme: null, + icon: null, + iconpos: null, + inline: null, + corners: true, + shadow: true, + iconshadow: true, + initSelector: "button, [type='button'], [type='submit'], [type='reset'], [type='image']" + }, + _create: function() { + var $el = this.element, + o = this.options, + type; + + // Add ARIA role + this.button = $( "
    " ) + .text( $el.text() || $el.val() ) + .buttonMarkup({ + theme: o.theme, + icon: o.icon, + iconpos: o.iconpos, + inline: o.inline, + corners: o.corners, + shadow: o.shadow, + iconshadow: o.iconshadow + }) + .insertBefore( $el ) + .append( $el.addClass( "ui-btn-hidden" ) ); + + // Add hidden input during submit + type = $el.attr( "type" ); + + if ( type !== "button" && type !== "reset" ) { + + $el.bind( "vclick", function() { + + var $buttonPlaceholder = $( "", { + type: "hidden", + name: $el.attr( "name" ), + value: $el.attr( "value" ) + }) + .insertBefore( $el ); + + // Bind to doc to remove after submit handling + $( document ).submit(function(){ + $buttonPlaceholder.remove(); + }); + }); + } + + this.refresh(); + }, + + enable: function() { + this.element.attr( "disabled", false ); + this.button.removeClass( "ui-disabled" ).attr( "aria-disabled", false ); + return this._setOption( "disabled", false ); + }, + + disable: function() { + this.element.attr( "disabled", true ); + this.button.addClass( "ui-disabled" ).attr( "aria-disabled", true ); + return this._setOption( "disabled", true ); + }, + + refresh: function() { + if ( this.element.attr( "disabled" ) ) { + this.disable(); + } else { + this.enable(); + } + } +}); + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + $( $.mobile.button.prototype.options.initSelector, e.target ) + .not( ":jqmData(role='none'), :jqmData(role='nojs')" ) + .button(); +}); + +})( jQuery );/* +* jQuery Mobile Framework : "slider" plugin +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +( function( $, undefined ) { + +$.widget( "mobile.slider", $.mobile.widget, { + options: { + theme: null, + trackTheme: null, + disabled: false, + initSelector: "input[type='range'], :jqmData(type='range'), :jqmData(role='slider')" + }, + + _create: function() { + + // TODO: Each of these should have comments explain what they're for + var self = this, + + control = this.element, + + parentTheme = control.parents( "[class*='ui-bar-'],[class*='ui-body-']" ).eq( 0 ), + + parentTheme = parentTheme.length ? parentTheme.attr( "class" ).match( /ui-(bar|body)-([a-z])/ )[ 2 ] : "c", + + theme = this.options.theme ? this.options.theme : parentTheme, + + trackTheme = this.options.trackTheme ? this.options.trackTheme : parentTheme, + + cType = control[ 0 ].nodeName.toLowerCase(), + + selectClass = ( cType == "select" ) ? "ui-slider-switch" : "", + + controlID = control.attr( "id" ), + + labelID = controlID + "-label", + + label = $( "[for='"+ controlID +"']" ).attr( "id", labelID ), + + val = function() { + return cType == "input" ? parseFloat( control.val() ) : control[0].selectedIndex; + }, + + min = cType == "input" ? parseFloat( control.attr( "min" ) ) : 0, + + max = cType == "input" ? parseFloat( control.attr( "max" ) ) : control.find( "option" ).length-1, + + step = window.parseFloat( control.attr( "step" ) || 1 ), + + slider = $( "
    " ), + + handle = $( "
    " ) + .appendTo( slider ) + .buttonMarkup({ corners: true, theme: theme, shadow: true }) + .attr({ + "role": "slider", + "aria-valuemin": min, + "aria-valuemax": max, + "aria-valuenow": val(), + "aria-valuetext": val(), + "title": val(), + "aria-labelledby": labelID + }), + options; + + $.extend( this, { + slider: slider, + handle: handle, + dragging: false, + beforeStart: null + }); + + if ( cType == "select" ) { + + slider.wrapInner( "
    " ); + + options = control.find( "option" ); + + control.find( "option" ).each(function( i ) { + + var side = !i ? "b":"a", + corners = !i ? "right" :"left", + theme = !i ? " ui-btn-down-" + trackTheme :" ui-btn-active"; + + $( "
    " ) + .prependTo( slider ); + + $( "" + $( this ).text() + "" ) + .prependTo( handle ); + }); + + } + + label.addClass( "ui-slider" ); + + // monitor the input for updated values + control.addClass( cType === "input" ? "ui-slider-input" : "ui-slider-switch" ) + .change( function() { + self.refresh( val(), true ); + }) + .keyup( function() { // necessary? + self.refresh( val(), true, true ); + }) + .blur( function() { + self.refresh( val(), true ); + }); + + // prevent screen drag when slider activated + $( document ).bind( "vmousemove", function( event ) { + if ( self.dragging ) { + self.refresh( event ); + return false; + } + }); + + slider.bind( "vmousedown", function( event ) { + self.dragging = true; + + if ( cType === "select" ) { + self.beforeStart = control[0].selectedIndex; + } + self.refresh( event ); + return false; + }); + + slider.add( document ) + .bind( "vmouseup", function() { + if ( self.dragging ) { + + self.dragging = false; + + if ( cType === "select" ) { + + if ( self.beforeStart === control[ 0 ].selectedIndex ) { + //tap occurred, but value didn't change. flip it! + self.refresh( !self.beforeStart ? 1 : 0 ); + } + var curval = val(); + var snapped = Math.round( curval / ( max - min ) * 100 ); + handle + .addClass( "ui-slider-handle-snapping" ) + .css( "left", snapped + "%" ) + .animationComplete( function() { + handle.removeClass( "ui-slider-handle-snapping" ); + }); + } + return false; + } + }); + + slider.insertAfter( control ); + + // NOTE force focus on handle + this.handle + .bind( "vmousedown", function() { + $( this ).focus(); + }) + .bind( "vclick", false ); + + this.handle + .bind( "keydown", function( event ) { + var index = val(); + + if ( self.options.disabled ) { + return; + } + + // In all cases prevent the default and mark the handle as active + switch ( event.keyCode ) { + case $.mobile.keyCode.HOME: + case $.mobile.keyCode.END: + case $.mobile.keyCode.PAGE_UP: + case $.mobile.keyCode.PAGE_DOWN: + case $.mobile.keyCode.UP: + case $.mobile.keyCode.RIGHT: + case $.mobile.keyCode.DOWN: + case $.mobile.keyCode.LEFT: + event.preventDefault(); + + if ( !self._keySliding ) { + self._keySliding = true; + $( this ).addClass( "ui-state-active" ); + } + break; + } + + // move the slider according to the keypress + switch ( event.keyCode ) { + case $.mobile.keyCode.HOME: + self.refresh( min ); + break; + case $.mobile.keyCode.END: + self.refresh( max ); + break; + case $.mobile.keyCode.PAGE_UP: + case $.mobile.keyCode.UP: + case $.mobile.keyCode.RIGHT: + self.refresh( index + step ); + break; + case $.mobile.keyCode.PAGE_DOWN: + case $.mobile.keyCode.DOWN: + case $.mobile.keyCode.LEFT: + self.refresh( index - step ); + break; + } + }) // remove active mark + .keyup( function( event ) { + if ( self._keySliding ) { + self._keySliding = false; + $( this ).removeClass( "ui-state-active" ); + } + }); + + this.refresh(undefined, undefined, true); + }, + + refresh: function( val, isfromControl, preventInputUpdate ) { + if ( this.options.disabled ) { return; } + + var control = this.element, percent, + cType = control[0].nodeName.toLowerCase(), + min = cType === "input" ? parseFloat( control.attr( "min" ) ) : 0, + max = cType === "input" ? parseFloat( control.attr( "max" ) ) : control.find( "option" ).length - 1; + + if ( typeof val === "object" ) { + var data = val, + // a slight tolerance helped get to the ends of the slider + tol = 8; + if ( !this.dragging || + data.pageX < this.slider.offset().left - tol || + data.pageX > this.slider.offset().left + this.slider.width() + tol ) { + return; + } + percent = Math.round( ( ( data.pageX - this.slider.offset().left ) / this.slider.width() ) * 100 ); + } else { + if ( val == null ) { + val = cType === "input" ? parseFloat( control.val() ) : control[0].selectedIndex; + } + percent = ( parseFloat( val ) - min ) / ( max - min ) * 100; + } + + if ( isNaN( percent ) ) { + return; + } + + if ( percent < 0 ) { + percent = 0; + } + + if ( percent > 100 ) { + percent = 100; + } + + var newval = Math.round( ( percent / 100 ) * ( max - min ) ) + min; + + if ( newval < min ) { + newval = min; + } + + if ( newval > max ) { + newval = max; + } + + // Flip the stack of the bg colors + if ( percent > 60 && cType === "select" ) { + // TODO: Dead path? + } + this.handle.css( "left", percent + "%" ); + this.handle.attr( { + "aria-valuenow": cType === "input" ? newval : control.find( "option" ).eq( newval ).attr( "value" ), + "aria-valuetext": cType === "input" ? newval : control.find( "option" ).eq( newval ).text(), + title: newval + }); + + // add/remove classes for flip toggle switch + if ( cType === "select" ) { + if ( newval === 0 ) { + this.slider.addClass( "ui-slider-switch-a" ) + .removeClass( "ui-slider-switch-b" ); + } else { + this.slider.addClass( "ui-slider-switch-b" ) + .removeClass( "ui-slider-switch-a" ); + } + } + + if ( !preventInputUpdate ) { + // update control"s value + if ( cType === "input" ) { + control.val( newval ); + } else { + control[ 0 ].selectedIndex = newval; + } + if ( !isfromControl ) { + control.trigger( "change" ); + } + } + }, + + enable: function() { + this.element.attr( "disabled", false ); + this.slider.removeClass( "ui-disabled" ).attr( "aria-disabled", false ); + return this._setOption( "disabled", false ); + }, + + disable: function() { + this.element.attr( "disabled", true ); + this.slider.addClass( "ui-disabled" ).attr( "aria-disabled", true ); + return this._setOption( "disabled", true ); + } + +}); + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + + $( $.mobile.slider.prototype.options.initSelector, e.target ) + .not( ":jqmData(role='none'), :jqmData(role='nojs')" ) + .slider(); + +}); + +})( jQuery );/* +* jQuery Mobile Framework : "textinput" plugin for text inputs, textareas +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +(function( $, undefined ) { + +$.widget( "mobile.textinput", $.mobile.widget, { + options: { + theme: null, + initSelector: "input[type='text'], input[type='search'], :jqmData(type='search'), input[type='number'], :jqmData(type='number'), input[type='password'], input[type='email'], input[type='url'], input[type='tel'], textarea" + }, + + _create: function() { + + var input = this.element, + o = this.options, + theme = o.theme, + themedParent, themeclass, themeLetter, focusedEl, clearbtn; + + if ( !theme ) { + themedParent = this.element.closest( "[class*='ui-bar-'],[class*='ui-body-']" ); + themeLetter = themedParent.length && /ui-(bar|body)-([a-z])/.exec( themedParent.attr( "class" ) ); + theme = themeLetter && themeLetter[2] || "c"; + } + + themeclass = " ui-body-" + theme; + + $( "label[for='" + input.attr( "id" ) + "']" ).addClass( "ui-input-text" ); + + input.addClass("ui-input-text ui-body-"+ o.theme ); + + focusedEl = input; + + // XXX: Temporary workaround for issue 785. Turn off autocorrect and + // autocomplete since the popup they use can't be dismissed by + // the user. Note that we test for the presence of the feature + // by looking for the autocorrect property on the input element. + if ( typeof input[0].autocorrect !== "undefined" ) { + // Set the attribute instead of the property just in case there + // is code that attempts to make modifications via HTML. + input[0].setAttribute( "autocorrect", "off" ); + input[0].setAttribute( "autocomplete", "off" ); + } + + + //"search" input widget + if ( input.is( "[type='search'],:jqmData(type='search')" ) ) { + + focusedEl = input.wrap( "" ).parent(); + clearbtn = $( "clear text" ) + .tap(function( event ) { + input.val( "" ).focus(); + input.trigger( "change" ); + clearbtn.addClass( "ui-input-clear-hidden" ); + event.preventDefault(); + }) + .appendTo( focusedEl ) + .buttonMarkup({ + icon: "delete", + iconpos: "notext", + corners: true, + shadow: true + }); + + function toggleClear() { + if ( !input.val() ) { + clearbtn.addClass( "ui-input-clear-hidden" ); + } else { + clearbtn.removeClass( "ui-input-clear-hidden" ); + } + } + + toggleClear(); + + input.keyup( toggleClear ) + .focus( toggleClear ); + + } else { + input.addClass( "ui-corner-all ui-shadow-inset" + themeclass ); + } + + input.focus(function() { + focusedEl.addClass( "ui-focus" ); + }) + .blur(function(){ + focusedEl.removeClass( "ui-focus" ); + }); + + // Autogrow + if ( input.is( "textarea" ) ) { + var extraLineHeight = 15, + keyupTimeoutBuffer = 100, + keyup = function() { + var scrollHeight = input[ 0 ].scrollHeight, + clientHeight = input[ 0 ].clientHeight; + + if ( clientHeight < scrollHeight ) { + input.css({ + height: (scrollHeight + extraLineHeight) + }); + } + }, + keyupTimeout; + + input.keyup(function() { + clearTimeout( keyupTimeout ); + keyupTimeout = setTimeout( keyup, keyupTimeoutBuffer ); + }); + } + }, + + disable: function(){ + ( this.element.attr( "disabled", true ).is( "[type='search'],:jqmData(type='search')" ) ? + this.element.parent() : this.element ).addClass( "ui-disabled" ); + }, + + enable: function(){ + ( this.element.attr( "disabled", false).is( "[type='search'],:jqmData(type='search')" ) ? + this.element.parent() : this.element ).removeClass( "ui-disabled" ); + } +}); + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + + $( $.mobile.textinput.prototype.options.initSelector, e.target ) + .not( ":jqmData(role='none'), :jqmData(role='nojs')" ) + .textinput(); + +}); + +})( jQuery ); +/* +* jQuery Mobile Framework : "selectmenu" plugin +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +(function( $, undefined ) { + +$.widget( "mobile.selectmenu", $.mobile.widget, { + options: { + theme: null, + disabled: false, + icon: "arrow-d", + iconpos: "right", + inline: null, + corners: true, + shadow: true, + iconshadow: true, + menuPageTheme: "b", + overlayTheme: "a", + hidePlaceholderMenuItems: true, + closeText: "Close", + nativeMenu: true, + initSelector: "select:not(:jqmData(role='slider'))" + }, + _create: function() { + + var self = this, + + o = this.options, + + select = this.element + .wrap( "
    " ), + + selectID = select.attr( "id" ), + + label = $( "label[for='"+ selectID +"']" ).addClass( "ui-select" ), + + // IE throws an exception at options.item() function when + // there is no selected item + // select first in this case + selectedIndex = select[ 0 ].selectedIndex == -1 ? 0 : select[ 0 ].selectedIndex, + + button = ( self.options.nativeMenu ? $( "
    " ) : $( "", { + "href": "#", + "role": "button", + "id": buttonId, + "aria-haspopup": "true", + "aria-owns": menuId + }) ) + .text( $( select[ 0 ].options.item( selectedIndex ) ).text() ) + .insertBefore( select ) + .buttonMarkup({ + theme: o.theme, + icon: o.icon, + iconpos: o.iconpos, + inline: o.inline, + corners: o.corners, + shadow: o.shadow, + iconshadow: o.iconshadow + }), + + // Multi select or not + isMultiple = self.isMultiple = select[ 0 ].multiple; + + // Opera does not properly support opacity on select elements + // In Mini, it hides the element, but not its text + // On the desktop,it seems to do the opposite + // for these reasons, using the nativeMenu option results in a full native select in Opera + if ( o.nativeMenu && window.opera && window.opera.version ) { + select.addClass( "ui-select-nativeonly" ); + } + + //vars for non-native menus + if ( !o.nativeMenu ) { + var options = select.find("option"), + + buttonId = selectID + "-button", + + menuId = selectID + "-menu", + + thisPage = select.closest( ".ui-page" ), + + //button theme + theme = /ui-btn-up-([a-z])/.exec( button.attr( "class" ) )[1], + + menuPage = $( "
    " + + "
    " + + "
    " + label.text() + "
    "+ + "
    "+ + "
    "+ + "
    " ) + .appendTo( $.mobile.pageContainer ) + .page(), + + menuPageContent = menuPage.find( ".ui-content" ), + + menuPageClose = menuPage.find( ".ui-header a" ), + + screen = $( "
    ", {"class": "ui-selectmenu-screen ui-screen-hidden"}) + .appendTo( thisPage ), + + listbox = $("
    ", { "class": "ui-selectmenu ui-selectmenu-hidden ui-overlay-shadow ui-corner-all ui-body-" + o.overlayTheme + " " + $.mobile.defaultDialogTransition }) + .insertAfter(screen), + + list = $( "