Repository: AreYouFreeBusy/AlexaSkillsKit.NET Branch: master Commit: fd5c2bc771ec Files: 243 Total size: 1.3 MB Directory structure: gitextract_syntntwh/ ├── .gitignore ├── .nuget/ │ ├── AlexaSkillsKit.Lib.nuspec │ ├── NuGet.Config │ └── pack.cmd ├── AlexaSkillsKit.Lib/ │ ├── AlexaSkillsKit.Lib.csproj │ ├── AsyncHelpers.cs │ ├── Authentication/ │ │ ├── SpeechletRequestSignatureVerifier.cs │ │ ├── SpeechletRequestTimestampVerifier.cs │ │ └── SpeechletRequestValidationResult.cs │ ├── Helpers/ │ │ └── DateTimeHelpers.cs │ ├── Interfaces/ │ │ ├── AudioPlayer/ │ │ │ ├── AudioItem.cs │ │ │ ├── AudioItemStream.cs │ │ │ ├── AudioPlayerInterface.cs │ │ │ ├── AudioPlayerPlaybackFailedRequest.cs │ │ │ ├── AudioPlayerRequest.cs │ │ │ ├── AudioPlayerResponse.cs │ │ │ ├── AudioPlayerState.cs │ │ │ ├── Directives/ │ │ │ │ ├── AudioPlayerClearQueueDirective.cs │ │ │ │ ├── AudioPlayerDirective.cs │ │ │ │ ├── AudioPlayerPlayDirective.cs │ │ │ │ └── AudioPlayerStopDirective.cs │ │ │ ├── IAudioPlayerSpeechlet.cs │ │ │ ├── IAudioPlayerSpeechletAsync.cs │ │ │ └── PlaybackControllerRequest.cs │ │ ├── Dialog/ │ │ │ └── Directives/ │ │ │ ├── DialogConfirmIntentDirective.cs │ │ │ ├── DialogConfirmSlotDirective.cs │ │ │ ├── DialogDelegateDirective.cs │ │ │ ├── DialogDirective.cs │ │ │ └── DialogElicitSlotDirective.cs │ │ ├── Display/ │ │ │ ├── Directives/ │ │ │ │ ├── DisplayRenderTemplateDirective.cs │ │ │ │ └── HintDirective.cs │ │ │ ├── DisplayImage.cs │ │ │ ├── DisplayImageSource.cs │ │ │ ├── DisplayInterface.cs │ │ │ ├── DisplayRequest.cs │ │ │ ├── DisplayTemplate.cs │ │ │ ├── IDisplaySpeechlet.cs │ │ │ ├── IDisplaySpeechletAsync.cs │ │ │ ├── ListItem.cs │ │ │ ├── TextContent.cs │ │ │ └── TextField.cs │ │ └── VideoApp/ │ │ ├── Directives/ │ │ │ └── VideoAppLaunchDirective.cs │ │ ├── VideoItem.cs │ │ └── VideoItemMetadata.cs │ ├── Json/ │ │ ├── CamelCasePropertyNamesExceptDictionaryKeysContractResolver.cs │ │ ├── Deserializer.cs │ │ ├── SpeechletRequestEnvelope.cs │ │ ├── SpeechletRequestParser.cs │ │ ├── SpeechletRequestParserExtensions.cs │ │ └── SpeechletResponseEnvelope.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── Sdk.cs │ ├── Slu/ │ │ ├── ConfirmationStatusEnum.cs │ │ ├── Intent.cs │ │ ├── Resolutions.cs │ │ ├── ResolutionsPerAuthority.cs │ │ ├── ResolutionsPerAuthorityStatus.cs │ │ ├── ResolutionsPerAuthorityValue.cs │ │ ├── ResolutionsPerAuthorityValueValue.cs │ │ └── Slot.cs │ ├── Speechlet/ │ │ ├── Application.cs │ │ ├── Cause.cs │ │ ├── Context.cs │ │ ├── Device.cs │ │ ├── Directive.cs │ │ ├── Error.cs │ │ ├── ExtendedSpeechletRequest.cs │ │ ├── ISpeechlet.cs │ │ ├── ISpeechletAsync.cs │ │ ├── ISpeechletBase.cs │ │ ├── ISpeechletInterface.cs │ │ ├── ISpeechletInterfaceState.cs │ │ ├── ISpeechletResponse.cs │ │ ├── ISpeechletWithContext.cs │ │ ├── ISpeechletWithContextAsync.cs │ │ ├── IntentRequest.cs │ │ ├── LaunchRequest.cs │ │ ├── Person.cs │ │ ├── PlaybackState.cs │ │ ├── Session.cs │ │ ├── SessionEndedRequest.cs │ │ ├── SessionStartedRequest.cs │ │ ├── Speechlet.cs │ │ ├── SpeechletAsync.cs │ │ ├── SpeechletBase.cs │ │ ├── SpeechletException.cs │ │ ├── SpeechletRequest.cs │ │ ├── SpeechletResponse.cs │ │ ├── SpeechletService.cs │ │ ├── SpeechletValidationException.cs │ │ ├── SupportedInterfaces.cs │ │ ├── SystemExceptionEncounteredRequest.cs │ │ ├── SystemRequest.cs │ │ ├── SystemState.cs │ │ └── User.cs │ ├── UI/ │ │ ├── AskForPermissionsConsentCard.cs │ │ ├── Card.cs │ │ ├── Image.cs │ │ ├── LinkAccountCard.cs │ │ ├── OutputSpeech.cs │ │ ├── PlainTextOutputSpeech.cs │ │ ├── Reprompt.cs │ │ ├── SimpleCard.cs │ │ ├── SsmlOutputSpeech.cs │ │ └── StandardCard.cs │ ├── app.config │ └── packages.config ├── AlexaSkillsKit.Sample/ │ ├── AlexaSkillsKit.Sample.csproj │ ├── App_Start/ │ │ ├── BundleConfig.cs │ │ ├── FilterConfig.cs │ │ ├── IdentityConfig.cs │ │ ├── RouteConfig.cs │ │ ├── Startup.Auth.cs │ │ └── WebApiConfig.cs │ ├── Areas/ │ │ └── HelpPage/ │ │ ├── ApiDescriptionExtensions.cs │ │ ├── App_Start/ │ │ │ └── HelpPageConfig.cs │ │ ├── Controllers/ │ │ │ └── HelpController.cs │ │ ├── HelpPage.css │ │ ├── HelpPageAreaRegistration.cs │ │ ├── HelpPageConfigurationExtensions.cs │ │ ├── ModelDescriptions/ │ │ │ ├── CollectionModelDescription.cs │ │ │ ├── ComplexTypeModelDescription.cs │ │ │ ├── DictionaryModelDescription.cs │ │ │ ├── EnumTypeModelDescription.cs │ │ │ ├── EnumValueDescription.cs │ │ │ ├── IModelDocumentationProvider.cs │ │ │ ├── KeyValuePairModelDescription.cs │ │ │ ├── ModelDescription.cs │ │ │ ├── ModelDescriptionGenerator.cs │ │ │ ├── ModelNameAttribute.cs │ │ │ ├── ModelNameHelper.cs │ │ │ ├── ParameterAnnotation.cs │ │ │ ├── ParameterDescription.cs │ │ │ └── SimpleTypeModelDescription.cs │ │ ├── Models/ │ │ │ └── HelpPageApiModel.cs │ │ ├── SampleGeneration/ │ │ │ ├── HelpPageSampleGenerator.cs │ │ │ ├── HelpPageSampleKey.cs │ │ │ ├── ImageSample.cs │ │ │ ├── InvalidSample.cs │ │ │ ├── ObjectGenerator.cs │ │ │ ├── SampleDirection.cs │ │ │ └── TextSample.cs │ │ ├── Views/ │ │ │ ├── Help/ │ │ │ │ ├── Api.cshtml │ │ │ │ ├── DisplayTemplates/ │ │ │ │ │ ├── ApiGroup.cshtml │ │ │ │ │ ├── CollectionModelDescription.cshtml │ │ │ │ │ ├── ComplexTypeModelDescription.cshtml │ │ │ │ │ ├── DictionaryModelDescription.cshtml │ │ │ │ │ ├── EnumTypeModelDescription.cshtml │ │ │ │ │ ├── HelpPageApiModel.cshtml │ │ │ │ │ ├── ImageSample.cshtml │ │ │ │ │ ├── InvalidSample.cshtml │ │ │ │ │ ├── KeyValuePairModelDescription.cshtml │ │ │ │ │ ├── ModelDescriptionLink.cshtml │ │ │ │ │ ├── Parameters.cshtml │ │ │ │ │ ├── Samples.cshtml │ │ │ │ │ ├── SimpleTypeModelDescription.cshtml │ │ │ │ │ └── TextSample.cshtml │ │ │ │ ├── Index.cshtml │ │ │ │ └── ResourceModel.cshtml │ │ │ ├── Shared/ │ │ │ │ └── _Layout.cshtml │ │ │ ├── Web.config │ │ │ └── _ViewStart.cshtml │ │ └── XmlDocumentationProvider.cs │ ├── Content/ │ │ ├── Site.css │ │ └── bootstrap.css │ ├── Controllers/ │ │ ├── AccountController.cs │ │ ├── HomeController.cs │ │ └── ValuesController.cs │ ├── Global.asax │ ├── Global.asax.cs │ ├── Models/ │ │ ├── AccountBindingModels.cs │ │ ├── AccountViewModels.cs │ │ └── IdentityModels.cs │ ├── NLog.config │ ├── NLog.xsd │ ├── Project_Readme.html │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── Providers/ │ │ └── ApplicationOAuthProvider.cs │ ├── Results/ │ │ └── ChallengeResult.cs │ ├── Scripts/ │ │ ├── _references.js │ │ ├── bootstrap.js │ │ ├── jquery-1.10.2.intellisense.js │ │ ├── jquery-1.10.2.js │ │ ├── jquery.validate-vsdoc.js │ │ ├── jquery.validate.js │ │ ├── jquery.validate.unobtrusive.js │ │ ├── modernizr-2.6.2.js │ │ └── respond.js │ ├── Speechlet/ │ │ ├── AlexaController.cs │ │ ├── SampleSessionIntentSchema.json │ │ ├── SampleSessionSpeechlet.cs │ │ └── SampleSessionUtterances.txt │ ├── Startup.cs │ ├── Views/ │ │ ├── Home/ │ │ │ └── Index.cshtml │ │ ├── Shared/ │ │ │ ├── Error.cshtml │ │ │ └── _Layout.cshtml │ │ ├── Web.config │ │ └── _ViewStart.cshtml │ ├── Web.Debug.config │ ├── Web.Release.config │ ├── Web.config │ └── packages.config ├── AlexaSkillsKit.Sample.Dialog.AzureFunc/ │ ├── .gitignore │ ├── AlexaHttpTrigger.cs │ ├── AlexaSkillsKit.Sample.Dialog.AzureFunc.csproj │ ├── Handlers/ │ │ ├── DefaultHandler.cs │ │ ├── IIntentHandler.cs │ │ ├── IntentHandler.cs │ │ ├── SampleIntent1Handler.cs │ │ ├── SampleIntent2Handler.cs │ │ └── SampleIntent3Handler.cs │ ├── Helpers/ │ │ ├── Builder/ │ │ │ ├── ISpeechletResponseBuilder.cs │ │ │ ├── SpeechletResponseBuilder.cs │ │ │ ├── SpeechletResponseBuilderCardExtentions.cs │ │ │ └── SpeechletResponseBuilderDialogExtentions.cs │ │ ├── DialogHelper.cs │ │ ├── IntentNames.cs │ │ └── SlotNames.cs │ ├── Properties/ │ │ └── launchSettings.json │ ├── SampleSkill.cs │ ├── SampleSkillFactory.cs │ └── host.json ├── AlexaSkillsKit.Sample.Dialog.AzureFunc.FunctionalTests/ │ ├── AlexaSkillsKit.Sample.Dialog.AzureFunc.FunctionalTests.csproj │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── SampleIntent1HandlerTests.cs │ ├── SampleIntent2HandlerTests.cs │ ├── SampleIntent3HandlerTests.cs │ ├── TestsBase.cs │ ├── app.config │ └── packages.config ├── AlexaSkillsKit.Sample.Dialog.AzureFunc.sln ├── AlexaSkillsKit.Tests/ │ ├── AlexaSkillsKit.Tests.csproj │ ├── Authentication/ │ │ └── SignatureVerifierTests.cs │ ├── Helpers/ │ │ └── DateTimeHelpersTests.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── TestData/ │ │ ├── RequestWithInvalidTimestamp.json │ │ ├── RequestWithIso8601Timestamp.json │ │ └── RequestWithUnixTimeTimestamp.json │ ├── app.config │ └── packages.config ├── AlexaSkillsKit.sln ├── LICENSE └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ build/ bld/ [Bb]in/ [Oo]bj/ # Visual Studo 2015 cache/options directory .vs/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opensdf *.sdf *.cachefile # Visual Studio profiler *.psess *.vsp *.vspx # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding addin-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch _NCrunch_* .*crunch*.local.xml # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # NuGet Packages *.nupkg .nuget/nuget.exe # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # Windows Azure Build Output csx/ *.build.csdef # Windows Store app package directory AppPackages/ # Others *.[Cc]ache ClientBin/ [Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.dbproj.schemaview *.pfx *.publishsettings node_modules/ bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # Node.js Tools for Visual Studio .ntvs_analysis.dat # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt ================================================ FILE: .nuget/AlexaSkillsKit.Lib.nuspec ================================================ AlexaSkillsKit.NET 1.6.0 AlexaSkillsKit.NET Stefan Negritoiu (FreeBusy) and contributors Stefan Negritoiu (FreeBusy) https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET http://opensource.org/licenses/MIT https://developer.amazon.com/public/binaries/content/gallery/developerportalpublic/solutions/alexa/dp_image_kit_02.png false .NET library that simplifies Alexa skills development; same object model as Amazon's AlexaSkillsKit for Java 1.4.0: Ability to override request validation policy and support for SSML output speech type 1.5.0: Fully implement certificate verification requirement and support for Standard cards 1.6.0: Implement support for Context object (Device Location, Audio Player, etc.), PlaybackController, Display, Dialog directives, and VideoApp. See project README for new interfaces. Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors amazon echo alexa speechlet ================================================ FILE: .nuget/NuGet.Config ================================================  ================================================ FILE: .nuget/pack.cmd ================================================ msbuild /property:Configuration=Release ..\AlexaSkillsKit.Lib\AlexaSkillsKit.Lib.csproj nuget pack AlexaSkillsKit.Lib.nuspec ================================================ FILE: AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj ================================================  Debug AnyCPU {0EC882A8-AACA-4BD5-B449-72F20FDB8586} Library Properties AlexaSkillsKit AlexaSkillsKit v4.5 512 ..\ true true full false bin\Debug\ DEBUG;TRACE prompt 4 pdbonly true bin\Release\ TRACE prompt 4 ..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll True ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll True ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll True ================================================ FILE: AlexaSkillsKit.Lib/AsyncHelpers.cs ================================================ // As discussed at http://stackoverflow.com/a/5097066 using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace AlexaSkillsKit { public static class AsyncHelpers { /// /// Execute's an async Task method which has a void return value synchronously /// /// Task method to execute public static void RunSync(Func task) { var oldContext = SynchronizationContext.Current; var synch = new ExclusiveSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(synch); synch.Post(async _ => { try { await task(); } catch (Exception e) { synch.InnerException = e; throw; } finally { synch.EndMessageLoop(); } }, null); synch.BeginMessageLoop(); SynchronizationContext.SetSynchronizationContext(oldContext); } /// /// Execute's an async Task method which has a T return type synchronously /// /// Return Type /// Task method to execute /// public static T RunSync(Func> task) { var oldContext = SynchronizationContext.Current; var synch = new ExclusiveSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(synch); T ret = default(T); synch.Post(async _ => { try { ret = await task(); } catch (Exception e) { synch.InnerException = e; throw; } finally { synch.EndMessageLoop(); } }, null); synch.BeginMessageLoop(); SynchronizationContext.SetSynchronizationContext(oldContext); return ret; } private class ExclusiveSynchronizationContext : SynchronizationContext { private bool done; public Exception InnerException { get; set; } readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false); readonly Queue> items = new Queue>(); public override void Send(SendOrPostCallback d, object state) { throw new NotSupportedException("We cannot send to our same thread"); } public override void Post(SendOrPostCallback d, object state) { lock (items) { items.Enqueue(Tuple.Create(d, state)); } workItemsWaiting.Set(); } public void EndMessageLoop() { Post(_ => done = true, null); } public void BeginMessageLoop() { while (!done) { Tuple task = null; lock (items) { if (items.Count > 0) { task = items.Dequeue(); } } if (task != null) { task.Item1(task.Item2); if (InnerException != null) // the method threw an exeption { throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException); } } else { workItemsWaiting.WaitOne(); } } } public override SynchronizationContext CreateCopy() { return this; } } } } ================================================ FILE: AlexaSkillsKit.Lib/Authentication/SpeechletRequestSignatureVerifier.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Runtime.Caching; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Org.BouncyCastle.X509; using Org.BouncyCastle.Security.Certificates; namespace AlexaSkillsKit.Authentication { public class SpeechletRequestSignatureVerifier { private static Func _getCertCacheKey = (string url) => string.Format("{0}_{1}", Sdk.SIGNATURE_CERT_URL_REQUEST_HEADER, url); private static CacheItemPolicy _policy = new CacheItemPolicy { Priority = CacheItemPriority.Default, AbsoluteExpiration = DateTimeOffset.UtcNow.AddHours(24) }; /// /// Verifying the Signature Certificate URL per requirements documented at /// https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/developing-an-alexa-skill-as-a-web-service /// public static bool VerifyCertificateUrl(string certChainUrl) { if (String.IsNullOrEmpty(certChainUrl)) { return false; } Uri certChainUri; if (!Uri.TryCreate(certChainUrl, UriKind.Absolute, out certChainUri)) { return false; } return certChainUri.Host.Equals(Sdk.SIGNATURE_CERT_URL_HOST, StringComparison.OrdinalIgnoreCase) && certChainUri.PathAndQuery.StartsWith(Sdk.SIGNATURE_CERT_URL_PATH) && certChainUri.Scheme == Uri.UriSchemeHttps && certChainUri.Port == 443; } /// /// Verifies request signature and manages the caching of the signature certificate /// public static bool VerifyRequestSignature( byte[] serializedSpeechletRequest, string expectedSignature, string certChainUrl) { string certCacheKey = _getCertCacheKey(certChainUrl); X509Certificate cert = MemoryCache.Default.Get(certCacheKey) as X509Certificate; if (cert == null || !CheckRequestSignature(serializedSpeechletRequest, expectedSignature, cert)) { // download the cert // if we don't have it in cache or // if we have it but it's stale because the current request was signed with a newer cert // (signaled by signature check fail with cached cert) cert = RetrieveAndVerifyCertificate(certChainUrl); if (cert == null) return false; MemoryCache.Default.Set(certCacheKey, cert, _policy); } return CheckRequestSignature(serializedSpeechletRequest, expectedSignature, cert); } /// /// Verifies request signature and manages the caching of the signature certificate /// public async static Task VerifyRequestSignatureAsync( byte[] serializedSpeechletRequest, string expectedSignature, string certChainUrl) { string certCacheKey = _getCertCacheKey(certChainUrl); X509Certificate cert = MemoryCache.Default.Get(certCacheKey) as X509Certificate; if (cert == null || !CheckRequestSignature(serializedSpeechletRequest, expectedSignature, cert)) { // download the cert // if we don't have it in cache or // if we have it but it's stale because the current request was signed with a newer cert // (signaled by signature check fail with cached cert) cert = await RetrieveAndVerifyCertificateAsync(certChainUrl); if (cert == null) return false; MemoryCache.Default.Set(certCacheKey, cert, _policy); } return CheckRequestSignature(serializedSpeechletRequest, expectedSignature, cert); } /// /// /// public static X509Certificate RetrieveAndVerifyCertificate(string certChainUrl) { // making requests to externally-supplied URLs is an open invitation to DoS // so restrict host to an Alexa controlled subdomain/path if (!VerifyCertificateUrl(certChainUrl)) return null; var webClient = new WebClient(); var content = webClient.DownloadString(certChainUrl); var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(new StringReader(content)); var cert = (X509Certificate)pemReader.ReadObject(); try { cert.CheckValidity(); if (!CheckCertSubjectNames(cert)) return null; } catch (CertificateExpiredException) { return null; } catch (CertificateNotYetValidException) { return null; } return cert; } /// /// /// public async static Task RetrieveAndVerifyCertificateAsync(string certChainUrl) { // making requests to externally-supplied URLs is an open invitation to DoS // so restrict host to an Alexa controlled subdomain/path if (!VerifyCertificateUrl(certChainUrl)) return null; var httpClient = new HttpClient(); var httpResponse = await httpClient.GetAsync(certChainUrl); var content = await httpResponse.Content.ReadAsStringAsync(); if (String.IsNullOrEmpty(content)) return null; var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(new StringReader(content)); var cert = (X509Certificate)pemReader.ReadObject(); try { cert.CheckValidity(); if (!CheckCertSubjectNames(cert)) return null; } catch (CertificateExpiredException) { return null; } catch (CertificateNotYetValidException) { return null; } return cert; } /// /// /// public static bool CheckRequestSignature( byte[] serializedSpeechletRequest, string expectedSignature, Org.BouncyCastle.X509.X509Certificate cert) { byte[] expectedSig = null; try { expectedSig = Convert.FromBase64String(expectedSignature); } catch (FormatException) { return false; } var publicKey = (Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters)cert.GetPublicKey(); var signer = Org.BouncyCastle.Security.SignerUtilities.GetSigner(Sdk.SIGNATURE_ALGORITHM); signer.Init(false, publicKey); signer.BlockUpdate(serializedSpeechletRequest, 0, serializedSpeechletRequest.Length); return signer.VerifySignature(expectedSig); } /// /// /// private static bool CheckCertSubjectNames(X509Certificate cert) { bool found = false; ArrayList subjectNamesList = (ArrayList)cert.GetSubjectAlternativeNames(); for (int i=0; i < subjectNamesList.Count; i++) { ArrayList subjectNames = (ArrayList)subjectNamesList[i]; for (int j = 0; j < subjectNames.Count; j++) { if (subjectNames[j] is String && subjectNames[j].Equals(Sdk.ECHO_API_DOMAIN_NAME)) { found = true; break; } } } return found; } } } ================================================ FILE: AlexaSkillsKit.Lib/Authentication/SpeechletRequestTimestampVerifier.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; using System.Diagnostics; using AlexaSkillsKit.Json; namespace AlexaSkillsKit.Authentication { public class SpeechletRequestTimestampVerifier { /// /// Verifies request timestamp /// public static bool VerifyRequestTimestamp(SpeechletRequestEnvelope requestEnvelope, DateTime referenceTimeUtc) { // verify timestamp is within tolerance var diff = referenceTimeUtc - requestEnvelope.Request.Timestamp; Debug.WriteLine("Request was timestamped {0:0.00} seconds ago.", diff.TotalSeconds); return (Math.Abs((decimal)diff.TotalSeconds) <= Sdk.TIMESTAMP_TOLERANCE_SEC); } } } ================================================ FILE: AlexaSkillsKit.Lib/Authentication/SpeechletRequestValidationResult.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; namespace AlexaSkillsKit.Authentication { [Flags] public enum SpeechletRequestValidationResult { OK = 0, NoSignatureHeader = 1, NoCertHeader = 2, InvalidSignature = 4, InvalidTimestamp = 8, InvalidJson = 16, InvalidApplicationId = 32, NoContent = 64, InvalidVersion = 128 } } ================================================ FILE: AlexaSkillsKit.Lib/Helpers/DateTimeHelpers.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Helpers { public class DateTimeHelpers { public static DateTime FromAlexaTimestamp(JObject requestJson) { try { return requestJson["timestamp"].Value(); } catch (Exception ex) when (ex is InvalidCastException || ex is FormatException) { try { return FromUnixTimeMilliseconds(requestJson["timestamp"].Value()).DateTime; } catch (ArgumentOutOfRangeException) { throw ex; } } } /// /// Mirror DateTimeOffset.FromUnixTimeMilliseconds method signature introduced in .NET Framework 4.6 /// https://msdn.microsoft.com/en-us/library/system.datetimeoffset.fromunixtimemilliseconds(v=vs.110).aspx /// /// /// A Unix time, expressed as the number of milliseconds that have elapsed since 1970-01-01T00:00:00Z (January 1, 1970, at 12:00 AM UTC). For Unix times before this date, its value is negative. /// /// /// A date and time value that represents the same moment in time as the Unix time. /// public static DateTimeOffset FromUnixTimeMilliseconds(string timestamp) { long milliseconds; if (!long.TryParse(timestamp, out milliseconds) || milliseconds < -62135596800000 || milliseconds > 253402300799999) { throw new ArgumentOutOfRangeException("timestamp"); } var epoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeZoneInfo.Utc.BaseUtcOffset); return epoch.AddMilliseconds(milliseconds); } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioItem.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Interfaces.AudioPlayer { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#play /// public class AudioItem { public virtual AudioItemStream Stream { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioItemStream.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Interfaces.AudioPlayer { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#play /// public class AudioItemStream { public virtual string Url { get; set; } public virtual string Token { get; set; } public virtual string ExpectedPreviousToken { get; set; } public virtual long OffsetInMilliseconds { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerInterface.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Speechlet; using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Interfaces.AudioPlayer { public class AudioPlayerInterface : ISpeechletInterface { public static AudioPlayerInterface FromJson(JObject json) { if (json == null) return null; return new AudioPlayerInterface(); } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerPlaybackFailedRequest.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Speechlet; using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Interfaces.AudioPlayer { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#playbackfailed /// public class AudioPlayerPlaybackFailedRequest : AudioPlayerRequest { public AudioPlayerPlaybackFailedRequest(string subtype, JObject json) : base(subtype, json) { Error = Error.FromJson(json.Value("error")); CurrentPlaybackState = PlaybackState.FromJson(json.Value("currentPlaybackState")); } public Error Error { get; private set; } public PlaybackState CurrentPlaybackState { get; private set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerRequest.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Speechlet; using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Interfaces.AudioPlayer { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#requests /// public class AudioPlayerRequest : ExtendedSpeechletRequest { public static readonly string TypeName = "AudioPlayer"; public AudioPlayerRequest(string subtype, JObject json) : base(TypeName, subtype, json) { Token = json.Value("token"); OffsetInMilliseconds = json.Value("offsetInMilliseconds"); } public string Token { get; private set; } public long? OffsetInMilliseconds { get; private set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerResponse.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Interfaces.AudioPlayer.Directives; using AlexaSkillsKit.Speechlet; using System.Collections.Generic; namespace AlexaSkillsKit.Interfaces.AudioPlayer { /// /// https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#response-to-audioplayer-or-playbackcontroller-example-directives-only /// public class AudioPlayerResponse : ISpeechletResponse { public virtual IEnumerable Directives { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerState.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Speechlet; using Newtonsoft.Json.Linq; using System; namespace AlexaSkillsKit.Interfaces.AudioPlayer { public class AudioPlayerState : ISpeechletInterfaceState { public static AudioPlayerState FromJson(JObject json) { if (json == null) return null; PlayerActivityEnum playerActivity = PlayerActivityEnum.NONE; Enum.TryParse(json.Value("playerActivity"), out playerActivity); return new AudioPlayerState { OffsetInMilliseconds = json.Value("offsetInMilliseconds"), Token = json.Value("token"), PlayerActivity = playerActivity }; } public long? OffsetInMilliseconds { get; private set; } public string Token { get; private set; } public PlayerActivityEnum PlayerActivity { get; private set; } public enum PlayerActivityEnum { NONE = 0, // default in case parsing fails PLAYING, PAUSED, FINISHED, BUFFER_UNDERRUN, IDLE, STOPPED } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/AudioPlayer/Directives/AudioPlayerClearQueueDirective.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Interfaces.AudioPlayer.Directives { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#clearqueue /// public class AudioPlayerClearQueueDirective : AudioPlayerDirective { public AudioPlayerClearQueueDirective() : base("ClearQueue") { } public virtual ClearBehaviorEnum ClearBehavior { get; set; } public enum ClearBehaviorEnum { CLEAR_ENQUEUED, CLEAR_ALL } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/AudioPlayer/Directives/AudioPlayerDirective.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Speechlet; namespace AlexaSkillsKit.Interfaces.AudioPlayer.Directives { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#directives /// public class AudioPlayerDirective : Directive { public AudioPlayerDirective(string subtype) : base($"AudioPlayer.{subtype}") { } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/AudioPlayer/Directives/AudioPlayerPlayDirective.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Interfaces.AudioPlayer.Directives { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#play /// public class AudioPlayerPlayDirective : AudioPlayerDirective { public AudioPlayerPlayDirective() : base("Play") { } public virtual PlayBehaviorEnum PlayBehavior { get; set; } public virtual AudioItem AudioItem { get; set; } public enum PlayBehaviorEnum { REPLACE_ALL, ENQUEUE, REPLACE_ENQUEUED } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/AudioPlayer/Directives/AudioPlayerStopDirective.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Interfaces.AudioPlayer.Directives { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#stop /// public class AudioPlayerStopDirective : AudioPlayerDirective { public AudioPlayerStopDirective() : base("Stop") { } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/AudioPlayer/IAudioPlayerSpeechlet.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Speechlet; namespace AlexaSkillsKit.Interfaces.AudioPlayer { public interface IAudioPlayerSpeechlet { AudioPlayerResponse OnAudioPlayer(AudioPlayerRequest audioRequest, Context context); AudioPlayerResponse OnPlaybackController(PlaybackControllerRequest playbackRequest, Context context); void OnSystemExceptionEncountered(SystemExceptionEncounteredRequest systemRequest, Context context); } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/AudioPlayer/IAudioPlayerSpeechletAsync.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Speechlet; using System.Threading.Tasks; namespace AlexaSkillsKit.Interfaces.AudioPlayer { public interface IAudioPlayerSpeechletAsync { Task OnAudioPlayerAsync(AudioPlayerRequest audioRequest, Context context); Task OnPlaybackControllerAsync(PlaybackControllerRequest playbackRequest, Context context); Task OnSystemExceptionEncounteredAsync(SystemExceptionEncounteredRequest systemRequest, Context context); } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/AudioPlayer/PlaybackControllerRequest.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Speechlet; using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Interfaces.AudioPlayer { /// /// https://developer.amazon.com/docs/custom-skills/playback-controller-interface-reference.html#requests /// public class PlaybackControllerRequest : ExtendedSpeechletRequest { public static readonly string TypeName = "PlaybackController"; public PlaybackControllerRequest(string subtype, JObject json) : base(TypeName, subtype, json) { } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/Dialog/Directives/DialogConfirmIntentDirective.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Interfaces.Dialog.Directives { /// /// https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#confirmintent /// public class DialogConfirmIntentDirective : DialogDirective { public DialogConfirmIntentDirective() : base("ConfirmIntent") { } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/Dialog/Directives/DialogConfirmSlotDirective.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Interfaces.Dialog.Directives { /// /// https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#confirmslot /// public class DialogConfirmSlotDirective : DialogDirective { public DialogConfirmSlotDirective() : base("ConfirmSlot") { } public virtual string SlotToConfirm { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/Dialog/Directives/DialogDelegateDirective.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Interfaces.Dialog.Directives { /// /// https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#delegate /// public class DialogDelegateDirective : DialogDirective { public DialogDelegateDirective() : base("Delegate") { } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/Dialog/Directives/DialogDirective.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Slu; using AlexaSkillsKit.Speechlet; namespace AlexaSkillsKit.Interfaces.Dialog.Directives { /// /// https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#directives /// public class DialogDirective: Directive { public DialogDirective(string subtype) : base($"Dialog.{subtype}") { } public virtual Intent UpdatedIntent { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/Dialog/Directives/DialogElicitSlotDirective.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Interfaces.Dialog.Directives { /// /// https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#elicitslot /// public class DialogElicitSlotDirective : DialogDirective { public DialogElicitSlotDirective() : base("ElicitSlot") { } public virtual string SlotToElicit { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/Display/Directives/DisplayRenderTemplateDirective.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Speechlet; namespace AlexaSkillsKit.Interfaces.Display.Directives { /// /// https://developer.amazon.com/docs/custom-skills/display-interface-reference.html#form-of-the-displayrendertemplate-directive /// public class DisplayRenderTemplateDirective : Directive { public DisplayRenderTemplateDirective() : base("Display.RenderTemplate") { } public virtual DisplayTemplate Template { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/Display/Directives/HintDirective.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Speechlet; namespace AlexaSkillsKit.Interfaces.Display.Directives { /// /// https://developer.amazon.com/docs/custom-skills/display-interface-reference.html#hint-directive /// public class HintDirective : Directive { public HintDirective() : base("Hint") { } public virtual TextField Hint { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/Display/DisplayImage.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System.Collections.Generic; namespace AlexaSkillsKit.Interfaces.Display { /// /// https://developer.amazon.com/docs/custom-skills/display-interface-reference.html#image-object-specifications /// public class DisplayImage { public virtual string ContentDescription { get; set; } public virtual IEnumerable Sources { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/Display/DisplayImageSource.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Interfaces.Display { /// /// https://developer.amazon.com/docs/custom-skills/display-interface-reference.html#image-object-specifications /// public class DisplayImageSource { public virtual string Url { get; set; } public virtual ImageSizeEnum Size { get; set; } public virtual int WidthPixels { get; set; } public virtual int HeightPixels { get; set; } public enum ImageSizeEnum { X_SMALL, SMALL, MEDIUM, LARGE, X_LARGE } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/Display/DisplayInterface.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Speechlet; using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Interfaces.Display { /// /// https://developer.amazon.com/docs/custom-skills/display-interface-reference.html#determining-the-version-of-the-supported-display /// public class DisplayInterface: ISpeechletInterface { private const string DefaultTemplateVersion = "1"; private const string DefaultMarkupVersion = "1"; public static DisplayInterface FromJson(JObject json) { if (json == null) return null; return new DisplayInterface { TemplateVersion = json.Value("templateVersion") ?? DefaultTemplateVersion, MarkupVersion = json.Value("markupVersion") ?? DefaultMarkupVersion, Token = json.Value("token") }; } public string TemplateVersion { get; private set; } public string MarkupVersion { get; private set; } public string Token { get; private set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/Display/DisplayRequest.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Speechlet; using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Interfaces.Display { /// /// https://developer.amazon.com/docs/custom-skills/display-interface-reference.html#touch-selection-events /// public class DisplayRequest : ExtendedSpeechletRequest { public static readonly string TypeName = "Display"; public DisplayRequest(string subtype, JObject json) : base(TypeName, subtype, json) { Token = json.Value("token"); } public string Token { get; private set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/Display/DisplayTemplate.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System.Collections.Generic; namespace AlexaSkillsKit.Interfaces.Display { /// /// https://developer.amazon.com/docs/custom-skills/display-interface-reference.html#display-template-elements /// public class DisplayTemplate { public virtual string Type { get; set; } public virtual string Token { get; set; } public virtual string Title { get; set; } public virtual TextContent TextContent { get; set; } public virtual ButtonStateEnum BackButton { get; set; } public virtual DisplayImage BackgroundImage { get; set; } public virtual DisplayImage Image { get; set; } public virtual IEnumerable ListItems { get; set; } public enum ButtonStateEnum { HIDDEN, VISIBLE } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/Display/IDisplaySpeechlet.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Speechlet; namespace AlexaSkillsKit.Interfaces.Display { public interface IDisplaySpeechlet { SpeechletResponse OnDisplay(DisplayRequest displayRequest, Context context); } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/Display/IDisplaySpeechletAsync.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Speechlet; using System.Threading.Tasks; namespace AlexaSkillsKit.Interfaces.Display { public interface IDisplaySpeechletAsync { Task OnDisplayAsync(DisplayRequest displayRequest, Context context); } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/Display/ListItem.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Interfaces.Display { /// /// https://developer.amazon.com/docs/custom-skills/display-interface-reference.html#display-template-elements /// public class ListItem { public virtual string Token { get; set; } public virtual DisplayImage Image { get; set; } public virtual TextContent TextContent { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/Display/TextContent.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Interfaces.Display { /// /// https://developer.amazon.com/docs/custom-skills/display-interface-reference.html#textcontent-object-specifications /// public class TextContent { public virtual TextField PrimaryText { get; set; } public virtual TextField SecondaryText { get; set; } public virtual TextField TertiaryText { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/Display/TextField.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Interfaces.Display { /// /// https://developer.amazon.com/docs/custom-skills/display-interface-reference.html#textcontent-object-specifications /// public class TextField { public virtual TextTypeEnum Type { get; set; } public virtual string Text { get; set; } public enum TextTypeEnum { PlainText, RichText } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/VideoApp/Directives/VideoAppLaunchDirective.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Speechlet; namespace AlexaSkillsKit.Interfaces.VideoApp.Directives { /// /// https://developer.amazon.com/docs/custom-skills/videoapp-interface-reference.html#videoapp-directives /// public class VideoAppLaunchDirective : Directive { public VideoAppLaunchDirective() : base("VideoApp.Launch") { } public virtual VideoItem VideoItem { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/VideoApp/VideoItem.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Interfaces.VideoApp { /// /// https://developer.amazon.com/docs/custom-skills/videoapp-interface-reference.html#parameters-of-response /// public class VideoItem { public virtual string Source { get; set; } public virtual VideoItemMetadata Metadata { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Interfaces/VideoApp/VideoItemMetadata.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Interfaces.VideoApp { /// /// https://developer.amazon.com/docs/custom-skills/videoapp-interface-reference.html#parameters-of-response /// public class VideoItemMetadata { public virtual string Title { get; set; } public virtual string Subtitle { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Json/CamelCasePropertyNamesExceptDictionaryKeysContractResolver.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using Newtonsoft.Json.Serialization; using System; namespace AlexaSkillsKit.Json { public class CamelCasePropertyNamesExceptDictionaryKeysContractResolver : CamelCasePropertyNamesContractResolver { protected override JsonDictionaryContract CreateDictionaryContract(Type objectType) { var contract = base.CreateDictionaryContract(objectType); contract.DictionaryKeyResolver = propertyName => propertyName; return contract; } } } ================================================ FILE: AlexaSkillsKit.Lib/Json/Deserializer.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; namespace AlexaSkillsKit.Json { public class Deserializer { private static IDictionary> deserializers = new Dictionary>(); public static void RegisterDeserializer(string name, Func fromJson) { deserializers.Add(name, fromJson); } public static T FromJson(JProperty json) { if (json == null || !deserializers.ContainsKey(json.Name)) return default(T); return deserializers[json.Name](json.Value as JObject); } } } ================================================ FILE: AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using AlexaSkillsKit.Speechlet; using AlexaSkillsKit.Authentication; namespace AlexaSkillsKit.Json { public class SpeechletRequestEnvelope { public static SpeechletRequestParser RequestParser { get; } = new SpeechletRequestParser(); static SpeechletRequestEnvelope() { RequestParser.AddStandard(); RequestParser.AddSystem(); RequestParser.AddAudioPlayer(); RequestParser.AddPlaybackController(); RequestParser.AddDisplay(); } /// /// /// /// /// public static SpeechletRequestEnvelope FromJson(string content) { if (String.IsNullOrEmpty(content)) { throw new SpeechletValidationException(SpeechletRequestValidationResult.NoContent, "Request content is empty"); } JObject json = JsonConvert.DeserializeObject(content, Sdk.DeserializationSettings); return FromJson(json); } /// /// /// /// /// public static SpeechletRequestEnvelope FromJson(JObject json) { var version = json.Value("version"); if (version != null && version != Sdk.VERSION) { throw new SpeechletValidationException(SpeechletRequestValidationResult.InvalidVersion, "Request must conform to 1.0 schema."); } return new SpeechletRequestEnvelope { Version = version, Request = RequestParser.Parse(json.Value("request")), Session = Session.FromJson(json.Value("session")), Context = Context.FromJson(json.Value("context")) }; } public virtual SpeechletRequest Request { get; set; } public virtual Session Session { get; set; } public virtual string Version { get; set; } public virtual Context Context { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Json/SpeechletRequestParser.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Speechlet; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; namespace AlexaSkillsKit.Json { public class SpeechletRequestParser { private IDictionary> resolvers = new Dictionary>(); private SpeechletRequest Parse(string type, string subtype, JObject json) { if (json == null || !resolvers.ContainsKey(type)) return null; return resolvers[type](subtype, json); } public SpeechletRequest Parse(JObject json) { var requestTypeParts = json?.Value("type")?.Split('.'); if (requestTypeParts == null) { throw new ArgumentException("json"); } var requestType = requestTypeParts.Length > 1 ? requestTypeParts[0] : string.Empty; var requestSubtype = requestTypeParts.Last(); var request = Parse(requestType, requestSubtype, json); if (request == null) { throw new ArgumentException("json"); } return request; } public void AddInterface(string name, Func resolver) { resolvers[name] = resolver; } } } ================================================ FILE: AlexaSkillsKit.Lib/Json/SpeechletRequestParserExtensions.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Interfaces.AudioPlayer; using AlexaSkillsKit.Interfaces.Display; using AlexaSkillsKit.Speechlet; namespace AlexaSkillsKit.Json { public static class SpeechletRequestParserExtensions { public static void AddStandard(this SpeechletRequestParser parser) { parser.AddInterface(string.Empty, (subtype, json) => { switch (subtype) { case "LaunchRequest": return new LaunchRequest(json); case "IntentRequest": return new IntentRequest(json); case "SessionEndedRequest": return new SessionEndedRequest(json); } return null; }); } public static void AddSystem(this SpeechletRequestParser parser) { parser.AddInterface(SystemRequest.TypeName, (subtype, json) => { switch (subtype) { case "ExceptionEncountered": return new SystemExceptionEncounteredRequest(subtype, json); } return null; }); } public static void AddAudioPlayer(this SpeechletRequestParser parser) { parser.AddInterface(AudioPlayerRequest.TypeName, (subtype, json) => { switch (subtype) { case "PlaybackFailed": return new AudioPlayerPlaybackFailedRequest(subtype, json); default: return new AudioPlayerRequest(subtype, json); } }); } public static void AddPlaybackController(this SpeechletRequestParser parser) { parser.AddInterface(PlaybackControllerRequest.TypeName, (subtype, json) => new PlaybackControllerRequest(subtype, json)); } public static void AddDisplay(this SpeechletRequestParser parser) { parser.AddInterface(DisplayRequest.TypeName, (subtype, json) => new DisplayRequest(subtype, json)); } } } ================================================ FILE: AlexaSkillsKit.Lib/Json/SpeechletResponseEnvelope.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System.Collections.Generic; using Newtonsoft.Json; using AlexaSkillsKit.Speechlet; namespace AlexaSkillsKit.Json { public class SpeechletResponseEnvelope { private static JsonSerializerSettings _serializerSettings = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, ContractResolver = new CamelCasePropertyNamesExceptDictionaryKeysContractResolver(), Converters = new List { new Newtonsoft.Json.Converters.StringEnumConverter() } }; /// /// /// /// public virtual string ToJson() { return JsonConvert.SerializeObject(this, _serializerSettings); } public virtual ISpeechletResponse Response { get; set; } public virtual Dictionary SessionAttributes { get; set; } public virtual string Version { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Properties/AssemblyInfo.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. 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("AlexaSkillsKit")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("AlexaSkillsKit")] [assembly: AssemblyCopyright("Copyright © 2018 Stefan Negritoiu (FreeBusy) and contributors")] [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("5471ba5b-32c1-486c-851b-dbb8d9c53f13")] // 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.6.0")] [assembly: AssemblyFileVersion("1.6.0")] ================================================ FILE: AlexaSkillsKit.Lib/Sdk.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; using Newtonsoft.Json; namespace AlexaSkillsKit { public static class Sdk { public const string VERSION = "1.0"; public const string CHARACTER_ENCODING = "UTF-8"; public const string ECHO_API_DOMAIN_NAME = "echo-api.amazon.com"; public const string SIGNATURE_CERT_URL_REQUEST_HEADER = "SignatureCertChainUrl"; public const string SIGNATURE_CERT_URL_HOST = "s3.amazonaws.com"; public const string SIGNATURE_CERT_URL_PATH = "/echo.api/"; public const string SIGNATURE_CERT_TYPE = "X.509"; public const string SIGNATURE_REQUEST_HEADER = "Signature"; public const string SIGNATURE_ALGORITHM = "SHA1withRSA"; public const string SIGNATURE_KEY_TYPE = "RSA"; public const int TIMESTAMP_TOLERANCE_SEC = 150; public static JsonSerializerSettings DeserializationSettings = new JsonSerializerSettings { MissingMemberHandling = MissingMemberHandling.Ignore }; } } ================================================ FILE: AlexaSkillsKit.Lib/Slu/ConfirmationStatusEnum.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Slu { public enum ConfirmationStatusEnum { NONE, CONFIRMED, DENIED } } ================================================ FILE: AlexaSkillsKit.Lib/Slu/Intent.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System.Collections.Generic; using System.Linq; using Newtonsoft.Json.Linq; using System; namespace AlexaSkillsKit.Slu { /// /// https://developer.amazon.com/docs/custom-skills/request-types-reference.html#intent-object /// public class Intent { /// /// /// /// /// public static Intent FromJson(JObject json) { if (json == null) return null; var slots = json.Value("slots")?.Children() .ToDictionary(x => x.Name, x => Slot.FromJson(x.Value as JObject)); ConfirmationStatusEnum confirmationStatus; Enum.TryParse(json.Value("confirmationStatus"), out confirmationStatus); return new Intent { Name = json.Value("name"), ConfirmationStatus = confirmationStatus, Slots = slots ?? new Dictionary() }; } public virtual string Name { get; set; } public virtual ConfirmationStatusEnum ConfirmationStatus { get; set; } public virtual IDictionary Slots { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Slu/Resolutions.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using Newtonsoft.Json.Linq; using System.Collections.Generic; using System.Linq; namespace AlexaSkillsKit.Slu { /// /// https://developer.amazon.com/docs/custom-skills/request-types-reference.html#resolutions-object /// public class Resolutions { public static Resolutions FromJson(JObject json) { if (json == null) return null; var resolutionsPerAuthority = json.Value("resolutionsPerAuthority")?.Children() .Select(x => Slu.ResolutionsPerAuthority.FromJson(x.Value())); return new Resolutions { ResolutionsPerAuthority = resolutionsPerAuthority }; } public virtual IEnumerable ResolutionsPerAuthority { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Slu/ResolutionsPerAuthority.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using Newtonsoft.Json.Linq; using System.Collections.Generic; using System.Linq; namespace AlexaSkillsKit.Slu { public class ResolutionsPerAuthority { public static ResolutionsPerAuthority FromJson(JObject json) { if (json == null) return null; var values = json.Value("values")?.Children() .Select(x => ResolutionsPerAuthorityValue.FromJson(x.Value())); return new ResolutionsPerAuthority { Authority = json.Value("authority"), Status = ResolutionsPerAuthorityStatus.FromJson(json.Value("status")), Values = values }; } public virtual string Authority { get; set; } public virtual ResolutionsPerAuthorityStatus Status { get; set; } public virtual IEnumerable Values { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Slu/ResolutionsPerAuthorityStatus.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Slu { public class ResolutionsPerAuthorityStatus { public static ResolutionsPerAuthorityStatus FromJson(JObject json) { if (json == null) return null; return new ResolutionsPerAuthorityStatus { Code = json.Value("code") }; } public virtual string Code { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Slu/ResolutionsPerAuthorityValue.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Slu { public class ResolutionsPerAuthorityValue { public static ResolutionsPerAuthorityValue FromJson(JObject json) { if (json == null) return null; return new ResolutionsPerAuthorityValue { Value = ResolutionsPerAuthorityValueValue.FromJson(json.Value("value")) }; } public virtual ResolutionsPerAuthorityValueValue Value { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Slu/ResolutionsPerAuthorityValueValue.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Slu { public class ResolutionsPerAuthorityValueValue { public static ResolutionsPerAuthorityValueValue FromJson(JObject json) { if (json == null) return null; return new ResolutionsPerAuthorityValueValue { Name = json.Value("name"), Id = json.Value("id") }; } public virtual string Name { get; set; } public virtual string Id { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Slu/Slot.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; namespace AlexaSkillsKit.Slu { /// /// https://developer.amazon.com/docs/custom-skills/request-types-reference.html#slot-object /// public class Slot { /// /// /// /// /// public static Slot FromJson(JObject json) { if (json == null) return null; ConfirmationStatusEnum confirmationStatus; Enum.TryParse(json.Value("confirmationStatus"), out confirmationStatus); return new Slot { Name = json.Value("name"), Value = json.Value("value"), ConfirmationStatus = confirmationStatus, Resolutions = Resolutions.FromJson(json.Value("resolutions")) }; } public virtual string Name { get; set; } public virtual string Value { get; set; } public virtual ConfirmationStatusEnum ConfirmationStatus { get; set; } [JsonIgnore] public virtual Resolutions Resolutions { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/Application.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { public class Application { /// /// /// /// /// public static Application FromJson(JObject json) { return new Application { Id = json.Value("applicationId") }; } public virtual string Id { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/Cause.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#system-exceptionencountered /// public class Cause { public static Cause FromJson(JObject json) { if (json == null) return null; return new Cause { RequestId = json.Value("requestId") }; } public string RequestId { get; private set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/Context.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Interfaces.AudioPlayer; using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { /// /// https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#context-object /// public class Context { public static Context FromJson(JObject json) { if (json == null) return null; return new Context { System = SystemState.FromJson(json.Value("System")), AudioPlayer = AudioPlayerState.FromJson(json.Value("AudioPlayer")) }; } public SystemState System { get; private set; } public AudioPlayerState AudioPlayer { get; private set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/Device.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { /// /// https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#system-object /// public class Device { public static Device FromJson(JObject json) { if (json == null) return null; return new Device { DeviceId = json.Value("deviceId"), SupportedInterfaces = SupportedInterfaces.FromJson(json.Value("supportedInterfaces")) }; } public string DeviceId { get; private set; } public SupportedInterfaces SupportedInterfaces { get; private set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/Directive.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Speechlet { /// /// https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#response-object /// public class Directive { public Directive(string type) { Type = type; } public virtual string Type { get; private set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/Error.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using Newtonsoft.Json.Linq; using System; namespace AlexaSkillsKit.Speechlet { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#playbackfailed /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#system-exceptionencountered /// public class Error { public static Error FromJson(JObject json) { if (json == null) return null; TypeEnum type = TypeEnum.NONE; Enum.TryParse(json.Value("type"), out type); return new Error { Type = type, Message = json.Value("message") }; } public TypeEnum Type { get; private set; } public string Message { get; private set; } public enum TypeEnum { NONE = 0, // default in case parsing fails INVALID_RESPONSE, DEVICE_COMMUNICATION_ERROR, INTERNAL_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_INVALID_REQUEST, MEDIA_ERROR_SERVICE_UNAVAILABLE, MEDIA_ERROR_INTERNAL_SERVER_ERROR, MEDIA_ERROR_INTERNAL_DEVICE_ERROR } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/ExtendedSpeechletRequest.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { /// /// https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#request-body-parameters /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#requests /// https://developer.amazon.com/docs/custom-skills/playback-controller-interface-reference.html#requests /// public class ExtendedSpeechletRequest : SpeechletRequest { public ExtendedSpeechletRequest(string type, string subtype, JObject json) : base(json) { Type = type; Subtype = subtype; } public string Type { get; private set; } public string Subtype { get; private set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/ISpeechlet.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; namespace AlexaSkillsKit.Speechlet { [Obsolete("Does not support context object. Implement ISpeechletWithContext instead")] public interface ISpeechlet : ISpeechletBase { SpeechletResponse OnIntent(IntentRequest intentRequest, Session session); SpeechletResponse OnLaunch(LaunchRequest launchRequest, Session session); void OnSessionStarted(SessionStartedRequest sessionStartedRequest, Session session); void OnSessionEnded(SessionEndedRequest sessionEndedRequest, Session session); } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/ISpeechletAsync.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; using System.Threading.Tasks; namespace AlexaSkillsKit.Speechlet { [Obsolete("Does not support context object. Implement ISpeechletWithContextAsync instead")] public interface ISpeechletAsync : ISpeechletBase { Task OnIntentAsync(IntentRequest intentRequest, Session session); Task OnLaunchAsync(LaunchRequest launchRequest, Session session); Task OnSessionStartedAsync(SessionStartedRequest sessionStartedRequest, Session session); Task OnSessionEndedAsync(SessionEndedRequest sessionEndedRequest, Session session); } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/ISpeechletBase.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; using AlexaSkillsKit.Authentication; using AlexaSkillsKit.Json; namespace AlexaSkillsKit.Speechlet { public interface ISpeechletBase { bool OnRequestValidation(SpeechletRequestValidationResult result, DateTime referenceTimeUtc, SpeechletRequestEnvelope requestEnvelope); } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/ISpeechletInterface.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Speechlet { public interface ISpeechletInterface { } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/ISpeechletInterfaceState.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Speechlet { public interface ISpeechletInterfaceState { } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/ISpeechletResponse.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Speechlet { public interface ISpeechletResponse { } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/ISpeechletWithContext.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Speechlet { public interface ISpeechletWithContext { SpeechletResponse OnIntent(IntentRequest intentRequest, Session session, Context context); SpeechletResponse OnLaunch(LaunchRequest launchRequest, Session session, Context context); void OnSessionStarted(SessionStartedRequest sessionStartedRequest, Session session, Context context); void OnSessionEnded(SessionEndedRequest sessionEndedRequest, Session session, Context context); } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/ISpeechletWithContextAsync.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System.Threading.Tasks; namespace AlexaSkillsKit.Speechlet { public interface ISpeechletWithContextAsync { Task OnIntentAsync(IntentRequest intentRequest, Session session, Context context); Task OnLaunchAsync(LaunchRequest launchRequest, Session session, Context context); Task OnSessionStartedAsync(SessionStartedRequest sessionStartedRequest, Session session, Context context); Task OnSessionEndedAsync(SessionEndedRequest sessionEndedRequest, Session session, Context context); } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/IntentRequest.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Slu; using Newtonsoft.Json.Linq; using System; namespace AlexaSkillsKit.Speechlet { public class IntentRequest : SpeechletRequest { public IntentRequest(JObject json) : base(json) { Intent = Intent.FromJson(json.Value("intent")); DialogStateEnum dialogState = DialogStateEnum.UNKNOWN; Enum.TryParse(json.Value("dialogState"), out dialogState); DialogState = dialogState; } public virtual Intent Intent { get; private set; } public virtual DialogStateEnum DialogState { get; private set; } public enum DialogStateEnum { UNKNOWN = 0, // default in case parsing fails STARTED, IN_PROGRESS, COMPLETED } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/LaunchRequest.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { public class LaunchRequest : SpeechletRequest { public LaunchRequest(JObject json) : base(json) { } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/Person.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { public class Person { /// /// /// /// /// public static Person FromJson(JObject json) { if (json == null) return null; return new Person{ PersonId = json.Value("personId"), AccessToken = json.Value("accessToken") }; } public string PersonId{ get; private set; } public virtual string AccessToken{ get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/PlaybackState.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using Newtonsoft.Json.Linq; using System; namespace AlexaSkillsKit.Speechlet { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#playbackfailed /// public class PlaybackState { public static PlaybackState FromJson(JObject json) { if (json == null) return null; PlayerActivityEnum playerActivity = PlayerActivityEnum.NONE; Enum.TryParse(json.Value("playerActivity"), out playerActivity); return new PlaybackState { Token = json.Value("token"), OffsetInMilliseconds = json.Value("offsetInMilliseconds"), PlayerActivity = playerActivity }; } public string Token { get; private set; } public long? OffsetInMilliseconds { get; private set; } public PlayerActivityEnum PlayerActivity { get; private set; } public enum PlayerActivityEnum { NONE = 0, // default in case parsing fails PLAYING, PAUSED, FINISHED, BUFFER_UNDERRUN, IDLE } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/Session.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; using System.Collections.Generic; using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { public class Session { public const string INTENT_SEQUENCE = "intentSequence"; public const string SEPARATOR = ";"; /// /// /// /// /// public static Session FromJson(JObject json) { if (json == null) return null; var attributes = new Dictionary(); var jsonAttributes = json.Value("attributes"); if (jsonAttributes != null) { foreach (var attrib in jsonAttributes.Children()) { attributes.Add(attrib.Value().Name, attrib.Value().Value.ToString()); } } return new Session { SessionId = json.Value("sessionId"), IsNew = json.Value("new"), User = User.FromJson(json.Value("user")), Application = Application.FromJson(json.Value("application")), Attributes = attributes }; } public virtual string SessionId { get; set; } public virtual bool IsNew { get; set; } public virtual Application Application { get; set; } public virtual User User { get; set; } public virtual Dictionary Attributes { get; set; } public virtual string[] IntentSequence { get { return !Attributes.ContainsKey(INTENT_SEQUENCE) || String.IsNullOrEmpty(Attributes[INTENT_SEQUENCE]) ? new string[0] : Attributes[INTENT_SEQUENCE].Split( new string[1] { SEPARATOR }, StringSplitOptions.RemoveEmptyEntries); } } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/SessionEndedRequest.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using Newtonsoft.Json.Linq; using System; namespace AlexaSkillsKit.Speechlet { public class SessionEndedRequest : SpeechletRequest { public SessionEndedRequest(JObject json) : base(json) { ReasonEnum reason = ReasonEnum.UNKNOWN; Enum.TryParse(json.Value("reason"), out reason); Reason = reason; Error = Error.FromJson(json.Value("error")); } public virtual ReasonEnum Reason { get; private set; } public Error Error { get; private set; } public enum ReasonEnum { NONE = 0, // default in case parsing fails (backwards compatibility) UNKNOWN = 0, // default in case parsing fails ERROR, USER_INITIATED, EXCEEDED_MAX_REPROMPTS, } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/SessionStartedRequest.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.Speechlet { public class SessionStartedRequest : SpeechletRequest { public SessionStartedRequest(SpeechletRequest other) : base(other) { } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/Speechlet.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; namespace AlexaSkillsKit.Speechlet { [Obsolete("Does not support context object. Derive from SpeechletBase instead and implement ISpeechletWithContext")] public abstract class Speechlet : SpeechletBase, ISpeechlet { public abstract SpeechletResponse OnIntent(IntentRequest intentRequest, Session session); public abstract SpeechletResponse OnLaunch(LaunchRequest launchRequest, Session session); public abstract void OnSessionStarted(SessionStartedRequest sessionStartedRequest, Session session); public abstract void OnSessionEnded(SessionEndedRequest sessionEndedRequest, Session session); } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; using System.Threading.Tasks; namespace AlexaSkillsKit.Speechlet { [Obsolete("Does not support context object. Derive from SpeechletBase instead and implement ISpeechletWithContextAsync")] public abstract class SpeechletAsync : SpeechletBase, ISpeechletAsync { public abstract Task OnIntentAsync(IntentRequest intentRequest, Session session); public abstract Task OnLaunchAsync(LaunchRequest launchRequest, Session session); public abstract Task OnSessionEndedAsync(SessionEndedRequest sessionEndedRequest, Session session); public abstract Task OnSessionStartedAsync(SessionStartedRequest sessionStartedRequest, Session session); } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Authentication; using AlexaSkillsKit.Json; using Newtonsoft.Json.Linq; using System; using System.Net.Http; using System.Threading.Tasks; namespace AlexaSkillsKit.Speechlet { public class SpeechletBase : ISpeechletBase { public SpeechletService Service { get; } protected SpeechletBase() { Service = new SpeechletService(this); } /// /// Processes Alexa request AND validates request signature /// /// /// public HttpResponseMessage GetResponse(HttpRequestMessage httpRequest) { return AsyncHelpers.RunSync(async () => await Service.GetResponseAsync(httpRequest)); } public async Task GetResponseAsync(HttpRequestMessage httpRequest) { return await Service.GetResponseAsync(httpRequest); } /// /// Processes Alexa request but does NOT validate request signature /// /// /// public string ProcessRequest(string requestContent) { return AsyncHelpers.RunSync(async () => await ProcessRequestAsync(requestContent)); } public async Task ProcessRequestAsync(string requestContent) { var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); return (await Service.ProcessRequestAsync(requestEnvelope))?.ToJson(); } /// /// Processes Alexa request but does NOT validate request signature /// /// /// public virtual string ProcessRequest(JObject requestJson) { return AsyncHelpers.RunSync(async () => await ProcessRequestAsync(requestJson)); } public async Task ProcessRequestAsync(JObject requestJson) { var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); return (await Service.ProcessRequestAsync(requestEnvelope))?.ToJson(); } /// /// Opportunity to set policy for handling requests with invalid signatures and/or timestamps /// /// true if request processing should continue, otherwise false public virtual bool OnRequestValidation( SpeechletRequestValidationResult result, DateTime referenceTimeUtc, SpeechletRequestEnvelope requestEnvelope) { return result == SpeechletRequestValidationResult.OK; } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/SpeechletException.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; namespace AlexaSkillsKit.Speechlet { public class SpeechletException : Exception { public SpeechletException() : base() { } public SpeechletException(string message) : base(message) { } public SpeechletException(string message, Exception cause) : base(message, cause) { } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/SpeechletRequest.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Helpers; using Newtonsoft.Json.Linq; using System; namespace AlexaSkillsKit.Speechlet { public abstract class SpeechletRequest { protected SpeechletRequest(JObject json) { RequestId = json.Value("requestId"); Timestamp = DateTimeHelpers.FromAlexaTimestamp(json); Locale = json.Value("locale"); } protected SpeechletRequest(SpeechletRequest other) { RequestId = other.RequestId; Timestamp = other.Timestamp; Locale = other.Locale; } public string RequestId { get; private set; } public DateTime Timestamp { get; private set; } public string Locale { get; private set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/SpeechletResponse.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.UI; using System.Collections.Generic; namespace AlexaSkillsKit.Speechlet { /// /// https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#response-object /// public class SpeechletResponse : ISpeechletResponse { /// /// Set default ShouldEndSession value to false for the sake of backwards compatibility /// public SpeechletResponse() { ShouldEndSession = false; } public virtual Card Card { get; set; } public virtual IEnumerable Directives { get; set; } public virtual OutputSpeech OutputSpeech { get; set; } public virtual Reprompt Reprompt { get; set; } public virtual bool? ShouldEndSession { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Authentication; using AlexaSkillsKit.Interfaces.AudioPlayer; using AlexaSkillsKit.Interfaces.Display; using AlexaSkillsKit.Json; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace AlexaSkillsKit.Speechlet { public class SpeechletService { private ISpeechletBase speechlet; private IDictionary>> handlers = new Dictionary>>(); public string ApplicationId { get; set; } public void AddHandler(string type, Func> handler) where T : SpeechletRequest { handlers[type] = (request, context) => handler(request as T, context); } public SpeechletService(ISpeechletBase speechlet) { this.speechlet = speechlet; if (speechlet is IAudioPlayerSpeechletAsync || speechlet is IAudioPlayerSpeechlet) { AddHandler(AudioPlayerRequest.TypeName, async (request, context) => { return (speechlet as IAudioPlayerSpeechlet)?.OnAudioPlayer(request, context) ?? await (speechlet as IAudioPlayerSpeechletAsync).OnAudioPlayerAsync(request, context); }); AddHandler(PlaybackControllerRequest.TypeName, async (request, context) => { return (speechlet as IAudioPlayerSpeechlet)?.OnPlaybackController(request, context) ?? await (speechlet as IAudioPlayerSpeechletAsync).OnPlaybackControllerAsync(request, context); }); AddHandler(SystemRequest.TypeName, async (request, context) => { (speechlet as IAudioPlayerSpeechlet)?.OnSystemExceptionEncountered(request, context); await (speechlet as IAudioPlayerSpeechletAsync).OnSystemExceptionEncounteredAsync(request, context); return null; }); } if (speechlet is IDisplaySpeechletAsync) { AddHandler(DisplayRequest.TypeName, async (request, context) => { return (speechlet as IDisplaySpeechlet)?.OnDisplay(request, context) ?? await (speechlet as IDisplaySpeechletAsync).OnDisplayAsync(request, context); }); } } /// /// Processes Alexa request AND validates request signature /// /// /// public async Task GetResponseAsync(HttpRequestMessage httpRequest) { string chainUrl = null; if (httpRequest.Headers.Contains(Sdk.SIGNATURE_CERT_URL_REQUEST_HEADER)) { chainUrl = httpRequest.Headers.GetValues(Sdk.SIGNATURE_CERT_URL_REQUEST_HEADER).FirstOrDefault(x => !string.IsNullOrEmpty(x)); } string signature = null; if (httpRequest.Headers.Contains(Sdk.SIGNATURE_REQUEST_HEADER)) { signature = httpRequest.Headers.GetValues(Sdk.SIGNATURE_REQUEST_HEADER).FirstOrDefault(x => !string.IsNullOrEmpty(x)); } var content = await httpRequest.Content.ReadAsStringAsync(); try { var alexaRequest = await GetRequestAsync(content, chainUrl, signature); var alexaResponse = await ProcessRequestAsync(alexaRequest); var json = alexaResponse?.ToJson(); return (json == null) ? new HttpResponseMessage(HttpStatusCode.InternalServerError) : new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(json, Encoding.UTF8, "application/json") }; } catch (SpeechletValidationException ex) { return new HttpResponseMessage(HttpStatusCode.BadRequest) { ReasonPhrase = ex.ValidationResult.ToString() }; } } public async Task GetRequestAsync(string content, string chainUrl, string signature) { var validationResult = SpeechletRequestValidationResult.OK; if (string.IsNullOrEmpty(chainUrl)) { validationResult |= SpeechletRequestValidationResult.NoCertHeader; } if (string.IsNullOrEmpty(signature)) { validationResult |= SpeechletRequestValidationResult.NoSignatureHeader; } // attempt to verify signature only if we were able to locate certificate and signature headers if (validationResult == SpeechletRequestValidationResult.OK) { var alexaBytes = Encoding.UTF8.GetBytes(content); if (!await SpeechletRequestSignatureVerifier.VerifyRequestSignatureAsync(alexaBytes, signature, chainUrl)) { validationResult |= SpeechletRequestValidationResult.InvalidSignature; } } SpeechletRequestEnvelope result = null; try { result = SpeechletRequestEnvelope.FromJson(content); } catch (SpeechletValidationException ex) { validationResult |= ex.ValidationResult; } catch (Exception ex) when (ex is JsonReaderException || ex is InvalidCastException || ex is FormatException) { validationResult |= SpeechletRequestValidationResult.InvalidJson; } var success = false; // attempt to verify timestamp only if we were able to parse request body if (result != null) { var now = DateTime.UtcNow; // reference time for this request if (!SpeechletRequestTimestampVerifier.VerifyRequestTimestamp(result, now)) { validationResult |= SpeechletRequestValidationResult.InvalidTimestamp; } if (!string.IsNullOrEmpty(ApplicationId) && result.Context.System.Application.Id != ApplicationId) { validationResult |= SpeechletRequestValidationResult.InvalidApplicationId; } success = speechlet?.OnRequestValidation(validationResult, now, result) ?? (validationResult == SpeechletRequestValidationResult.OK); } if (!success) { throw new SpeechletValidationException(validationResult); } return result; } /// /// /// /// /// public async Task ProcessRequestAsync(SpeechletRequestEnvelope requestEnvelope) { var session = requestEnvelope.Session; var context = requestEnvelope.Context; var request = requestEnvelope.Request; var response = !(request is ExtendedSpeechletRequest) ? await HandleStandardRequestAsync(request, session, context) : await HandleExtendedRequestAsync(request as ExtendedSpeechletRequest, context); if (response == null) { response = new SpeechletResponse(); } var responseEnvelope = new SpeechletResponseEnvelope { Version = requestEnvelope.Version, Response = response, SessionAttributes = session?.Attributes }; return responseEnvelope; } /// /// /// private async Task HandleStandardRequestAsync( SpeechletRequest request, Session session, Context context) { #pragma warning disable 612, 618 if (session != null) { // Do session management prior to calling OnSessionStarted and OnIntentAsync // to allow dev to change session values if behavior is not desired DoSessionManagement(request as IntentRequest, session); if (session.IsNew) { var sessionStartedRequest = new SessionStartedRequest(request); if (speechlet is ISpeechletWithContext) (speechlet as ISpeechletWithContext).OnSessionStarted(sessionStartedRequest, session, context); else if (speechlet is ISpeechletWithContextAsync) await (speechlet as ISpeechletWithContextAsync).OnSessionStartedAsync(sessionStartedRequest, session, context); else if (speechlet is ISpeechlet) (speechlet as ISpeechlet).OnSessionStarted(sessionStartedRequest, session); else if (speechlet is ISpeechletAsync) await (speechlet as ISpeechletAsync).OnSessionStartedAsync(sessionStartedRequest, session); } } #pragma warning restore 612, 618 #pragma warning disable 612, 618 if (request is LaunchRequest) { // process launch request if (speechlet is ISpeechletWithContext) return (speechlet as ISpeechletWithContext).OnLaunch(request as LaunchRequest, session, context); else if (speechlet is ISpeechletWithContextAsync) return await (speechlet as ISpeechletWithContextAsync).OnLaunchAsync(request as LaunchRequest, session, context); else if (speechlet is ISpeechlet) return (speechlet as ISpeechlet).OnLaunch(request as LaunchRequest, session); else if (speechlet is ISpeechletAsync) return await (speechlet as ISpeechletAsync).OnLaunchAsync(request as LaunchRequest, session); } else if (request is IntentRequest) { // process intent request if (speechlet is ISpeechletWithContext) return (speechlet as ISpeechletWithContext).OnIntent(request as IntentRequest, session, context); else if (speechlet is ISpeechletWithContextAsync) return await (speechlet as ISpeechletWithContextAsync).OnIntentAsync(request as IntentRequest, session, context); else if (speechlet is ISpeechlet) return (speechlet as ISpeechlet).OnIntent(request as IntentRequest, session); else if (speechlet is ISpeechletAsync) return await (speechlet as ISpeechletAsync).OnIntentAsync(request as IntentRequest, session); } else if (request is SessionEndedRequest) { // process session ended request if (speechlet is ISpeechletWithContext) (speechlet as ISpeechletWithContext).OnSessionEnded(request as SessionEndedRequest, session, context); else if (speechlet is ISpeechletWithContextAsync) await (speechlet as ISpeechletWithContextAsync).OnSessionEndedAsync(request as SessionEndedRequest, session, context); else if (speechlet is ISpeechlet) (speechlet as ISpeechlet).OnSessionEnded(request as SessionEndedRequest, session); else if (speechlet is ISpeechletAsync) await (speechlet as ISpeechletAsync).OnSessionEndedAsync(request as SessionEndedRequest, session); } #pragma warning restore 612, 618 return null; } private async Task HandleExtendedRequestAsync(ExtendedSpeechletRequest request, Context context) { return handlers.ContainsKey(request.Type) ? (await handlers[request.Type].Invoke(request, context)) : null; } /// /// /// private void DoSessionManagement(IntentRequest request, Session session) { if (request == null) return; if (session.Attributes == null) { session.Attributes = new Dictionary(); } if (session.IsNew) { session.Attributes[Session.INTENT_SEQUENCE] = request.Intent.Name; } else { // if the session was started as a result of a launch request // a first intent isn't yet set, so set it to the current intent if (!session.Attributes.ContainsKey(Session.INTENT_SEQUENCE)) { session.Attributes[Session.INTENT_SEQUENCE] = request.Intent.Name; } else { session.Attributes[Session.INTENT_SEQUENCE] += Session.SEPARATOR + request.Intent.Name; } } // Auto-session management: copy all slot values from current intent into session foreach (var slot in request.Intent.Slots.Values) { session.Attributes[slot.Name] = slot.Value; } } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/SpeechletValidationException.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using AlexaSkillsKit.Authentication; using System; namespace AlexaSkillsKit.Speechlet { public class SpeechletValidationException : SpeechletException { public SpeechletRequestValidationResult ValidationResult { get; } public SpeechletValidationException(SpeechletRequestValidationResult validationResult) : base() { ValidationResult = validationResult; } public SpeechletValidationException(SpeechletRequestValidationResult validationResult, string message) : base(message) { ValidationResult = validationResult; } public SpeechletValidationException(SpeechletRequestValidationResult validationResult, string message, Exception cause) : base(message, cause) { ValidationResult = validationResult; } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/SupportedInterfaces.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System.Collections.Generic; using Newtonsoft.Json.Linq; using System.Linq; using AlexaSkillsKit.Interfaces.Display; using AlexaSkillsKit.Interfaces.AudioPlayer; using AlexaSkillsKit.Json; namespace AlexaSkillsKit.Speechlet { /// /// https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#system-object /// public class SupportedInterfaces : Dictionary { /// /// Register supported interfaces for deserialization /// static SupportedInterfaces() { Deserializer.RegisterDeserializer("Display", DisplayInterface.FromJson); Deserializer.RegisterDeserializer("AudioPlayer", AudioPlayerInterface.FromJson); } public static SupportedInterfaces FromJson(JObject json) { if (json == null) return null; var dictionary = json.Children() .ToDictionary(x => x.Name, x => Deserializer.FromJson(x)); return new SupportedInterfaces(dictionary); } private SupportedInterfaces(IDictionary dictionary) : base(dictionary) { } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/SystemExceptionEncounteredRequest.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#system-exceptionencountered /// public class SystemExceptionEncounteredRequest : SystemRequest { public SystemExceptionEncounteredRequest(string subtype, JObject json) : base(subtype, json) { Error = Error.FromJson(json.Value("error")); Cause = Cause.FromJson(json.Value("cause")); } public Error Error { get; private set; } public Cause Cause { get; private set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/SystemRequest.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#system-exceptionencountered /// public class SystemRequest : ExtendedSpeechletRequest { public static readonly string TypeName = "System"; public SystemRequest(string subtype, JObject json) : base(TypeName, subtype, json) { } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/SystemState.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { /// /// https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#system-object /// public class SystemState : ISpeechletInterfaceState { public static SystemState FromJson(JObject json) { if (json == null) return null; return new SystemState { Application = Application.FromJson(json.Value("application")), User = User.FromJson(json.Value("user")), Device = Device.FromJson(json.Value("device")), Person = Person.FromJson(json.Value("person")), ApiEndpoint = json.Value("apiEndpoint"), ApiAccessToken = json.Value("apiAccessToken") }; } public Application Application { get; private set; } public User User { get; private set; } public Person Person { get; private set; } public Device Device { get; private set; } public string ApiEndpoint { get; private set; } public string ApiAccessToken { get; private set; } } } ================================================ FILE: AlexaSkillsKit.Lib/Speechlet/User.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { public class User { /// /// /// /// /// public static User FromJson(JObject json) { return new User { Id = json.Value("userId"), AccessToken = json.Value("accessToken") }; } public virtual string Id { get; set; } public virtual string AccessToken { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/UI/AskForPermissionsConsentCard.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System.Collections.Generic; namespace AlexaSkillsKit.UI { /// /// https://developer.amazon.com/docs/custom-skills/device-address-api.html#permissions-card /// public class AskForPermissionsConsentCard : Card { public override string Type { get { return "AskForPermissionsConsent"; } } public virtual IEnumerable Permissions { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/UI/Card.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; namespace AlexaSkillsKit.UI { public abstract class Card { public abstract string Type { get; } } } ================================================ FILE: AlexaSkillsKit.Lib/UI/Image.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; using System.Collections.Generic; namespace AlexaSkillsKit.UI { public class Image { public virtual string SmallImageUrl { get; set; } public virtual string LargeImageUrl { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/UI/LinkAccountCard.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; using System.Collections.Generic; namespace AlexaSkillsKit.UI { public class LinkAccountCard : Card { public override string Type { get { return "LinkAccount"; } } } } ================================================ FILE: AlexaSkillsKit.Lib/UI/OutputSpeech.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; using System.Collections.Generic; namespace AlexaSkillsKit.UI { public abstract class OutputSpeech { public abstract string Type { get; } } } ================================================ FILE: AlexaSkillsKit.Lib/UI/PlainTextOutputSpeech.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.UI { public class PlainTextOutputSpeech : OutputSpeech { public override string Type { get { return "PlainText"; } } public virtual string Text { get; set; } public static implicit operator PlainTextOutputSpeech(string spokenText) { return new PlainTextOutputSpeech() { Text = spokenText }; } } } ================================================ FILE: AlexaSkillsKit.Lib/UI/Reprompt.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; using System.Collections.Generic; namespace AlexaSkillsKit.UI { public class Reprompt { public OutputSpeech OutputSpeech { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/UI/SimpleCard.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.UI { public class SimpleCard : Card { public override string Type { get { return "Simple"; } } public virtual string Title { get; set; } public virtual string Content { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/UI/SsmlOutputSpeech.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; using System.Collections.Generic; namespace AlexaSkillsKit.UI { public class SsmlOutputSpeech : OutputSpeech { public override string Type { get { return "SSML"; } } public virtual string Ssml { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/UI/StandardCard.cs ================================================ // Copyright 2018 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. namespace AlexaSkillsKit.UI { public class StandardCard : Card { public override string Type { get { return "Standard"; } } public virtual string Title { get; set; } public virtual string Text { get; set; } public virtual Image Image { get; set; } } } ================================================ FILE: AlexaSkillsKit.Lib/app.config ================================================  ================================================ FILE: AlexaSkillsKit.Lib/packages.config ================================================  ================================================ FILE: AlexaSkillsKit.Sample/AlexaSkillsKit.Sample.csproj ================================================  Debug AnyCPU 2.0 {9FDEF793-5832-4D37-B0D5-62C55AB1C12E} {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library Properties Sample Sample v4.5 false true ..\ true true full false bin\ DEBUG;TRACE prompt 4 pdbonly true bin\ TRACE prompt 4 ..\packages\Antlr.3.5.0.2\lib\Antlr3.Runtime.dll True ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll True ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll True ..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll True ..\packages\Microsoft.AspNet.Identity.EntityFramework.2.2.1\lib\net45\Microsoft.AspNet.Identity.EntityFramework.dll True ..\packages\Microsoft.AspNet.Identity.Owin.2.2.1\lib\net45\Microsoft.AspNet.Identity.Owin.dll True ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll True ..\packages\Microsoft.Owin.Host.SystemWeb.3.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll True ..\packages\Microsoft.Owin.Security.3.0.1\lib\net45\Microsoft.Owin.Security.dll True ..\packages\Microsoft.Owin.Security.Cookies.3.0.1\lib\net45\Microsoft.Owin.Security.Cookies.dll True ..\packages\Microsoft.Owin.Security.Facebook.3.0.1\lib\net45\Microsoft.Owin.Security.Facebook.dll True ..\packages\Microsoft.Owin.Security.Google.3.0.1\lib\net45\Microsoft.Owin.Security.Google.dll True ..\packages\Microsoft.Owin.Security.MicrosoftAccount.3.0.1\lib\net45\Microsoft.Owin.Security.MicrosoftAccount.dll True ..\packages\Microsoft.Owin.Security.OAuth.3.0.1\lib\net45\Microsoft.Owin.Security.OAuth.dll True ..\packages\Microsoft.Owin.Security.Twitter.3.0.1\lib\net45\Microsoft.Owin.Security.Twitter.dll True False ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll True ..\packages\NLog.3.2.1\lib\net45\NLog.dll True ..\packages\Owin.1.0\lib\net40\Owin.dll ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll True ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.Helpers.dll True ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll True ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll True ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll True ..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll True ..\packages\Microsoft.AspNet.Web.Optimization.1.1.3\lib\net40\System.Web.Optimization.dll ..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll True ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.dll True ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Deployment.dll True ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Razor.dll True ..\packages\WebGrease.1.6.0\lib\WebGrease.dll True Global.asax Always Designer Web.config Web.config Designer {0ec882a8-aaca-4bd5-b449-72f20fdb8586} AlexaSkillsKit.Lib 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) True True 58080 / http://localhost:58080/ False False False ================================================ FILE: AlexaSkillsKit.Sample/App_Start/BundleConfig.cs ================================================ using System.Web; using System.Web.Optimization; namespace Sample { public class BundleConfig { // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862 public static void RegisterBundles(BundleCollection bundles) { bundles.Add(new ScriptBundle("~/bundles/jquery").Include( "~/Scripts/jquery-{version}.js")); // Use the development version of Modernizr to develop with and learn from. Then, when you're // ready for production, use the build tool at http://modernizr.com to pick only the tests you need. bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( "~/Scripts/modernizr-*")); bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( "~/Scripts/bootstrap.js", "~/Scripts/respond.js")); bundles.Add(new StyleBundle("~/Content/css").Include( "~/Content/bootstrap.css", "~/Content/site.css")); } } } ================================================ FILE: AlexaSkillsKit.Sample/App_Start/FilterConfig.cs ================================================ using System.Web; using System.Web.Mvc; namespace Sample { public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); } } } ================================================ FILE: AlexaSkillsKit.Sample/App_Start/IdentityConfig.cs ================================================ using System.Threading.Tasks; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin; using Sample.Models; namespace Sample { // Configure the application user manager used in this application. UserManager is defined in ASP.NET Identity and is used by the application. public class ApplicationUserManager : UserManager { public ApplicationUserManager(IUserStore store) : base(store) { } public static ApplicationUserManager Create(IdentityFactoryOptions options, IOwinContext context) { var manager = new ApplicationUserManager(new UserStore(context.Get())); // Configure validation logic for usernames manager.UserValidator = new UserValidator(manager) { AllowOnlyAlphanumericUserNames = false, RequireUniqueEmail = true }; // Configure validation logic for passwords manager.PasswordValidator = new PasswordValidator { RequiredLength = 6, RequireNonLetterOrDigit = true, RequireDigit = true, RequireLowercase = true, RequireUppercase = true, }; var dataProtectionProvider = options.DataProtectionProvider; if (dataProtectionProvider != null) { manager.UserTokenProvider = new DataProtectorTokenProvider(dataProtectionProvider.Create("ASP.NET Identity")); } return manager; } } } ================================================ FILE: AlexaSkillsKit.Sample/App_Start/RouteConfig.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace Sample { public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } } } ================================================ FILE: AlexaSkillsKit.Sample/App_Start/Startup.Auth.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.Owin; using Microsoft.Owin.Security.Cookies; using Microsoft.Owin.Security.Google; using Microsoft.Owin.Security.OAuth; using Owin; using Sample.Providers; using Sample.Models; namespace Sample { public partial class Startup { public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; } public static string PublicClientId { get; private set; } // For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864 public void ConfigureAuth(IAppBuilder app) { // Configure the db context and user manager to use a single instance per request app.CreatePerOwinContext(ApplicationDbContext.Create); app.CreatePerOwinContext(ApplicationUserManager.Create); // Enable the application to use a cookie to store information for the signed in user // and to use a cookie to temporarily store information about a user logging in with a third party login provider app.UseCookieAuthentication(new CookieAuthenticationOptions()); app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie); // Configure the application for OAuth based flow PublicClientId = "self"; OAuthOptions = new OAuthAuthorizationServerOptions { TokenEndpointPath = new PathString("/Token"), Provider = new ApplicationOAuthProvider(PublicClientId), AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"), AccessTokenExpireTimeSpan = TimeSpan.FromDays(14), AllowInsecureHttp = true }; // Enable the application to use bearer tokens to authenticate users app.UseOAuthBearerTokens(OAuthOptions); // Uncomment the following lines to enable logging in with third party login providers //app.UseMicrosoftAccountAuthentication( // clientId: "", // clientSecret: ""); //app.UseTwitterAuthentication( // consumerKey: "", // consumerSecret: ""); //app.UseFacebookAuthentication( // appId: "", // appSecret: ""); //app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions() //{ // ClientId = "", // ClientSecret = "" //}); } } } ================================================ FILE: AlexaSkillsKit.Sample/App_Start/WebApiConfig.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Web.Http; using Microsoft.Owin.Security.OAuth; using Newtonsoft.Json.Serialization; namespace Sample { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API configuration and services // Configure Web API to use only bearer token authentication. config.SuppressDefaultHostAuthentication(); config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType)); // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/ApiDescriptionExtensions.cs ================================================ using System; using System.Text; using System.Web; using System.Web.Http.Description; namespace Sample.Areas.HelpPage { public static class ApiDescriptionExtensions { /// /// Generates an URI-friendly ID for the . E.g. "Get-Values-id_name" instead of "GetValues/{id}?name={name}" /// /// The . /// The ID as a string. public static string GetFriendlyId(this ApiDescription description) { string path = description.RelativePath; string[] urlParts = path.Split('?'); string localPath = urlParts[0]; string queryKeyString = null; if (urlParts.Length > 1) { string query = urlParts[1]; string[] queryKeys = HttpUtility.ParseQueryString(query).AllKeys; queryKeyString = String.Join("_", queryKeys); } StringBuilder friendlyPath = new StringBuilder(); friendlyPath.AppendFormat("{0}-{1}", description.HttpMethod.Method, localPath.Replace("/", "-").Replace("{", String.Empty).Replace("}", String.Empty)); if (queryKeyString != null) { friendlyPath.AppendFormat("_{0}", queryKeyString.Replace('.', '-')); } return friendlyPath.ToString(); } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/App_Start/HelpPageConfig.cs ================================================ // Uncomment the following to provide samples for PageResult. Must also add the Microsoft.AspNet.WebApi.OData // package to your project. ////#define Handle_PageResultOfT using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net.Http.Headers; using System.Reflection; using System.Web; using System.Web.Http; #if Handle_PageResultOfT using System.Web.Http.OData; #endif namespace Sample.Areas.HelpPage { /// /// Use this class to customize the Help Page. /// For example you can set a custom to supply the documentation /// or you can provide the samples for the requests/responses. /// public static class HelpPageConfig { [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Sample.Areas.HelpPage.TextSample.#ctor(System.String)", Justification = "End users may choose to merge this string with existing localized resources.")] [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "bsonspec", Justification = "Part of a URI.")] public static void Register(HttpConfiguration config) { //// Uncomment the following to use the documentation from XML documentation file. //config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml"))); //// Uncomment the following to use "sample string" as the sample for all actions that have string as the body parameter or return type. //// Also, the string arrays will be used for IEnumerable. The sample objects will be serialized into different media type //// formats by the available formatters. //config.SetSampleObjects(new Dictionary //{ // {typeof(string), "sample string"}, // {typeof(IEnumerable), new string[]{"sample 1", "sample 2"}} //}); // Extend the following to provide factories for types not handled automatically (those lacking parameterless // constructors) or for which you prefer to use non-default property values. Line below provides a fallback // since automatic handling will fail and GeneratePageResult handles only a single type. #if Handle_PageResultOfT config.GetHelpPageSampleGenerator().SampleObjectFactories.Add(GeneratePageResult); #endif // Extend the following to use a preset object directly as the sample for all actions that support a media // type, regardless of the body parameter or return type. The lines below avoid display of binary content. // The BsonMediaTypeFormatter (if available) is not used to serialize the TextSample object. config.SetSampleForMediaType( new TextSample("Binary JSON content. See http://bsonspec.org for details."), new MediaTypeHeaderValue("application/bson")); //// Uncomment the following to use "[0]=foo&[1]=bar" directly as the sample for all actions that support form URL encoded format //// and have IEnumerable as the body parameter or return type. //config.SetSampleForType("[0]=foo&[1]=bar", new MediaTypeHeaderValue("application/x-www-form-urlencoded"), typeof(IEnumerable)); //// Uncomment the following to use "1234" directly as the request sample for media type "text/plain" on the controller named "Values" //// and action named "Put". //config.SetSampleRequest("1234", new MediaTypeHeaderValue("text/plain"), "Values", "Put"); //// Uncomment the following to use the image on "../images/aspNetHome.png" directly as the response sample for media type "image/png" //// on the controller named "Values" and action named "Get" with parameter "id". //config.SetSampleResponse(new ImageSample("../images/aspNetHome.png"), new MediaTypeHeaderValue("image/png"), "Values", "Get", "id"); //// Uncomment the following to correct the sample request when the action expects an HttpRequestMessage with ObjectContent. //// The sample will be generated as if the controller named "Values" and action named "Get" were having string as the body parameter. //config.SetActualRequestType(typeof(string), "Values", "Get"); //// Uncomment the following to correct the sample response when the action returns an HttpResponseMessage with ObjectContent. //// The sample will be generated as if the controller named "Values" and action named "Post" were returning a string. //config.SetActualResponseType(typeof(string), "Values", "Post"); } #if Handle_PageResultOfT private static object GeneratePageResult(HelpPageSampleGenerator sampleGenerator, Type type) { if (type.IsGenericType) { Type openGenericType = type.GetGenericTypeDefinition(); if (openGenericType == typeof(PageResult<>)) { // Get the T in PageResult Type[] typeParameters = type.GetGenericArguments(); Debug.Assert(typeParameters.Length == 1); // Create an enumeration to pass as the first parameter to the PageResult constuctor Type itemsType = typeof(List<>).MakeGenericType(typeParameters); object items = sampleGenerator.GetSampleObject(itemsType); // Fill in the other information needed to invoke the PageResult constuctor Type[] parameterTypes = new Type[] { itemsType, typeof(Uri), typeof(long?), }; object[] parameters = new object[] { items, null, (long)ObjectGenerator.DefaultCollectionSize, }; // Call PageResult(IEnumerable items, Uri nextPageLink, long? count) constructor ConstructorInfo constructor = type.GetConstructor(parameterTypes); return constructor.Invoke(parameters); } } return null; } #endif } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Controllers/HelpController.cs ================================================ using System; using System.Web.Http; using System.Web.Mvc; using Sample.Areas.HelpPage.ModelDescriptions; using Sample.Areas.HelpPage.Models; namespace Sample.Areas.HelpPage.Controllers { /// /// The controller that will handle requests for the help page. /// public class HelpController : Controller { private const string ErrorViewName = "Error"; public HelpController() : this(GlobalConfiguration.Configuration) { } public HelpController(HttpConfiguration config) { Configuration = config; } public HttpConfiguration Configuration { get; private set; } public ActionResult Index() { ViewBag.DocumentationProvider = Configuration.Services.GetDocumentationProvider(); return View(Configuration.Services.GetApiExplorer().ApiDescriptions); } public ActionResult Api(string apiId) { if (!String.IsNullOrEmpty(apiId)) { HelpPageApiModel apiModel = Configuration.GetHelpPageApiModel(apiId); if (apiModel != null) { return View(apiModel); } } return View(ErrorViewName); } public ActionResult ResourceModel(string modelName) { if (!String.IsNullOrEmpty(modelName)) { ModelDescriptionGenerator modelDescriptionGenerator = Configuration.GetModelDescriptionGenerator(); ModelDescription modelDescription; if (modelDescriptionGenerator.GeneratedModels.TryGetValue(modelName, out modelDescription)) { return View(modelDescription); } } return View(ErrorViewName); } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/HelpPage.css ================================================ .help-page h1, .help-page .h1, .help-page h2, .help-page .h2, .help-page h3, .help-page .h3, #body.help-page, .help-page-table th, .help-page-table pre, .help-page-table p { font-family: "Segoe UI Light", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif; } .help-page pre.wrapped { white-space: -moz-pre-wrap; white-space: -pre-wrap; white-space: -o-pre-wrap; white-space: pre-wrap; } .help-page .warning-message-container { margin-top: 20px; padding: 0 10px; color: #525252; background: #EFDCA9; border: 1px solid #CCCCCC; } .help-page-table { width: 100%; border-collapse: collapse; text-align: left; margin: 0px 0px 20px 0px; border-top: 1px solid #D4D4D4; } .help-page-table th { text-align: left; font-weight: bold; border-bottom: 1px solid #D4D4D4; padding: 5px 6px 5px 6px; } .help-page-table td { border-bottom: 1px solid #D4D4D4; padding: 10px 8px 10px 8px; vertical-align: top; } .help-page-table pre, .help-page-table p { margin: 0px; padding: 0px; font-family: inherit; font-size: 100%; } .help-page-table tbody tr:hover td { background-color: #F3F3F3; } .help-page a:hover { background-color: transparent; } .help-page .sample-header { border: 2px solid #D4D4D4; background: #00497E; color: #FFFFFF; padding: 8px 15px; border-bottom: none; display: inline-block; margin: 10px 0px 0px 0px; } .help-page .sample-content { display: block; border-width: 0; padding: 15px 20px; background: #FFFFFF; border: 2px solid #D4D4D4; margin: 0px 0px 10px 0px; } .help-page .api-name { width: 40%; } .help-page .api-documentation { width: 60%; } .help-page .parameter-name { width: 20%; } .help-page .parameter-documentation { width: 40%; } .help-page .parameter-type { width: 20%; } .help-page .parameter-annotations { width: 20%; } .help-page h1, .help-page .h1 { font-size: 36px; line-height: normal; } .help-page h2, .help-page .h2 { font-size: 24px; } .help-page h3, .help-page .h3 { font-size: 20px; } #body.help-page { font-size: 14px; line-height: 143%; color: #333; } .help-page a { color: #0000EE; text-decoration: none; } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/HelpPageAreaRegistration.cs ================================================ using System.Web.Http; using System.Web.Mvc; namespace Sample.Areas.HelpPage { public class HelpPageAreaRegistration : AreaRegistration { public override string AreaName { get { return "HelpPage"; } } public override void RegisterArea(AreaRegistrationContext context) { context.MapRoute( "HelpPage_Default", "Help/{action}/{apiId}", new { controller = "Help", action = "Index", apiId = UrlParameter.Optional }); HelpPageConfig.Register(GlobalConfiguration.Configuration); } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/HelpPageConfigurationExtensions.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.Description; using Sample.Areas.HelpPage.ModelDescriptions; using Sample.Areas.HelpPage.Models; namespace Sample.Areas.HelpPage { public static class HelpPageConfigurationExtensions { private const string ApiModelPrefix = "MS_HelpPageApiModel_"; /// /// Sets the documentation provider for help page. /// /// The . /// The documentation provider. public static void SetDocumentationProvider(this HttpConfiguration config, IDocumentationProvider documentationProvider) { config.Services.Replace(typeof(IDocumentationProvider), documentationProvider); } /// /// Sets the objects that will be used by the formatters to produce sample requests/responses. /// /// The . /// The sample objects. public static void SetSampleObjects(this HttpConfiguration config, IDictionary sampleObjects) { config.GetHelpPageSampleGenerator().SampleObjects = sampleObjects; } /// /// Sets the sample request directly for the specified media type and action. /// /// The . /// The sample request. /// The media type. /// Name of the controller. /// Name of the action. public static void SetSampleRequest(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName) { config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Request, controllerName, actionName, new[] { "*" }), sample); } /// /// Sets the sample request directly for the specified media type and action with parameters. /// /// The . /// The sample request. /// The media type. /// Name of the controller. /// Name of the action. /// The parameter names. public static void SetSampleRequest(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName, params string[] parameterNames) { config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Request, controllerName, actionName, parameterNames), sample); } /// /// Sets the sample request directly for the specified media type of the action. /// /// The . /// The sample response. /// The media type. /// Name of the controller. /// Name of the action. public static void SetSampleResponse(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName) { config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Response, controllerName, actionName, new[] { "*" }), sample); } /// /// Sets the sample response directly for the specified media type of the action with specific parameters. /// /// The . /// The sample response. /// The media type. /// Name of the controller. /// Name of the action. /// The parameter names. public static void SetSampleResponse(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName, params string[] parameterNames) { config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Response, controllerName, actionName, parameterNames), sample); } /// /// Sets the sample directly for all actions with the specified media type. /// /// The . /// The sample. /// The media type. public static void SetSampleForMediaType(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType) { config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType), sample); } /// /// Sets the sample directly for all actions with the specified type and media type. /// /// The . /// The sample. /// The media type. /// The parameter type or return type of an action. public static void SetSampleForType(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, Type type) { config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, type), sample); } /// /// Specifies the actual type of passed to the in an action. /// The help page will use this information to produce more accurate request samples. /// /// The . /// The type. /// Name of the controller. /// Name of the action. public static void SetActualRequestType(this HttpConfiguration config, Type type, string controllerName, string actionName) { config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Request, controllerName, actionName, new[] { "*" }), type); } /// /// Specifies the actual type of passed to the in an action. /// The help page will use this information to produce more accurate request samples. /// /// The . /// The type. /// Name of the controller. /// Name of the action. /// The parameter names. public static void SetActualRequestType(this HttpConfiguration config, Type type, string controllerName, string actionName, params string[] parameterNames) { config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Request, controllerName, actionName, parameterNames), type); } /// /// Specifies the actual type of returned as part of the in an action. /// The help page will use this information to produce more accurate response samples. /// /// The . /// The type. /// Name of the controller. /// Name of the action. public static void SetActualResponseType(this HttpConfiguration config, Type type, string controllerName, string actionName) { config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Response, controllerName, actionName, new[] { "*" }), type); } /// /// Specifies the actual type of returned as part of the in an action. /// The help page will use this information to produce more accurate response samples. /// /// The . /// The type. /// Name of the controller. /// Name of the action. /// The parameter names. public static void SetActualResponseType(this HttpConfiguration config, Type type, string controllerName, string actionName, params string[] parameterNames) { config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Response, controllerName, actionName, parameterNames), type); } /// /// Gets the help page sample generator. /// /// The . /// The help page sample generator. public static HelpPageSampleGenerator GetHelpPageSampleGenerator(this HttpConfiguration config) { return (HelpPageSampleGenerator)config.Properties.GetOrAdd( typeof(HelpPageSampleGenerator), k => new HelpPageSampleGenerator()); } /// /// Sets the help page sample generator. /// /// The . /// The help page sample generator. public static void SetHelpPageSampleGenerator(this HttpConfiguration config, HelpPageSampleGenerator sampleGenerator) { config.Properties.AddOrUpdate( typeof(HelpPageSampleGenerator), k => sampleGenerator, (k, o) => sampleGenerator); } /// /// Gets the model description generator. /// /// The configuration. /// The public static ModelDescriptionGenerator GetModelDescriptionGenerator(this HttpConfiguration config) { return (ModelDescriptionGenerator)config.Properties.GetOrAdd( typeof(ModelDescriptionGenerator), k => InitializeModelDescriptionGenerator(config)); } /// /// Gets the model that represents an API displayed on the help page. The model is initialized on the first call and cached for subsequent calls. /// /// The . /// The ID. /// /// An /// public static HelpPageApiModel GetHelpPageApiModel(this HttpConfiguration config, string apiDescriptionId) { object model; string modelId = ApiModelPrefix + apiDescriptionId; if (!config.Properties.TryGetValue(modelId, out model)) { Collection apiDescriptions = config.Services.GetApiExplorer().ApiDescriptions; ApiDescription apiDescription = apiDescriptions.FirstOrDefault(api => String.Equals(api.GetFriendlyId(), apiDescriptionId, StringComparison.OrdinalIgnoreCase)); if (apiDescription != null) { model = GenerateApiModel(apiDescription, config); config.Properties.TryAdd(modelId, model); } } return (HelpPageApiModel)model; } private static HelpPageApiModel GenerateApiModel(ApiDescription apiDescription, HttpConfiguration config) { HelpPageApiModel apiModel = new HelpPageApiModel() { ApiDescription = apiDescription, }; ModelDescriptionGenerator modelGenerator = config.GetModelDescriptionGenerator(); HelpPageSampleGenerator sampleGenerator = config.GetHelpPageSampleGenerator(); GenerateUriParameters(apiModel, modelGenerator); GenerateRequestModelDescription(apiModel, modelGenerator, sampleGenerator); GenerateResourceDescription(apiModel, modelGenerator); GenerateSamples(apiModel, sampleGenerator); return apiModel; } private static void GenerateUriParameters(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator) { ApiDescription apiDescription = apiModel.ApiDescription; foreach (ApiParameterDescription apiParameter in apiDescription.ParameterDescriptions) { if (apiParameter.Source == ApiParameterSource.FromUri) { HttpParameterDescriptor parameterDescriptor = apiParameter.ParameterDescriptor; Type parameterType = null; ModelDescription typeDescription = null; ComplexTypeModelDescription complexTypeDescription = null; if (parameterDescriptor != null) { parameterType = parameterDescriptor.ParameterType; typeDescription = modelGenerator.GetOrCreateModelDescription(parameterType); complexTypeDescription = typeDescription as ComplexTypeModelDescription; } // Example: // [TypeConverter(typeof(PointConverter))] // public class Point // { // public Point(int x, int y) // { // X = x; // Y = y; // } // public int X { get; set; } // public int Y { get; set; } // } // Class Point is bindable with a TypeConverter, so Point will be added to UriParameters collection. // // public class Point // { // public int X { get; set; } // public int Y { get; set; } // } // Regular complex class Point will have properties X and Y added to UriParameters collection. if (complexTypeDescription != null && !IsBindableWithTypeConverter(parameterType)) { foreach (ParameterDescription uriParameter in complexTypeDescription.Properties) { apiModel.UriParameters.Add(uriParameter); } } else if (parameterDescriptor != null) { ParameterDescription uriParameter = AddParameterDescription(apiModel, apiParameter, typeDescription); if (!parameterDescriptor.IsOptional) { uriParameter.Annotations.Add(new ParameterAnnotation() { Documentation = "Required" }); } object defaultValue = parameterDescriptor.DefaultValue; if (defaultValue != null) { uriParameter.Annotations.Add(new ParameterAnnotation() { Documentation = "Default value is " + Convert.ToString(defaultValue, CultureInfo.InvariantCulture) }); } } else { Debug.Assert(parameterDescriptor == null); // If parameterDescriptor is null, this is an undeclared route parameter which only occurs // when source is FromUri. Ignored in request model and among resource parameters but listed // as a simple string here. ModelDescription modelDescription = modelGenerator.GetOrCreateModelDescription(typeof(string)); AddParameterDescription(apiModel, apiParameter, modelDescription); } } } } private static bool IsBindableWithTypeConverter(Type parameterType) { if (parameterType == null) { return false; } return TypeDescriptor.GetConverter(parameterType).CanConvertFrom(typeof(string)); } private static ParameterDescription AddParameterDescription(HelpPageApiModel apiModel, ApiParameterDescription apiParameter, ModelDescription typeDescription) { ParameterDescription parameterDescription = new ParameterDescription { Name = apiParameter.Name, Documentation = apiParameter.Documentation, TypeDescription = typeDescription, }; apiModel.UriParameters.Add(parameterDescription); return parameterDescription; } private static void GenerateRequestModelDescription(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator, HelpPageSampleGenerator sampleGenerator) { ApiDescription apiDescription = apiModel.ApiDescription; foreach (ApiParameterDescription apiParameter in apiDescription.ParameterDescriptions) { if (apiParameter.Source == ApiParameterSource.FromBody) { Type parameterType = apiParameter.ParameterDescriptor.ParameterType; apiModel.RequestModelDescription = modelGenerator.GetOrCreateModelDescription(parameterType); apiModel.RequestDocumentation = apiParameter.Documentation; } else if (apiParameter.ParameterDescriptor != null && apiParameter.ParameterDescriptor.ParameterType == typeof(HttpRequestMessage)) { Type parameterType = sampleGenerator.ResolveHttpRequestMessageType(apiDescription); if (parameterType != null) { apiModel.RequestModelDescription = modelGenerator.GetOrCreateModelDescription(parameterType); } } } } private static void GenerateResourceDescription(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator) { ResponseDescription response = apiModel.ApiDescription.ResponseDescription; Type responseType = response.ResponseType ?? response.DeclaredType; if (responseType != null && responseType != typeof(void)) { apiModel.ResourceDescription = modelGenerator.GetOrCreateModelDescription(responseType); } } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The exception is recorded as ErrorMessages.")] private static void GenerateSamples(HelpPageApiModel apiModel, HelpPageSampleGenerator sampleGenerator) { try { foreach (var item in sampleGenerator.GetSampleRequests(apiModel.ApiDescription)) { apiModel.SampleRequests.Add(item.Key, item.Value); LogInvalidSampleAsError(apiModel, item.Value); } foreach (var item in sampleGenerator.GetSampleResponses(apiModel.ApiDescription)) { apiModel.SampleResponses.Add(item.Key, item.Value); LogInvalidSampleAsError(apiModel, item.Value); } } catch (Exception e) { apiModel.ErrorMessages.Add(String.Format(CultureInfo.CurrentCulture, "An exception has occurred while generating the sample. Exception message: {0}", HelpPageSampleGenerator.UnwrapException(e).Message)); } } private static bool TryGetResourceParameter(ApiDescription apiDescription, HttpConfiguration config, out ApiParameterDescription parameterDescription, out Type resourceType) { parameterDescription = apiDescription.ParameterDescriptions.FirstOrDefault( p => p.Source == ApiParameterSource.FromBody || (p.ParameterDescriptor != null && p.ParameterDescriptor.ParameterType == typeof(HttpRequestMessage))); if (parameterDescription == null) { resourceType = null; return false; } resourceType = parameterDescription.ParameterDescriptor.ParameterType; if (resourceType == typeof(HttpRequestMessage)) { HelpPageSampleGenerator sampleGenerator = config.GetHelpPageSampleGenerator(); resourceType = sampleGenerator.ResolveHttpRequestMessageType(apiDescription); } if (resourceType == null) { parameterDescription = null; return false; } return true; } private static ModelDescriptionGenerator InitializeModelDescriptionGenerator(HttpConfiguration config) { ModelDescriptionGenerator modelGenerator = new ModelDescriptionGenerator(config); Collection apis = config.Services.GetApiExplorer().ApiDescriptions; foreach (ApiDescription api in apis) { ApiParameterDescription parameterDescription; Type parameterType; if (TryGetResourceParameter(api, config, out parameterDescription, out parameterType)) { modelGenerator.GetOrCreateModelDescription(parameterType); } } return modelGenerator; } private static void LogInvalidSampleAsError(HelpPageApiModel apiModel, object sample) { InvalidSample invalidSample = sample as InvalidSample; if (invalidSample != null) { apiModel.ErrorMessages.Add(invalidSample.ErrorMessage); } } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/ModelDescriptions/CollectionModelDescription.cs ================================================ namespace Sample.Areas.HelpPage.ModelDescriptions { public class CollectionModelDescription : ModelDescription { public ModelDescription ElementDescription { get; set; } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/ModelDescriptions/ComplexTypeModelDescription.cs ================================================ using System.Collections.ObjectModel; namespace Sample.Areas.HelpPage.ModelDescriptions { public class ComplexTypeModelDescription : ModelDescription { public ComplexTypeModelDescription() { Properties = new Collection(); } public Collection Properties { get; private set; } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/ModelDescriptions/DictionaryModelDescription.cs ================================================ namespace Sample.Areas.HelpPage.ModelDescriptions { public class DictionaryModelDescription : KeyValuePairModelDescription { } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/ModelDescriptions/EnumTypeModelDescription.cs ================================================ using System.Collections.Generic; using System.Collections.ObjectModel; namespace Sample.Areas.HelpPage.ModelDescriptions { public class EnumTypeModelDescription : ModelDescription { public EnumTypeModelDescription() { Values = new Collection(); } public Collection Values { get; private set; } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/ModelDescriptions/EnumValueDescription.cs ================================================ namespace Sample.Areas.HelpPage.ModelDescriptions { public class EnumValueDescription { public string Documentation { get; set; } public string Name { get; set; } public string Value { get; set; } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/ModelDescriptions/IModelDocumentationProvider.cs ================================================ using System; using System.Reflection; namespace Sample.Areas.HelpPage.ModelDescriptions { public interface IModelDocumentationProvider { string GetDocumentation(MemberInfo member); string GetDocumentation(Type type); } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/ModelDescriptions/KeyValuePairModelDescription.cs ================================================ namespace Sample.Areas.HelpPage.ModelDescriptions { public class KeyValuePairModelDescription : ModelDescription { public ModelDescription KeyModelDescription { get; set; } public ModelDescription ValueModelDescription { get; set; } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/ModelDescriptions/ModelDescription.cs ================================================ using System; namespace Sample.Areas.HelpPage.ModelDescriptions { /// /// Describes a type model. /// public abstract class ModelDescription { public string Documentation { get; set; } public Type ModelType { get; set; } public string Name { get; set; } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/ModelDescriptions/ModelDescriptionGenerator.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Reflection; using System.Runtime.Serialization; using System.Web.Http; using System.Web.Http.Description; using System.Xml.Serialization; using Newtonsoft.Json; namespace Sample.Areas.HelpPage.ModelDescriptions { /// /// Generates model descriptions for given types. /// public class ModelDescriptionGenerator { // Modify this to support more data annotation attributes. private readonly IDictionary> AnnotationTextGenerator = new Dictionary> { { typeof(RequiredAttribute), a => "Required" }, { typeof(RangeAttribute), a => { RangeAttribute range = (RangeAttribute)a; return String.Format(CultureInfo.CurrentCulture, "Range: inclusive between {0} and {1}", range.Minimum, range.Maximum); } }, { typeof(MaxLengthAttribute), a => { MaxLengthAttribute maxLength = (MaxLengthAttribute)a; return String.Format(CultureInfo.CurrentCulture, "Max length: {0}", maxLength.Length); } }, { typeof(MinLengthAttribute), a => { MinLengthAttribute minLength = (MinLengthAttribute)a; return String.Format(CultureInfo.CurrentCulture, "Min length: {0}", minLength.Length); } }, { typeof(StringLengthAttribute), a => { StringLengthAttribute strLength = (StringLengthAttribute)a; return String.Format(CultureInfo.CurrentCulture, "String length: inclusive between {0} and {1}", strLength.MinimumLength, strLength.MaximumLength); } }, { typeof(DataTypeAttribute), a => { DataTypeAttribute dataType = (DataTypeAttribute)a; return String.Format(CultureInfo.CurrentCulture, "Data type: {0}", dataType.CustomDataType ?? dataType.DataType.ToString()); } }, { typeof(RegularExpressionAttribute), a => { RegularExpressionAttribute regularExpression = (RegularExpressionAttribute)a; return String.Format(CultureInfo.CurrentCulture, "Matching regular expression pattern: {0}", regularExpression.Pattern); } }, }; // Modify this to add more default documentations. private readonly IDictionary DefaultTypeDocumentation = new Dictionary { { typeof(Int16), "integer" }, { typeof(Int32), "integer" }, { typeof(Int64), "integer" }, { typeof(UInt16), "unsigned integer" }, { typeof(UInt32), "unsigned integer" }, { typeof(UInt64), "unsigned integer" }, { typeof(Byte), "byte" }, { typeof(Char), "character" }, { typeof(SByte), "signed byte" }, { typeof(Uri), "URI" }, { typeof(Single), "decimal number" }, { typeof(Double), "decimal number" }, { typeof(Decimal), "decimal number" }, { typeof(String), "string" }, { typeof(Guid), "globally unique identifier" }, { typeof(TimeSpan), "time interval" }, { typeof(DateTime), "date" }, { typeof(DateTimeOffset), "date" }, { typeof(Boolean), "boolean" }, }; private Lazy _documentationProvider; public ModelDescriptionGenerator(HttpConfiguration config) { if (config == null) { throw new ArgumentNullException("config"); } _documentationProvider = new Lazy(() => config.Services.GetDocumentationProvider() as IModelDocumentationProvider); GeneratedModels = new Dictionary(StringComparer.OrdinalIgnoreCase); } public Dictionary GeneratedModels { get; private set; } private IModelDocumentationProvider DocumentationProvider { get { return _documentationProvider.Value; } } public ModelDescription GetOrCreateModelDescription(Type modelType) { if (modelType == null) { throw new ArgumentNullException("modelType"); } Type underlyingType = Nullable.GetUnderlyingType(modelType); if (underlyingType != null) { modelType = underlyingType; } ModelDescription modelDescription; string modelName = ModelNameHelper.GetModelName(modelType); if (GeneratedModels.TryGetValue(modelName, out modelDescription)) { if (modelType != modelDescription.ModelType) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, "A model description could not be created. Duplicate model name '{0}' was found for types '{1}' and '{2}'. " + "Use the [ModelName] attribute to change the model name for at least one of the types so that it has a unique name.", modelName, modelDescription.ModelType.FullName, modelType.FullName)); } return modelDescription; } if (DefaultTypeDocumentation.ContainsKey(modelType)) { return GenerateSimpleTypeModelDescription(modelType); } if (modelType.IsEnum) { return GenerateEnumTypeModelDescription(modelType); } if (modelType.IsGenericType) { Type[] genericArguments = modelType.GetGenericArguments(); if (genericArguments.Length == 1) { Type enumerableType = typeof(IEnumerable<>).MakeGenericType(genericArguments); if (enumerableType.IsAssignableFrom(modelType)) { return GenerateCollectionModelDescription(modelType, genericArguments[0]); } } if (genericArguments.Length == 2) { Type dictionaryType = typeof(IDictionary<,>).MakeGenericType(genericArguments); if (dictionaryType.IsAssignableFrom(modelType)) { return GenerateDictionaryModelDescription(modelType, genericArguments[0], genericArguments[1]); } Type keyValuePairType = typeof(KeyValuePair<,>).MakeGenericType(genericArguments); if (keyValuePairType.IsAssignableFrom(modelType)) { return GenerateKeyValuePairModelDescription(modelType, genericArguments[0], genericArguments[1]); } } } if (modelType.IsArray) { Type elementType = modelType.GetElementType(); return GenerateCollectionModelDescription(modelType, elementType); } if (modelType == typeof(NameValueCollection)) { return GenerateDictionaryModelDescription(modelType, typeof(string), typeof(string)); } if (typeof(IDictionary).IsAssignableFrom(modelType)) { return GenerateDictionaryModelDescription(modelType, typeof(object), typeof(object)); } if (typeof(IEnumerable).IsAssignableFrom(modelType)) { return GenerateCollectionModelDescription(modelType, typeof(object)); } return GenerateComplexTypeModelDescription(modelType); } // Change this to provide different name for the member. private static string GetMemberName(MemberInfo member, bool hasDataContractAttribute) { JsonPropertyAttribute jsonProperty = member.GetCustomAttribute(); if (jsonProperty != null && !String.IsNullOrEmpty(jsonProperty.PropertyName)) { return jsonProperty.PropertyName; } if (hasDataContractAttribute) { DataMemberAttribute dataMember = member.GetCustomAttribute(); if (dataMember != null && !String.IsNullOrEmpty(dataMember.Name)) { return dataMember.Name; } } return member.Name; } private static bool ShouldDisplayMember(MemberInfo member, bool hasDataContractAttribute) { JsonIgnoreAttribute jsonIgnore = member.GetCustomAttribute(); XmlIgnoreAttribute xmlIgnore = member.GetCustomAttribute(); IgnoreDataMemberAttribute ignoreDataMember = member.GetCustomAttribute(); NonSerializedAttribute nonSerialized = member.GetCustomAttribute(); ApiExplorerSettingsAttribute apiExplorerSetting = member.GetCustomAttribute(); bool hasMemberAttribute = member.DeclaringType.IsEnum ? member.GetCustomAttribute() != null : member.GetCustomAttribute() != null; // Display member only if all the followings are true: // no JsonIgnoreAttribute // no XmlIgnoreAttribute // no IgnoreDataMemberAttribute // no NonSerializedAttribute // no ApiExplorerSettingsAttribute with IgnoreApi set to true // no DataContractAttribute without DataMemberAttribute or EnumMemberAttribute return jsonIgnore == null && xmlIgnore == null && ignoreDataMember == null && nonSerialized == null && (apiExplorerSetting == null || !apiExplorerSetting.IgnoreApi) && (!hasDataContractAttribute || hasMemberAttribute); } private string CreateDefaultDocumentation(Type type) { string documentation; if (DefaultTypeDocumentation.TryGetValue(type, out documentation)) { return documentation; } if (DocumentationProvider != null) { documentation = DocumentationProvider.GetDocumentation(type); } return documentation; } private void GenerateAnnotations(MemberInfo property, ParameterDescription propertyModel) { List annotations = new List(); IEnumerable attributes = property.GetCustomAttributes(); foreach (Attribute attribute in attributes) { Func textGenerator; if (AnnotationTextGenerator.TryGetValue(attribute.GetType(), out textGenerator)) { annotations.Add( new ParameterAnnotation { AnnotationAttribute = attribute, Documentation = textGenerator(attribute) }); } } // Rearrange the annotations annotations.Sort((x, y) => { // Special-case RequiredAttribute so that it shows up on top if (x.AnnotationAttribute is RequiredAttribute) { return -1; } if (y.AnnotationAttribute is RequiredAttribute) { return 1; } // Sort the rest based on alphabetic order of the documentation return String.Compare(x.Documentation, y.Documentation, StringComparison.OrdinalIgnoreCase); }); foreach (ParameterAnnotation annotation in annotations) { propertyModel.Annotations.Add(annotation); } } private CollectionModelDescription GenerateCollectionModelDescription(Type modelType, Type elementType) { ModelDescription collectionModelDescription = GetOrCreateModelDescription(elementType); if (collectionModelDescription != null) { return new CollectionModelDescription { Name = ModelNameHelper.GetModelName(modelType), ModelType = modelType, ElementDescription = collectionModelDescription }; } return null; } private ModelDescription GenerateComplexTypeModelDescription(Type modelType) { ComplexTypeModelDescription complexModelDescription = new ComplexTypeModelDescription { Name = ModelNameHelper.GetModelName(modelType), ModelType = modelType, Documentation = CreateDefaultDocumentation(modelType) }; GeneratedModels.Add(complexModelDescription.Name, complexModelDescription); bool hasDataContractAttribute = modelType.GetCustomAttribute() != null; PropertyInfo[] properties = modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (PropertyInfo property in properties) { if (ShouldDisplayMember(property, hasDataContractAttribute)) { ParameterDescription propertyModel = new ParameterDescription { Name = GetMemberName(property, hasDataContractAttribute) }; if (DocumentationProvider != null) { propertyModel.Documentation = DocumentationProvider.GetDocumentation(property); } GenerateAnnotations(property, propertyModel); complexModelDescription.Properties.Add(propertyModel); propertyModel.TypeDescription = GetOrCreateModelDescription(property.PropertyType); } } FieldInfo[] fields = modelType.GetFields(BindingFlags.Public | BindingFlags.Instance); foreach (FieldInfo field in fields) { if (ShouldDisplayMember(field, hasDataContractAttribute)) { ParameterDescription propertyModel = new ParameterDescription { Name = GetMemberName(field, hasDataContractAttribute) }; if (DocumentationProvider != null) { propertyModel.Documentation = DocumentationProvider.GetDocumentation(field); } complexModelDescription.Properties.Add(propertyModel); propertyModel.TypeDescription = GetOrCreateModelDescription(field.FieldType); } } return complexModelDescription; } private DictionaryModelDescription GenerateDictionaryModelDescription(Type modelType, Type keyType, Type valueType) { ModelDescription keyModelDescription = GetOrCreateModelDescription(keyType); ModelDescription valueModelDescription = GetOrCreateModelDescription(valueType); return new DictionaryModelDescription { Name = ModelNameHelper.GetModelName(modelType), ModelType = modelType, KeyModelDescription = keyModelDescription, ValueModelDescription = valueModelDescription }; } private EnumTypeModelDescription GenerateEnumTypeModelDescription(Type modelType) { EnumTypeModelDescription enumDescription = new EnumTypeModelDescription { Name = ModelNameHelper.GetModelName(modelType), ModelType = modelType, Documentation = CreateDefaultDocumentation(modelType) }; bool hasDataContractAttribute = modelType.GetCustomAttribute() != null; foreach (FieldInfo field in modelType.GetFields(BindingFlags.Public | BindingFlags.Static)) { if (ShouldDisplayMember(field, hasDataContractAttribute)) { EnumValueDescription enumValue = new EnumValueDescription { Name = field.Name, Value = field.GetRawConstantValue().ToString() }; if (DocumentationProvider != null) { enumValue.Documentation = DocumentationProvider.GetDocumentation(field); } enumDescription.Values.Add(enumValue); } } GeneratedModels.Add(enumDescription.Name, enumDescription); return enumDescription; } private KeyValuePairModelDescription GenerateKeyValuePairModelDescription(Type modelType, Type keyType, Type valueType) { ModelDescription keyModelDescription = GetOrCreateModelDescription(keyType); ModelDescription valueModelDescription = GetOrCreateModelDescription(valueType); return new KeyValuePairModelDescription { Name = ModelNameHelper.GetModelName(modelType), ModelType = modelType, KeyModelDescription = keyModelDescription, ValueModelDescription = valueModelDescription }; } private ModelDescription GenerateSimpleTypeModelDescription(Type modelType) { SimpleTypeModelDescription simpleModelDescription = new SimpleTypeModelDescription { Name = ModelNameHelper.GetModelName(modelType), ModelType = modelType, Documentation = CreateDefaultDocumentation(modelType) }; GeneratedModels.Add(simpleModelDescription.Name, simpleModelDescription); return simpleModelDescription; } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/ModelDescriptions/ModelNameAttribute.cs ================================================ using System; namespace Sample.Areas.HelpPage.ModelDescriptions { /// /// Use this attribute to change the name of the generated for a type. /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum, AllowMultiple = false, Inherited = false)] public sealed class ModelNameAttribute : Attribute { public ModelNameAttribute(string name) { Name = name; } public string Name { get; private set; } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/ModelDescriptions/ModelNameHelper.cs ================================================ using System; using System.Globalization; using System.Linq; using System.Reflection; namespace Sample.Areas.HelpPage.ModelDescriptions { internal static class ModelNameHelper { // Modify this to provide custom model name mapping. public static string GetModelName(Type type) { ModelNameAttribute modelNameAttribute = type.GetCustomAttribute(); if (modelNameAttribute != null && !String.IsNullOrEmpty(modelNameAttribute.Name)) { return modelNameAttribute.Name; } string modelName = type.Name; if (type.IsGenericType) { // Format the generic type name to something like: GenericOfAgurment1AndArgument2 Type genericType = type.GetGenericTypeDefinition(); Type[] genericArguments = type.GetGenericArguments(); string genericTypeName = genericType.Name; // Trim the generic parameter counts from the name genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf('`')); string[] argumentTypeNames = genericArguments.Select(t => GetModelName(t)).ToArray(); modelName = String.Format(CultureInfo.InvariantCulture, "{0}Of{1}", genericTypeName, String.Join("And", argumentTypeNames)); } return modelName; } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/ModelDescriptions/ParameterAnnotation.cs ================================================ using System; namespace Sample.Areas.HelpPage.ModelDescriptions { public class ParameterAnnotation { public Attribute AnnotationAttribute { get; set; } public string Documentation { get; set; } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/ModelDescriptions/ParameterDescription.cs ================================================ using System.Collections.Generic; using System.Collections.ObjectModel; namespace Sample.Areas.HelpPage.ModelDescriptions { public class ParameterDescription { public ParameterDescription() { Annotations = new Collection(); } public Collection Annotations { get; private set; } public string Documentation { get; set; } public string Name { get; set; } public ModelDescription TypeDescription { get; set; } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/ModelDescriptions/SimpleTypeModelDescription.cs ================================================ namespace Sample.Areas.HelpPage.ModelDescriptions { public class SimpleTypeModelDescription : ModelDescription { } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Models/HelpPageApiModel.cs ================================================ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Net.Http.Headers; using System.Web.Http.Description; using Sample.Areas.HelpPage.ModelDescriptions; namespace Sample.Areas.HelpPage.Models { /// /// The model that represents an API displayed on the help page. /// public class HelpPageApiModel { /// /// Initializes a new instance of the class. /// public HelpPageApiModel() { UriParameters = new Collection(); SampleRequests = new Dictionary(); SampleResponses = new Dictionary(); ErrorMessages = new Collection(); } /// /// Gets or sets the that describes the API. /// public ApiDescription ApiDescription { get; set; } /// /// Gets or sets the collection that describes the URI parameters for the API. /// public Collection UriParameters { get; private set; } /// /// Gets or sets the documentation for the request. /// public string RequestDocumentation { get; set; } /// /// Gets or sets the that describes the request body. /// public ModelDescription RequestModelDescription { get; set; } /// /// Gets the request body parameter descriptions. /// public IList RequestBodyParameters { get { return GetParameterDescriptions(RequestModelDescription); } } /// /// Gets or sets the that describes the resource. /// public ModelDescription ResourceDescription { get; set; } /// /// Gets the resource property descriptions. /// public IList ResourceProperties { get { return GetParameterDescriptions(ResourceDescription); } } /// /// Gets the sample requests associated with the API. /// public IDictionary SampleRequests { get; private set; } /// /// Gets the sample responses associated with the API. /// public IDictionary SampleResponses { get; private set; } /// /// Gets the error messages associated with this model. /// public Collection ErrorMessages { get; private set; } private static IList GetParameterDescriptions(ModelDescription modelDescription) { ComplexTypeModelDescription complexTypeModelDescription = modelDescription as ComplexTypeModelDescription; if (complexTypeModelDescription != null) { return complexTypeModelDescription.Properties; } CollectionModelDescription collectionModelDescription = modelDescription as CollectionModelDescription; if (collectionModelDescription != null) { complexTypeModelDescription = collectionModelDescription.ElementDescription as ComplexTypeModelDescription; if (complexTypeModelDescription != null) { return complexTypeModelDescription.Properties; } } return null; } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/SampleGeneration/HelpPageSampleGenerator.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Web.Http.Description; using System.Xml.Linq; using Newtonsoft.Json; namespace Sample.Areas.HelpPage { /// /// This class will generate the samples for the help page. /// public class HelpPageSampleGenerator { /// /// Initializes a new instance of the class. /// public HelpPageSampleGenerator() { ActualHttpMessageTypes = new Dictionary(); ActionSamples = new Dictionary(); SampleObjects = new Dictionary(); SampleObjectFactories = new List> { DefaultSampleObjectFactory, }; } /// /// Gets CLR types that are used as the content of or . /// public IDictionary ActualHttpMessageTypes { get; internal set; } /// /// Gets the objects that are used directly as samples for certain actions. /// public IDictionary ActionSamples { get; internal set; } /// /// Gets the objects that are serialized as samples by the supported formatters. /// public IDictionary SampleObjects { get; internal set; } /// /// Gets factories for the objects that the supported formatters will serialize as samples. Processed in order, /// stopping when the factory successfully returns a non- object. /// /// /// Collection includes just initially. Use /// SampleObjectFactories.Insert(0, func) to provide an override and /// SampleObjectFactories.Add(func) to provide a fallback. [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] public IList> SampleObjectFactories { get; private set; } /// /// Gets the request body samples for a given . /// /// The . /// The samples keyed by media type. public IDictionary GetSampleRequests(ApiDescription api) { return GetSample(api, SampleDirection.Request); } /// /// Gets the response body samples for a given . /// /// The . /// The samples keyed by media type. public IDictionary GetSampleResponses(ApiDescription api) { return GetSample(api, SampleDirection.Response); } /// /// Gets the request or response body samples. /// /// The . /// The value indicating whether the sample is for a request or for a response. /// The samples keyed by media type. public virtual IDictionary GetSample(ApiDescription api, SampleDirection sampleDirection) { if (api == null) { throw new ArgumentNullException("api"); } string controllerName = api.ActionDescriptor.ControllerDescriptor.ControllerName; string actionName = api.ActionDescriptor.ActionName; IEnumerable parameterNames = api.ParameterDescriptions.Select(p => p.Name); Collection formatters; Type type = ResolveType(api, controllerName, actionName, parameterNames, sampleDirection, out formatters); var samples = new Dictionary(); // Use the samples provided directly for actions var actionSamples = GetAllActionSamples(controllerName, actionName, parameterNames, sampleDirection); foreach (var actionSample in actionSamples) { samples.Add(actionSample.Key.MediaType, WrapSampleIfString(actionSample.Value)); } // Do the sample generation based on formatters only if an action doesn't return an HttpResponseMessage. // Here we cannot rely on formatters because we don't know what's in the HttpResponseMessage, it might not even use formatters. if (type != null && !typeof(HttpResponseMessage).IsAssignableFrom(type)) { object sampleObject = GetSampleObject(type); foreach (var formatter in formatters) { foreach (MediaTypeHeaderValue mediaType in formatter.SupportedMediaTypes) { if (!samples.ContainsKey(mediaType)) { object sample = GetActionSample(controllerName, actionName, parameterNames, type, formatter, mediaType, sampleDirection); // If no sample found, try generate sample using formatter and sample object if (sample == null && sampleObject != null) { sample = WriteSampleObjectUsingFormatter(formatter, sampleObject, type, mediaType); } samples.Add(mediaType, WrapSampleIfString(sample)); } } } } return samples; } /// /// Search for samples that are provided directly through . /// /// Name of the controller. /// Name of the action. /// The parameter names. /// The CLR type. /// The formatter. /// The media type. /// The value indicating whether the sample is for a request or for a response. /// The sample that matches the parameters. public virtual object GetActionSample(string controllerName, string actionName, IEnumerable parameterNames, Type type, MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType, SampleDirection sampleDirection) { object sample; // First, try to get the sample provided for the specified mediaType, sampleDirection, controllerName, actionName and parameterNames. // If not found, try to get the sample provided for the specified mediaType, sampleDirection, controllerName and actionName regardless of the parameterNames. // If still not found, try to get the sample provided for the specified mediaType and type. // Finally, try to get the sample provided for the specified mediaType. if (ActionSamples.TryGetValue(new HelpPageSampleKey(mediaType, sampleDirection, controllerName, actionName, parameterNames), out sample) || ActionSamples.TryGetValue(new HelpPageSampleKey(mediaType, sampleDirection, controllerName, actionName, new[] { "*" }), out sample) || ActionSamples.TryGetValue(new HelpPageSampleKey(mediaType, type), out sample) || ActionSamples.TryGetValue(new HelpPageSampleKey(mediaType), out sample)) { return sample; } return null; } /// /// Gets the sample object that will be serialized by the formatters. /// First, it will look at the . If no sample object is found, it will try to create /// one using (which wraps an ) and other /// factories in . /// /// The type. /// The sample object. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Even if all items in SampleObjectFactories throw, problem will be visible as missing sample.")] public virtual object GetSampleObject(Type type) { object sampleObject; if (!SampleObjects.TryGetValue(type, out sampleObject)) { // No specific object available, try our factories. foreach (Func factory in SampleObjectFactories) { if (factory == null) { continue; } try { sampleObject = factory(this, type); if (sampleObject != null) { break; } } catch { // Ignore any problems encountered in the factory; go on to the next one (if any). } } } return sampleObject; } /// /// Resolves the actual type of passed to the in an action. /// /// The . /// The type. public virtual Type ResolveHttpRequestMessageType(ApiDescription api) { string controllerName = api.ActionDescriptor.ControllerDescriptor.ControllerName; string actionName = api.ActionDescriptor.ActionName; IEnumerable parameterNames = api.ParameterDescriptions.Select(p => p.Name); Collection formatters; return ResolveType(api, controllerName, actionName, parameterNames, SampleDirection.Request, out formatters); } /// /// Resolves the type of the action parameter or return value when or is used. /// /// The . /// Name of the controller. /// Name of the action. /// The parameter names. /// The value indicating whether the sample is for a request or a response. /// The formatters. [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Justification = "This is only used in advanced scenarios.")] public virtual Type ResolveType(ApiDescription api, string controllerName, string actionName, IEnumerable parameterNames, SampleDirection sampleDirection, out Collection formatters) { if (!Enum.IsDefined(typeof(SampleDirection), sampleDirection)) { throw new InvalidEnumArgumentException("sampleDirection", (int)sampleDirection, typeof(SampleDirection)); } if (api == null) { throw new ArgumentNullException("api"); } Type type; if (ActualHttpMessageTypes.TryGetValue(new HelpPageSampleKey(sampleDirection, controllerName, actionName, parameterNames), out type) || ActualHttpMessageTypes.TryGetValue(new HelpPageSampleKey(sampleDirection, controllerName, actionName, new[] { "*" }), out type)) { // Re-compute the supported formatters based on type Collection newFormatters = new Collection(); foreach (var formatter in api.ActionDescriptor.Configuration.Formatters) { if (IsFormatSupported(sampleDirection, formatter, type)) { newFormatters.Add(formatter); } } formatters = newFormatters; } else { switch (sampleDirection) { case SampleDirection.Request: ApiParameterDescription requestBodyParameter = api.ParameterDescriptions.FirstOrDefault(p => p.Source == ApiParameterSource.FromBody); type = requestBodyParameter == null ? null : requestBodyParameter.ParameterDescriptor.ParameterType; formatters = api.SupportedRequestBodyFormatters; break; case SampleDirection.Response: default: type = api.ResponseDescription.ResponseType ?? api.ResponseDescription.DeclaredType; formatters = api.SupportedResponseFormatters; break; } } return type; } /// /// Writes the sample object using formatter. /// /// The formatter. /// The value. /// The type. /// Type of the media. /// [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The exception is recorded as InvalidSample.")] public virtual object WriteSampleObjectUsingFormatter(MediaTypeFormatter formatter, object value, Type type, MediaTypeHeaderValue mediaType) { if (formatter == null) { throw new ArgumentNullException("formatter"); } if (mediaType == null) { throw new ArgumentNullException("mediaType"); } object sample = String.Empty; MemoryStream ms = null; HttpContent content = null; try { if (formatter.CanWriteType(type)) { ms = new MemoryStream(); content = new ObjectContent(type, value, formatter, mediaType); formatter.WriteToStreamAsync(type, value, ms, content, null).Wait(); ms.Position = 0; StreamReader reader = new StreamReader(ms); string serializedSampleString = reader.ReadToEnd(); if (mediaType.MediaType.ToUpperInvariant().Contains("XML")) { serializedSampleString = TryFormatXml(serializedSampleString); } else if (mediaType.MediaType.ToUpperInvariant().Contains("JSON")) { serializedSampleString = TryFormatJson(serializedSampleString); } sample = new TextSample(serializedSampleString); } else { sample = new InvalidSample(String.Format( CultureInfo.CurrentCulture, "Failed to generate the sample for media type '{0}'. Cannot use formatter '{1}' to write type '{2}'.", mediaType, formatter.GetType().Name, type.Name)); } } catch (Exception e) { sample = new InvalidSample(String.Format( CultureInfo.CurrentCulture, "An exception has occurred while using the formatter '{0}' to generate sample for media type '{1}'. Exception message: {2}", formatter.GetType().Name, mediaType.MediaType, UnwrapException(e).Message)); } finally { if (ms != null) { ms.Dispose(); } if (content != null) { content.Dispose(); } } return sample; } internal static Exception UnwrapException(Exception exception) { AggregateException aggregateException = exception as AggregateException; if (aggregateException != null) { return aggregateException.Flatten().InnerException; } return exception; } // Default factory for sample objects private static object DefaultSampleObjectFactory(HelpPageSampleGenerator sampleGenerator, Type type) { // Try to create a default sample object ObjectGenerator objectGenerator = new ObjectGenerator(); return objectGenerator.GenerateObject(type); } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Handling the failure by returning the original string.")] private static string TryFormatJson(string str) { try { object parsedJson = JsonConvert.DeserializeObject(str); return JsonConvert.SerializeObject(parsedJson, Formatting.Indented); } catch { // can't parse JSON, return the original string return str; } } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Handling the failure by returning the original string.")] private static string TryFormatXml(string str) { try { XDocument xml = XDocument.Parse(str); return xml.ToString(); } catch { // can't parse XML, return the original string return str; } } private static bool IsFormatSupported(SampleDirection sampleDirection, MediaTypeFormatter formatter, Type type) { switch (sampleDirection) { case SampleDirection.Request: return formatter.CanReadType(type); case SampleDirection.Response: return formatter.CanWriteType(type); } return false; } private IEnumerable> GetAllActionSamples(string controllerName, string actionName, IEnumerable parameterNames, SampleDirection sampleDirection) { HashSet parameterNamesSet = new HashSet(parameterNames, StringComparer.OrdinalIgnoreCase); foreach (var sample in ActionSamples) { HelpPageSampleKey sampleKey = sample.Key; if (String.Equals(controllerName, sampleKey.ControllerName, StringComparison.OrdinalIgnoreCase) && String.Equals(actionName, sampleKey.ActionName, StringComparison.OrdinalIgnoreCase) && (sampleKey.ParameterNames.SetEquals(new[] { "*" }) || parameterNamesSet.SetEquals(sampleKey.ParameterNames)) && sampleDirection == sampleKey.SampleDirection) { yield return sample; } } } private static object WrapSampleIfString(object sample) { string stringSample = sample as string; if (stringSample != null) { return new TextSample(stringSample); } return sample; } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/SampleGeneration/HelpPageSampleKey.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Net.Http.Headers; namespace Sample.Areas.HelpPage { /// /// This is used to identify the place where the sample should be applied. /// public class HelpPageSampleKey { /// /// Creates a new based on media type. /// /// The media type. public HelpPageSampleKey(MediaTypeHeaderValue mediaType) { if (mediaType == null) { throw new ArgumentNullException("mediaType"); } ActionName = String.Empty; ControllerName = String.Empty; MediaType = mediaType; ParameterNames = new HashSet(StringComparer.OrdinalIgnoreCase); } /// /// Creates a new based on media type and CLR type. /// /// The media type. /// The CLR type. public HelpPageSampleKey(MediaTypeHeaderValue mediaType, Type type) : this(mediaType) { if (type == null) { throw new ArgumentNullException("type"); } ParameterType = type; } /// /// Creates a new based on , controller name, action name and parameter names. /// /// The . /// Name of the controller. /// Name of the action. /// The parameter names. public HelpPageSampleKey(SampleDirection sampleDirection, string controllerName, string actionName, IEnumerable parameterNames) { if (!Enum.IsDefined(typeof(SampleDirection), sampleDirection)) { throw new InvalidEnumArgumentException("sampleDirection", (int)sampleDirection, typeof(SampleDirection)); } if (controllerName == null) { throw new ArgumentNullException("controllerName"); } if (actionName == null) { throw new ArgumentNullException("actionName"); } if (parameterNames == null) { throw new ArgumentNullException("parameterNames"); } ControllerName = controllerName; ActionName = actionName; ParameterNames = new HashSet(parameterNames, StringComparer.OrdinalIgnoreCase); SampleDirection = sampleDirection; } /// /// Creates a new based on media type, , controller name, action name and parameter names. /// /// The media type. /// The . /// Name of the controller. /// Name of the action. /// The parameter names. public HelpPageSampleKey(MediaTypeHeaderValue mediaType, SampleDirection sampleDirection, string controllerName, string actionName, IEnumerable parameterNames) : this(sampleDirection, controllerName, actionName, parameterNames) { if (mediaType == null) { throw new ArgumentNullException("mediaType"); } MediaType = mediaType; } /// /// Gets the name of the controller. /// /// /// The name of the controller. /// public string ControllerName { get; private set; } /// /// Gets the name of the action. /// /// /// The name of the action. /// public string ActionName { get; private set; } /// /// Gets the media type. /// /// /// The media type. /// public MediaTypeHeaderValue MediaType { get; private set; } /// /// Gets the parameter names. /// public HashSet ParameterNames { get; private set; } public Type ParameterType { get; private set; } /// /// Gets the . /// public SampleDirection? SampleDirection { get; private set; } public override bool Equals(object obj) { HelpPageSampleKey otherKey = obj as HelpPageSampleKey; if (otherKey == null) { return false; } return String.Equals(ControllerName, otherKey.ControllerName, StringComparison.OrdinalIgnoreCase) && String.Equals(ActionName, otherKey.ActionName, StringComparison.OrdinalIgnoreCase) && (MediaType == otherKey.MediaType || (MediaType != null && MediaType.Equals(otherKey.MediaType))) && ParameterType == otherKey.ParameterType && SampleDirection == otherKey.SampleDirection && ParameterNames.SetEquals(otherKey.ParameterNames); } public override int GetHashCode() { int hashCode = ControllerName.ToUpperInvariant().GetHashCode() ^ ActionName.ToUpperInvariant().GetHashCode(); if (MediaType != null) { hashCode ^= MediaType.GetHashCode(); } if (SampleDirection != null) { hashCode ^= SampleDirection.GetHashCode(); } if (ParameterType != null) { hashCode ^= ParameterType.GetHashCode(); } foreach (string parameterName in ParameterNames) { hashCode ^= parameterName.ToUpperInvariant().GetHashCode(); } return hashCode; } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/SampleGeneration/ImageSample.cs ================================================ using System; namespace Sample.Areas.HelpPage { /// /// This represents an image sample on the help page. There's a display template named ImageSample associated with this class. /// public class ImageSample { /// /// Initializes a new instance of the class. /// /// The URL of an image. public ImageSample(string src) { if (src == null) { throw new ArgumentNullException("src"); } Src = src; } public string Src { get; private set; } public override bool Equals(object obj) { ImageSample other = obj as ImageSample; return other != null && Src == other.Src; } public override int GetHashCode() { return Src.GetHashCode(); } public override string ToString() { return Src; } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/SampleGeneration/InvalidSample.cs ================================================ using System; namespace Sample.Areas.HelpPage { /// /// This represents an invalid sample on the help page. There's a display template named InvalidSample associated with this class. /// public class InvalidSample { public InvalidSample(string errorMessage) { if (errorMessage == null) { throw new ArgumentNullException("errorMessage"); } ErrorMessage = errorMessage; } public string ErrorMessage { get; private set; } public override bool Equals(object obj) { InvalidSample other = obj as InvalidSample; return other != null && ErrorMessage == other.ErrorMessage; } public override int GetHashCode() { return ErrorMessage.GetHashCode(); } public override string ToString() { return ErrorMessage; } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/SampleGeneration/ObjectGenerator.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Reflection; namespace Sample.Areas.HelpPage { /// /// This class will create an object of a given type and populate it with sample data. /// public class ObjectGenerator { internal const int DefaultCollectionSize = 2; private readonly SimpleTypeObjectGenerator SimpleObjectGenerator = new SimpleTypeObjectGenerator(); /// /// Generates an object for a given type. The type needs to be public, have a public default constructor and settable public properties/fields. Currently it supports the following types: /// Simple types: , , , , , etc. /// Complex types: POCO types. /// Nullables: . /// Arrays: arrays of simple types or complex types. /// Key value pairs: /// Tuples: , , etc /// Dictionaries: or anything deriving from . /// Collections: , , , , , or anything deriving from or . /// Queryables: , . /// /// The type. /// An object of the given type. public object GenerateObject(Type type) { return GenerateObject(type, new Dictionary()); } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Here we just want to return null if anything goes wrong.")] private object GenerateObject(Type type, Dictionary createdObjectReferences) { try { if (SimpleTypeObjectGenerator.CanGenerateObject(type)) { return SimpleObjectGenerator.GenerateObject(type); } if (type.IsArray) { return GenerateArray(type, DefaultCollectionSize, createdObjectReferences); } if (type.IsGenericType) { return GenerateGenericType(type, DefaultCollectionSize, createdObjectReferences); } if (type == typeof(IDictionary)) { return GenerateDictionary(typeof(Hashtable), DefaultCollectionSize, createdObjectReferences); } if (typeof(IDictionary).IsAssignableFrom(type)) { return GenerateDictionary(type, DefaultCollectionSize, createdObjectReferences); } if (type == typeof(IList) || type == typeof(IEnumerable) || type == typeof(ICollection)) { return GenerateCollection(typeof(ArrayList), DefaultCollectionSize, createdObjectReferences); } if (typeof(IList).IsAssignableFrom(type)) { return GenerateCollection(type, DefaultCollectionSize, createdObjectReferences); } if (type == typeof(IQueryable)) { return GenerateQueryable(type, DefaultCollectionSize, createdObjectReferences); } if (type.IsEnum) { return GenerateEnum(type); } if (type.IsPublic || type.IsNestedPublic) { return GenerateComplexObject(type, createdObjectReferences); } } catch { // Returns null if anything fails return null; } return null; } private static object GenerateGenericType(Type type, int collectionSize, Dictionary createdObjectReferences) { Type genericTypeDefinition = type.GetGenericTypeDefinition(); if (genericTypeDefinition == typeof(Nullable<>)) { return GenerateNullable(type, createdObjectReferences); } if (genericTypeDefinition == typeof(KeyValuePair<,>)) { return GenerateKeyValuePair(type, createdObjectReferences); } if (IsTuple(genericTypeDefinition)) { return GenerateTuple(type, createdObjectReferences); } Type[] genericArguments = type.GetGenericArguments(); if (genericArguments.Length == 1) { if (genericTypeDefinition == typeof(IList<>) || genericTypeDefinition == typeof(IEnumerable<>) || genericTypeDefinition == typeof(ICollection<>)) { Type collectionType = typeof(List<>).MakeGenericType(genericArguments); return GenerateCollection(collectionType, collectionSize, createdObjectReferences); } if (genericTypeDefinition == typeof(IQueryable<>)) { return GenerateQueryable(type, collectionSize, createdObjectReferences); } Type closedCollectionType = typeof(ICollection<>).MakeGenericType(genericArguments[0]); if (closedCollectionType.IsAssignableFrom(type)) { return GenerateCollection(type, collectionSize, createdObjectReferences); } } if (genericArguments.Length == 2) { if (genericTypeDefinition == typeof(IDictionary<,>)) { Type dictionaryType = typeof(Dictionary<,>).MakeGenericType(genericArguments); return GenerateDictionary(dictionaryType, collectionSize, createdObjectReferences); } Type closedDictionaryType = typeof(IDictionary<,>).MakeGenericType(genericArguments[0], genericArguments[1]); if (closedDictionaryType.IsAssignableFrom(type)) { return GenerateDictionary(type, collectionSize, createdObjectReferences); } } if (type.IsPublic || type.IsNestedPublic) { return GenerateComplexObject(type, createdObjectReferences); } return null; } private static object GenerateTuple(Type type, Dictionary createdObjectReferences) { Type[] genericArgs = type.GetGenericArguments(); object[] parameterValues = new object[genericArgs.Length]; bool failedToCreateTuple = true; ObjectGenerator objectGenerator = new ObjectGenerator(); for (int i = 0; i < genericArgs.Length; i++) { parameterValues[i] = objectGenerator.GenerateObject(genericArgs[i], createdObjectReferences); failedToCreateTuple &= parameterValues[i] == null; } if (failedToCreateTuple) { return null; } object result = Activator.CreateInstance(type, parameterValues); return result; } private static bool IsTuple(Type genericTypeDefinition) { return genericTypeDefinition == typeof(Tuple<>) || genericTypeDefinition == typeof(Tuple<,>) || genericTypeDefinition == typeof(Tuple<,,>) || genericTypeDefinition == typeof(Tuple<,,,>) || genericTypeDefinition == typeof(Tuple<,,,,>) || genericTypeDefinition == typeof(Tuple<,,,,,>) || genericTypeDefinition == typeof(Tuple<,,,,,,>) || genericTypeDefinition == typeof(Tuple<,,,,,,,>); } private static object GenerateKeyValuePair(Type keyValuePairType, Dictionary createdObjectReferences) { Type[] genericArgs = keyValuePairType.GetGenericArguments(); Type typeK = genericArgs[0]; Type typeV = genericArgs[1]; ObjectGenerator objectGenerator = new ObjectGenerator(); object keyObject = objectGenerator.GenerateObject(typeK, createdObjectReferences); object valueObject = objectGenerator.GenerateObject(typeV, createdObjectReferences); if (keyObject == null && valueObject == null) { // Failed to create key and values return null; } object result = Activator.CreateInstance(keyValuePairType, keyObject, valueObject); return result; } private static object GenerateArray(Type arrayType, int size, Dictionary createdObjectReferences) { Type type = arrayType.GetElementType(); Array result = Array.CreateInstance(type, size); bool areAllElementsNull = true; ObjectGenerator objectGenerator = new ObjectGenerator(); for (int i = 0; i < size; i++) { object element = objectGenerator.GenerateObject(type, createdObjectReferences); result.SetValue(element, i); areAllElementsNull &= element == null; } if (areAllElementsNull) { return null; } return result; } private static object GenerateDictionary(Type dictionaryType, int size, Dictionary createdObjectReferences) { Type typeK = typeof(object); Type typeV = typeof(object); if (dictionaryType.IsGenericType) { Type[] genericArgs = dictionaryType.GetGenericArguments(); typeK = genericArgs[0]; typeV = genericArgs[1]; } object result = Activator.CreateInstance(dictionaryType); MethodInfo addMethod = dictionaryType.GetMethod("Add") ?? dictionaryType.GetMethod("TryAdd"); MethodInfo containsMethod = dictionaryType.GetMethod("Contains") ?? dictionaryType.GetMethod("ContainsKey"); ObjectGenerator objectGenerator = new ObjectGenerator(); for (int i = 0; i < size; i++) { object newKey = objectGenerator.GenerateObject(typeK, createdObjectReferences); if (newKey == null) { // Cannot generate a valid key return null; } bool containsKey = (bool)containsMethod.Invoke(result, new object[] { newKey }); if (!containsKey) { object newValue = objectGenerator.GenerateObject(typeV, createdObjectReferences); addMethod.Invoke(result, new object[] { newKey, newValue }); } } return result; } private static object GenerateEnum(Type enumType) { Array possibleValues = Enum.GetValues(enumType); if (possibleValues.Length > 0) { return possibleValues.GetValue(0); } return null; } private static object GenerateQueryable(Type queryableType, int size, Dictionary createdObjectReferences) { bool isGeneric = queryableType.IsGenericType; object list; if (isGeneric) { Type listType = typeof(List<>).MakeGenericType(queryableType.GetGenericArguments()); list = GenerateCollection(listType, size, createdObjectReferences); } else { list = GenerateArray(typeof(object[]), size, createdObjectReferences); } if (list == null) { return null; } if (isGeneric) { Type argumentType = typeof(IEnumerable<>).MakeGenericType(queryableType.GetGenericArguments()); MethodInfo asQueryableMethod = typeof(Queryable).GetMethod("AsQueryable", new[] { argumentType }); return asQueryableMethod.Invoke(null, new[] { list }); } return Queryable.AsQueryable((IEnumerable)list); } private static object GenerateCollection(Type collectionType, int size, Dictionary createdObjectReferences) { Type type = collectionType.IsGenericType ? collectionType.GetGenericArguments()[0] : typeof(object); object result = Activator.CreateInstance(collectionType); MethodInfo addMethod = collectionType.GetMethod("Add"); bool areAllElementsNull = true; ObjectGenerator objectGenerator = new ObjectGenerator(); for (int i = 0; i < size; i++) { object element = objectGenerator.GenerateObject(type, createdObjectReferences); addMethod.Invoke(result, new object[] { element }); areAllElementsNull &= element == null; } if (areAllElementsNull) { return null; } return result; } private static object GenerateNullable(Type nullableType, Dictionary createdObjectReferences) { Type type = nullableType.GetGenericArguments()[0]; ObjectGenerator objectGenerator = new ObjectGenerator(); return objectGenerator.GenerateObject(type, createdObjectReferences); } private static object GenerateComplexObject(Type type, Dictionary createdObjectReferences) { object result = null; if (createdObjectReferences.TryGetValue(type, out result)) { // The object has been created already, just return it. This will handle the circular reference case. return result; } if (type.IsValueType) { result = Activator.CreateInstance(type); } else { ConstructorInfo defaultCtor = type.GetConstructor(Type.EmptyTypes); if (defaultCtor == null) { // Cannot instantiate the type because it doesn't have a default constructor return null; } result = defaultCtor.Invoke(new object[0]); } createdObjectReferences.Add(type, result); SetPublicProperties(type, result, createdObjectReferences); SetPublicFields(type, result, createdObjectReferences); return result; } private static void SetPublicProperties(Type type, object obj, Dictionary createdObjectReferences) { PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); ObjectGenerator objectGenerator = new ObjectGenerator(); foreach (PropertyInfo property in properties) { if (property.CanWrite) { object propertyValue = objectGenerator.GenerateObject(property.PropertyType, createdObjectReferences); property.SetValue(obj, propertyValue, null); } } } private static void SetPublicFields(Type type, object obj, Dictionary createdObjectReferences) { FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance); ObjectGenerator objectGenerator = new ObjectGenerator(); foreach (FieldInfo field in fields) { object fieldValue = objectGenerator.GenerateObject(field.FieldType, createdObjectReferences); field.SetValue(obj, fieldValue); } } private class SimpleTypeObjectGenerator { private long _index = 0; private static readonly Dictionary> DefaultGenerators = InitializeGenerators(); [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These are simple type factories and cannot be split up.")] private static Dictionary> InitializeGenerators() { return new Dictionary> { { typeof(Boolean), index => true }, { typeof(Byte), index => (Byte)64 }, { typeof(Char), index => (Char)65 }, { typeof(DateTime), index => DateTime.Now }, { typeof(DateTimeOffset), index => new DateTimeOffset(DateTime.Now) }, { typeof(DBNull), index => DBNull.Value }, { typeof(Decimal), index => (Decimal)index }, { typeof(Double), index => (Double)(index + 0.1) }, { typeof(Guid), index => Guid.NewGuid() }, { typeof(Int16), index => (Int16)(index % Int16.MaxValue) }, { typeof(Int32), index => (Int32)(index % Int32.MaxValue) }, { typeof(Int64), index => (Int64)index }, { typeof(Object), index => new object() }, { typeof(SByte), index => (SByte)64 }, { typeof(Single), index => (Single)(index + 0.1) }, { typeof(String), index => { return String.Format(CultureInfo.CurrentCulture, "sample string {0}", index); } }, { typeof(TimeSpan), index => { return TimeSpan.FromTicks(1234567); } }, { typeof(UInt16), index => (UInt16)(index % UInt16.MaxValue) }, { typeof(UInt32), index => (UInt32)(index % UInt32.MaxValue) }, { typeof(UInt64), index => (UInt64)index }, { typeof(Uri), index => { return new Uri(String.Format(CultureInfo.CurrentCulture, "http://webapihelppage{0}.com", index)); } }, }; } public static bool CanGenerateObject(Type type) { return DefaultGenerators.ContainsKey(type); } public object GenerateObject(Type type) { return DefaultGenerators[type](++_index); } } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/SampleGeneration/SampleDirection.cs ================================================ namespace Sample.Areas.HelpPage { /// /// Indicates whether the sample is used for request or response /// public enum SampleDirection { Request = 0, Response } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/SampleGeneration/TextSample.cs ================================================ using System; namespace Sample.Areas.HelpPage { /// /// This represents a preformatted text sample on the help page. There's a display template named TextSample associated with this class. /// public class TextSample { public TextSample(string text) { if (text == null) { throw new ArgumentNullException("text"); } Text = text; } public string Text { get; private set; } public override bool Equals(object obj) { TextSample other = obj as TextSample; return other != null && Text == other.Text; } public override int GetHashCode() { return Text.GetHashCode(); } public override string ToString() { return Text; } } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Help/Api.cshtml ================================================ @using System.Web.Http @using Sample.Areas.HelpPage.Models @model HelpPageApiModel @{ var description = Model.ApiDescription; ViewBag.Title = description.HttpMethod.Method + " " + description.RelativePath; }
@Html.DisplayForModel()
================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Help/DisplayTemplates/ApiGroup.cshtml ================================================ @using System.Web.Http @using System.Web.Http.Controllers @using System.Web.Http.Description @using Sample.Areas.HelpPage @using Sample.Areas.HelpPage.Models @model IGrouping @{ var controllerDocumentation = ViewBag.DocumentationProvider != null ? ViewBag.DocumentationProvider.GetDocumentation(Model.Key) : null; }

@Model.Key.ControllerName

@if (!String.IsNullOrEmpty(controllerDocumentation)) {

@controllerDocumentation

} @foreach (var api in Model) { }
APIDescription
@api.HttpMethod.Method @api.RelativePath @if (api.Documentation != null) {

@api.Documentation

} else {

No documentation available.

}
================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Help/DisplayTemplates/CollectionModelDescription.cshtml ================================================ @using Sample.Areas.HelpPage.ModelDescriptions @model CollectionModelDescription @if (Model.ElementDescription is ComplexTypeModelDescription) { @Html.DisplayFor(m => m.ElementDescription) } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Help/DisplayTemplates/ComplexTypeModelDescription.cshtml ================================================ @using Sample.Areas.HelpPage.ModelDescriptions @model ComplexTypeModelDescription @Html.DisplayFor(m => m.Properties, "Parameters") ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Help/DisplayTemplates/DictionaryModelDescription.cshtml ================================================ @using Sample.Areas.HelpPage.ModelDescriptions @model DictionaryModelDescription Dictionary of @Html.DisplayFor(m => Model.KeyModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.KeyModelDescription }) [key] and @Html.DisplayFor(m => Model.ValueModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ValueModelDescription }) [value] ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Help/DisplayTemplates/EnumTypeModelDescription.cshtml ================================================ @using Sample.Areas.HelpPage.ModelDescriptions @model EnumTypeModelDescription

Possible enumeration values:

@foreach (EnumValueDescription value in Model.Values) { }
NameValueDescription
@value.Name

@value.Value

@value.Documentation

================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Help/DisplayTemplates/HelpPageApiModel.cshtml ================================================ @using System.Web.Http @using System.Web.Http.Description @using Sample.Areas.HelpPage.Models @using Sample.Areas.HelpPage.ModelDescriptions @model HelpPageApiModel @{ ApiDescription description = Model.ApiDescription; }

@description.HttpMethod.Method @description.RelativePath

@description.Documentation

Request Information

URI Parameters

@Html.DisplayFor(m => m.UriParameters, "Parameters")

Body Parameters

@Model.RequestDocumentation

@if (Model.RequestModelDescription != null) { @Html.DisplayFor(m => m.RequestModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.RequestModelDescription }) if (Model.RequestBodyParameters != null) { @Html.DisplayFor(m => m.RequestBodyParameters, "Parameters") } } else {

None.

} @if (Model.SampleRequests.Count > 0) {

Request Formats

@Html.DisplayFor(m => m.SampleRequests, "Samples") }

Response Information

Resource Description

@description.ResponseDescription.Documentation

@if (Model.ResourceDescription != null) { @Html.DisplayFor(m => m.ResourceDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ResourceDescription }) if (Model.ResourceProperties != null) { @Html.DisplayFor(m => m.ResourceProperties, "Parameters") } } else {

None.

} @if (Model.SampleResponses.Count > 0) {

Response Formats

@Html.DisplayFor(m => m.SampleResponses, "Samples") }
================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Help/DisplayTemplates/ImageSample.cshtml ================================================ @using Sample.Areas.HelpPage @model ImageSample ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Help/DisplayTemplates/InvalidSample.cshtml ================================================ @using Sample.Areas.HelpPage @model InvalidSample @if (HttpContext.Current.IsDebuggingEnabled) {

@Model.ErrorMessage

} else {

Sample not available.

} ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Help/DisplayTemplates/KeyValuePairModelDescription.cshtml ================================================ @using Sample.Areas.HelpPage.ModelDescriptions @model KeyValuePairModelDescription Pair of @Html.DisplayFor(m => Model.KeyModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.KeyModelDescription }) [key] and @Html.DisplayFor(m => Model.ValueModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ValueModelDescription }) [value] ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Help/DisplayTemplates/ModelDescriptionLink.cshtml ================================================ @using Sample.Areas.HelpPage.ModelDescriptions @model Type @{ ModelDescription modelDescription = ViewBag.modelDescription; if (modelDescription is ComplexTypeModelDescription || modelDescription is EnumTypeModelDescription) { if (Model == typeof(Object)) { @:Object } else { @Html.ActionLink(modelDescription.Name, "ResourceModel", "Help", new { modelName = modelDescription.Name }, null) } } else if (modelDescription is CollectionModelDescription) { var collectionDescription = modelDescription as CollectionModelDescription; var elementDescription = collectionDescription.ElementDescription; @:Collection of @Html.DisplayFor(m => elementDescription.ModelType, "ModelDescriptionLink", new { modelDescription = elementDescription }) } else { @Html.DisplayFor(m => modelDescription) } } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Help/DisplayTemplates/Parameters.cshtml ================================================ @using System.Collections.Generic @using System.Collections.ObjectModel @using System.Web.Http.Description @using System.Threading @using Sample.Areas.HelpPage.ModelDescriptions @model IList @if (Model.Count > 0) { @foreach (ParameterDescription parameter in Model) { ModelDescription modelDescription = parameter.TypeDescription; }
NameDescriptionTypeAdditional information
@parameter.Name

@parameter.Documentation

@Html.DisplayFor(m => modelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = modelDescription }) @if (parameter.Annotations.Count > 0) { foreach (var annotation in parameter.Annotations) {

@annotation.Documentation

} } else {

None.

}
} else {

None.

} ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Help/DisplayTemplates/Samples.cshtml ================================================ @using System.Net.Http.Headers @model Dictionary @{ // Group the samples into a single tab if they are the same. Dictionary samples = Model.GroupBy(pair => pair.Value).ToDictionary( pair => String.Join(", ", pair.Select(m => m.Key.ToString()).ToArray()), pair => pair.Key); var mediaTypes = samples.Keys; }
@foreach (var mediaType in mediaTypes) {

@mediaType

Sample: @{ var sample = samples[mediaType]; if (sample == null) {

Sample not available.

} else { @Html.DisplayFor(s => sample); } }
}
================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Help/DisplayTemplates/SimpleTypeModelDescription.cshtml ================================================ @using Sample.Areas.HelpPage.ModelDescriptions @model SimpleTypeModelDescription @Model.Documentation ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Help/DisplayTemplates/TextSample.cshtml ================================================ @using Sample.Areas.HelpPage @model TextSample
@Model.Text
================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Help/Index.cshtml ================================================ @using System.Web.Http @using System.Web.Http.Controllers @using System.Web.Http.Description @using System.Collections.ObjectModel @using Sample.Areas.HelpPage.Models @model Collection @{ ViewBag.Title = "ASP.NET Web API Help Page"; // Group APIs by controller ILookup apiGroups = Model.ToLookup(api => api.ActionDescriptor.ControllerDescriptor); }

@ViewBag.Title

@foreach (var group in apiGroups) { @Html.DisplayFor(m => group, "ApiGroup") }
================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Help/ResourceModel.cshtml ================================================ @using System.Web.Http @using Sample.Areas.HelpPage.ModelDescriptions @model ModelDescription

@Model.Name

@Model.Documentation

@Html.DisplayFor(m => Model)
================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Shared/_Layout.cshtml ================================================ @ViewBag.Title @RenderSection("scripts", required: false) @RenderBody() ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/Web.config ================================================ 
================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/Views/_ViewStart.cshtml ================================================ @{ // Change the Layout path below to blend the look and feel of the help page with your existing web pages. Layout = "~/Areas/HelpPage/Views/Shared/_Layout.cshtml"; } ================================================ FILE: AlexaSkillsKit.Sample/Areas/HelpPage/XmlDocumentationProvider.cs ================================================ using System; using System.Globalization; using System.Linq; using System.Reflection; using System.Web.Http.Controllers; using System.Web.Http.Description; using System.Xml.XPath; using Sample.Areas.HelpPage.ModelDescriptions; namespace Sample.Areas.HelpPage { /// /// A custom that reads the API documentation from an XML documentation file. /// public class XmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider { private XPathNavigator _documentNavigator; private const string TypeExpression = "/doc/members/member[@name='T:{0}']"; private const string MethodExpression = "/doc/members/member[@name='M:{0}']"; private const string PropertyExpression = "/doc/members/member[@name='P:{0}']"; private const string FieldExpression = "/doc/members/member[@name='F:{0}']"; private const string ParameterExpression = "param[@name='{0}']"; /// /// Initializes a new instance of the class. /// /// The physical path to XML document. public XmlDocumentationProvider(string documentPath) { if (documentPath == null) { throw new ArgumentNullException("documentPath"); } XPathDocument xpath = new XPathDocument(documentPath); _documentNavigator = xpath.CreateNavigator(); } public string GetDocumentation(HttpControllerDescriptor controllerDescriptor) { XPathNavigator typeNode = GetTypeNode(controllerDescriptor.ControllerType); return GetTagValue(typeNode, "summary"); } public virtual string GetDocumentation(HttpActionDescriptor actionDescriptor) { XPathNavigator methodNode = GetMethodNode(actionDescriptor); return GetTagValue(methodNode, "summary"); } public virtual string GetDocumentation(HttpParameterDescriptor parameterDescriptor) { ReflectedHttpParameterDescriptor reflectedParameterDescriptor = parameterDescriptor as ReflectedHttpParameterDescriptor; if (reflectedParameterDescriptor != null) { XPathNavigator methodNode = GetMethodNode(reflectedParameterDescriptor.ActionDescriptor); if (methodNode != null) { string parameterName = reflectedParameterDescriptor.ParameterInfo.Name; XPathNavigator parameterNode = methodNode.SelectSingleNode(String.Format(CultureInfo.InvariantCulture, ParameterExpression, parameterName)); if (parameterNode != null) { return parameterNode.Value.Trim(); } } } return null; } public string GetResponseDocumentation(HttpActionDescriptor actionDescriptor) { XPathNavigator methodNode = GetMethodNode(actionDescriptor); return GetTagValue(methodNode, "returns"); } public string GetDocumentation(MemberInfo member) { string memberName = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", GetTypeName(member.DeclaringType), member.Name); string expression = member.MemberType == MemberTypes.Field ? FieldExpression : PropertyExpression; string selectExpression = String.Format(CultureInfo.InvariantCulture, expression, memberName); XPathNavigator propertyNode = _documentNavigator.SelectSingleNode(selectExpression); return GetTagValue(propertyNode, "summary"); } public string GetDocumentation(Type type) { XPathNavigator typeNode = GetTypeNode(type); return GetTagValue(typeNode, "summary"); } private XPathNavigator GetMethodNode(HttpActionDescriptor actionDescriptor) { ReflectedHttpActionDescriptor reflectedActionDescriptor = actionDescriptor as ReflectedHttpActionDescriptor; if (reflectedActionDescriptor != null) { string selectExpression = String.Format(CultureInfo.InvariantCulture, MethodExpression, GetMemberName(reflectedActionDescriptor.MethodInfo)); return _documentNavigator.SelectSingleNode(selectExpression); } return null; } private static string GetMemberName(MethodInfo method) { string name = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", GetTypeName(method.DeclaringType), method.Name); ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length != 0) { string[] parameterTypeNames = parameters.Select(param => GetTypeName(param.ParameterType)).ToArray(); name += String.Format(CultureInfo.InvariantCulture, "({0})", String.Join(",", parameterTypeNames)); } return name; } private static string GetTagValue(XPathNavigator parentNode, string tagName) { if (parentNode != null) { XPathNavigator node = parentNode.SelectSingleNode(tagName); if (node != null) { return node.Value.Trim(); } } return null; } private XPathNavigator GetTypeNode(Type type) { string controllerTypeName = GetTypeName(type); string selectExpression = String.Format(CultureInfo.InvariantCulture, TypeExpression, controllerTypeName); return _documentNavigator.SelectSingleNode(selectExpression); } private static string GetTypeName(Type type) { string name = type.FullName; if (type.IsGenericType) { // Format the generic type name to something like: Generic{System.Int32,System.String} Type genericType = type.GetGenericTypeDefinition(); Type[] genericArguments = type.GetGenericArguments(); string genericTypeName = genericType.FullName; // Trim the generic parameter counts from the name genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf('`')); string[] argumentTypeNames = genericArguments.Select(t => GetTypeName(t)).ToArray(); name = String.Format(CultureInfo.InvariantCulture, "{0}{{{1}}}", genericTypeName, String.Join(",", argumentTypeNames)); } if (type.IsNested) { // Changing the nested type name from OuterType+InnerType to OuterType.InnerType to match the XML documentation syntax. name = name.Replace("+", "."); } return name; } } } ================================================ FILE: AlexaSkillsKit.Sample/Content/Site.css ================================================ body { padding-top: 50px; padding-bottom: 20px; } /* Set padding to keep content from hitting the edges */ .body-content { padding-left: 15px; padding-right: 15px; } /* Set width on the form input elements since they're 100% wide by default */ input, select, textarea { max-width: 280px; } ================================================ FILE: AlexaSkillsKit.Sample/Content/bootstrap.css ================================================ /* NUGET: BEGIN LICENSE TEXT * * Microsoft grants you the right to use these script files for the sole * purpose of either: (i) interacting through your browser with the Microsoft * website or online service, subject to the applicable licensing or use * terms; or (ii) using the files as included with a Microsoft product subject * to that product's license terms. Microsoft reserves all other rights to the * files not expressly granted by Microsoft, whether by implication, estoppel * or otherwise. The notices and licenses below are for informational purposes only. * * NUGET: END LICENSE TEXT */ /*! * Bootstrap v3.0.0 * * Copyright 2013 Twitter, Inc * Licensed under the Apache License v2.0 * http://www.apache.org/licenses/LICENSE-2.0 * * Designed and built with all the love in the world by @mdo and @fat. */ /*! normalize.css v2.1.0 | MIT License | git.io/normalize */ article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; } audio, canvas, video { display: inline-block; } audio:not([controls]) { display: none; height: 0; } [hidden] { display: none; } html { font-family: sans-serif; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } body { margin: 0; } a:focus { outline: thin dotted; } a:active, a:hover { outline: 0; } h1 { margin: 0.67em 0; font-size: 2em; } abbr[title] { border-bottom: 1px dotted; } b, strong { font-weight: bold; } dfn { font-style: italic; } hr { height: 0; -moz-box-sizing: content-box; box-sizing: content-box; } mark { color: #000; background: #ff0; } code, kbd, pre, samp { font-family: monospace, serif; font-size: 1em; } pre { white-space: pre-wrap; } q { quotes: "\201C" "\201D" "\2018" "\2019"; } small { font-size: 80%; } sub, sup { position: relative; font-size: 75%; line-height: 0; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } img { border: 0; } svg:not(:root) { overflow: hidden; } figure { margin: 0; } fieldset { padding: 0.35em 0.625em 0.75em; margin: 0 2px; border: 1px solid #c0c0c0; } legend { padding: 0; border: 0; } button, input, select, textarea { margin: 0; font-family: inherit; font-size: 100%; } button, input { line-height: normal; } button, select { text-transform: none; } button, html input[type="button"], input[type="reset"], input[type="submit"] { cursor: pointer; -webkit-appearance: button; } button[disabled], html input[disabled] { cursor: default; } input[type="checkbox"], input[type="radio"] { padding: 0; box-sizing: border-box; } input[type="search"] { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; -webkit-appearance: textfield; } input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } button::-moz-focus-inner, input::-moz-focus-inner { padding: 0; border: 0; } textarea { overflow: auto; vertical-align: top; } table { border-collapse: collapse; border-spacing: 0; } @media print { * { color: #000 !important; text-shadow: none !important; background: transparent !important; box-shadow: none !important; } a, a:visited { text-decoration: underline; } a[href]:after { content: " (" attr(href) ")"; } abbr[title]:after { content: " (" attr(title) ")"; } .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } thead { display: table-header-group; } tr, img { page-break-inside: avoid; } img { max-width: 100% !important; } @page { margin: 2cm .5cm; } p, h2, h3 { orphans: 3; widows: 3; } h2, h3 { page-break-after: avoid; } .navbar { display: none; } .table td, .table th { background-color: #fff !important; } .btn > .caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px solid #000; } .table { border-collapse: collapse !important; } .table-bordered th, .table-bordered td { border: 1px solid #ddd !important; } } *, *:before, *:after { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } html { font-size: 62.5%; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.428571429; color: #333333; background-color: #ffffff; } input, button, select, textarea { font-family: inherit; font-size: inherit; line-height: inherit; } button, input, select[multiple], textarea { background-image: none; } a { color: #428bca; text-decoration: none; } a:hover, a:focus { color: #2a6496; text-decoration: underline; } a:focus { outline: thin dotted #333; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } img { vertical-align: middle; } .img-responsive { display: block; height: auto; max-width: 100%; } .img-rounded { border-radius: 6px; } .img-thumbnail { display: inline-block; height: auto; max-width: 100%; padding: 4px; line-height: 1.428571429; background-color: #ffffff; border: 1px solid #dddddd; border-radius: 4px; -webkit-transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out; } .img-circle { border-radius: 50%; } hr { margin-top: 20px; margin-bottom: 20px; border: 0; border-top: 1px solid #eeeeee; } .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); border: 0; } p { margin: 0 0 10px; } .lead { margin-bottom: 20px; font-size: 16.099999999999998px; font-weight: 200; line-height: 1.4; } @media (min-width: 768px) { .lead { font-size: 21px; } } small { font-size: 85%; } cite { font-style: normal; } .text-muted { color: #999999; } .text-primary { color: #428bca; } .text-warning { color: #c09853; } .text-danger { color: #b94a48; } .text-success { color: #468847; } .text-info { color: #3a87ad; } .text-left { text-align: left; } .text-right { text-align: right; } .text-center { text-align: center; } h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-weight: 500; line-height: 1.1; } h1 small, h2 small, h3 small, h4 small, h5 small, h6 small, .h1 small, .h2 small, .h3 small, .h4 small, .h5 small, .h6 small { font-weight: normal; line-height: 1; color: #999999; } h1, h2, h3 { margin-top: 20px; margin-bottom: 10px; } h4, h5, h6 { margin-top: 10px; margin-bottom: 10px; } h1, .h1 { font-size: 36px; } h2, .h2 { font-size: 30px; } h3, .h3 { font-size: 24px; } h4, .h4 { font-size: 18px; } h5, .h5 { font-size: 14px; } h6, .h6 { font-size: 12px; } h1 small, .h1 small { font-size: 24px; } h2 small, .h2 small { font-size: 18px; } h3 small, .h3 small, h4 small, .h4 small { font-size: 14px; } .page-header { padding-bottom: 9px; margin: 40px 0 20px; border-bottom: 1px solid #eeeeee; } ul, ol { margin-top: 0; margin-bottom: 10px; } ul ul, ol ul, ul ol, ol ol { margin-bottom: 0; } .list-unstyled { padding-left: 0; list-style: none; } .list-inline { padding-left: 0; list-style: none; } .list-inline > li { display: inline-block; padding-right: 5px; padding-left: 5px; } dl { margin-bottom: 20px; } dt, dd { line-height: 1.428571429; } dt { font-weight: bold; } dd { margin-left: 0; } @media (min-width: 768px) { .dl-horizontal dt { float: left; width: 160px; overflow: hidden; clear: left; text-align: right; text-overflow: ellipsis; white-space: nowrap; } .dl-horizontal dd { margin-left: 180px; } .dl-horizontal dd:before, .dl-horizontal dd:after { display: table; content: " "; } .dl-horizontal dd:after { clear: both; } .dl-horizontal dd:before, .dl-horizontal dd:after { display: table; content: " "; } .dl-horizontal dd:after { clear: both; } } abbr[title], abbr[data-original-title] { cursor: help; border-bottom: 1px dotted #999999; } abbr.initialism { font-size: 90%; text-transform: uppercase; } blockquote { padding: 10px 20px; margin: 0 0 20px; border-left: 5px solid #eeeeee; } blockquote p { font-size: 17.5px; font-weight: 300; line-height: 1.25; } blockquote p:last-child { margin-bottom: 0; } blockquote small { display: block; line-height: 1.428571429; color: #999999; } blockquote small:before { content: '\2014 \00A0'; } blockquote.pull-right { padding-right: 15px; padding-left: 0; border-right: 5px solid #eeeeee; border-left: 0; } blockquote.pull-right p, blockquote.pull-right small { text-align: right; } blockquote.pull-right small:before { content: ''; } blockquote.pull-right small:after { content: '\00A0 \2014'; } q:before, q:after, blockquote:before, blockquote:after { content: ""; } address { display: block; margin-bottom: 20px; font-style: normal; line-height: 1.428571429; } code, pre { font-family: Monaco, Menlo, Consolas, "Courier New", monospace; } code { padding: 2px 4px; font-size: 90%; color: #c7254e; white-space: nowrap; background-color: #f9f2f4; border-radius: 4px; } pre { display: block; padding: 9.5px; margin: 0 0 10px; font-size: 13px; line-height: 1.428571429; color: #333333; word-break: break-all; word-wrap: break-word; background-color: #f5f5f5; border: 1px solid #cccccc; border-radius: 4px; } pre.prettyprint { margin-bottom: 20px; } pre code { padding: 0; font-size: inherit; color: inherit; white-space: pre-wrap; background-color: transparent; border: 0; } .pre-scrollable { max-height: 340px; overflow-y: scroll; } .container { padding-right: 15px; padding-left: 15px; margin-right: auto; margin-left: auto; } .container:before, .container:after { display: table; content: " "; } .container:after { clear: both; } .container:before, .container:after { display: table; content: " "; } .container:after { clear: both; } .row { margin-right: -15px; margin-left: -15px; } .row:before, .row:after { display: table; content: " "; } .row:after { clear: both; } .row:before, .row:after { display: table; content: " "; } .row:after { clear: both; } .col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { position: relative; min-height: 1px; padding-right: 15px; padding-left: 15px; } .col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11 { float: left; } .col-xs-1 { width: 8.333333333333332%; } .col-xs-2 { width: 16.666666666666664%; } .col-xs-3 { width: 25%; } .col-xs-4 { width: 33.33333333333333%; } .col-xs-5 { width: 41.66666666666667%; } .col-xs-6 { width: 50%; } .col-xs-7 { width: 58.333333333333336%; } .col-xs-8 { width: 66.66666666666666%; } .col-xs-9 { width: 75%; } .col-xs-10 { width: 83.33333333333334%; } .col-xs-11 { width: 91.66666666666666%; } .col-xs-12 { width: 100%; } @media (min-width: 768px) { .container { max-width: 750px; } .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11 { float: left; } .col-sm-1 { width: 8.333333333333332%; } .col-sm-2 { width: 16.666666666666664%; } .col-sm-3 { width: 25%; } .col-sm-4 { width: 33.33333333333333%; } .col-sm-5 { width: 41.66666666666667%; } .col-sm-6 { width: 50%; } .col-sm-7 { width: 58.333333333333336%; } .col-sm-8 { width: 66.66666666666666%; } .col-sm-9 { width: 75%; } .col-sm-10 { width: 83.33333333333334%; } .col-sm-11 { width: 91.66666666666666%; } .col-sm-12 { width: 100%; } .col-sm-push-1 { left: 8.333333333333332%; } .col-sm-push-2 { left: 16.666666666666664%; } .col-sm-push-3 { left: 25%; } .col-sm-push-4 { left: 33.33333333333333%; } .col-sm-push-5 { left: 41.66666666666667%; } .col-sm-push-6 { left: 50%; } .col-sm-push-7 { left: 58.333333333333336%; } .col-sm-push-8 { left: 66.66666666666666%; } .col-sm-push-9 { left: 75%; } .col-sm-push-10 { left: 83.33333333333334%; } .col-sm-push-11 { left: 91.66666666666666%; } .col-sm-pull-1 { right: 8.333333333333332%; } .col-sm-pull-2 { right: 16.666666666666664%; } .col-sm-pull-3 { right: 25%; } .col-sm-pull-4 { right: 33.33333333333333%; } .col-sm-pull-5 { right: 41.66666666666667%; } .col-sm-pull-6 { right: 50%; } .col-sm-pull-7 { right: 58.333333333333336%; } .col-sm-pull-8 { right: 66.66666666666666%; } .col-sm-pull-9 { right: 75%; } .col-sm-pull-10 { right: 83.33333333333334%; } .col-sm-pull-11 { right: 91.66666666666666%; } .col-sm-offset-1 { margin-left: 8.333333333333332%; } .col-sm-offset-2 { margin-left: 16.666666666666664%; } .col-sm-offset-3 { margin-left: 25%; } .col-sm-offset-4 { margin-left: 33.33333333333333%; } .col-sm-offset-5 { margin-left: 41.66666666666667%; } .col-sm-offset-6 { margin-left: 50%; } .col-sm-offset-7 { margin-left: 58.333333333333336%; } .col-sm-offset-8 { margin-left: 66.66666666666666%; } .col-sm-offset-9 { margin-left: 75%; } .col-sm-offset-10 { margin-left: 83.33333333333334%; } .col-sm-offset-11 { margin-left: 91.66666666666666%; } } @media (min-width: 992px) { .container { max-width: 970px; } .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11 { float: left; } .col-md-1 { width: 8.333333333333332%; } .col-md-2 { width: 16.666666666666664%; } .col-md-3 { width: 25%; } .col-md-4 { width: 33.33333333333333%; } .col-md-5 { width: 41.66666666666667%; } .col-md-6 { width: 50%; } .col-md-7 { width: 58.333333333333336%; } .col-md-8 { width: 66.66666666666666%; } .col-md-9 { width: 75%; } .col-md-10 { width: 83.33333333333334%; } .col-md-11 { width: 91.66666666666666%; } .col-md-12 { width: 100%; } .col-md-push-0 { left: auto; } .col-md-push-1 { left: 8.333333333333332%; } .col-md-push-2 { left: 16.666666666666664%; } .col-md-push-3 { left: 25%; } .col-md-push-4 { left: 33.33333333333333%; } .col-md-push-5 { left: 41.66666666666667%; } .col-md-push-6 { left: 50%; } .col-md-push-7 { left: 58.333333333333336%; } .col-md-push-8 { left: 66.66666666666666%; } .col-md-push-9 { left: 75%; } .col-md-push-10 { left: 83.33333333333334%; } .col-md-push-11 { left: 91.66666666666666%; } .col-md-pull-0 { right: auto; } .col-md-pull-1 { right: 8.333333333333332%; } .col-md-pull-2 { right: 16.666666666666664%; } .col-md-pull-3 { right: 25%; } .col-md-pull-4 { right: 33.33333333333333%; } .col-md-pull-5 { right: 41.66666666666667%; } .col-md-pull-6 { right: 50%; } .col-md-pull-7 { right: 58.333333333333336%; } .col-md-pull-8 { right: 66.66666666666666%; } .col-md-pull-9 { right: 75%; } .col-md-pull-10 { right: 83.33333333333334%; } .col-md-pull-11 { right: 91.66666666666666%; } .col-md-offset-0 { margin-left: 0; } .col-md-offset-1 { margin-left: 8.333333333333332%; } .col-md-offset-2 { margin-left: 16.666666666666664%; } .col-md-offset-3 { margin-left: 25%; } .col-md-offset-4 { margin-left: 33.33333333333333%; } .col-md-offset-5 { margin-left: 41.66666666666667%; } .col-md-offset-6 { margin-left: 50%; } .col-md-offset-7 { margin-left: 58.333333333333336%; } .col-md-offset-8 { margin-left: 66.66666666666666%; } .col-md-offset-9 { margin-left: 75%; } .col-md-offset-10 { margin-left: 83.33333333333334%; } .col-md-offset-11 { margin-left: 91.66666666666666%; } } @media (min-width: 1200px) { .container { max-width: 1170px; } .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11 { float: left; } .col-lg-1 { width: 8.333333333333332%; } .col-lg-2 { width: 16.666666666666664%; } .col-lg-3 { width: 25%; } .col-lg-4 { width: 33.33333333333333%; } .col-lg-5 { width: 41.66666666666667%; } .col-lg-6 { width: 50%; } .col-lg-7 { width: 58.333333333333336%; } .col-lg-8 { width: 66.66666666666666%; } .col-lg-9 { width: 75%; } .col-lg-10 { width: 83.33333333333334%; } .col-lg-11 { width: 91.66666666666666%; } .col-lg-12 { width: 100%; } .col-lg-push-0 { left: auto; } .col-lg-push-1 { left: 8.333333333333332%; } .col-lg-push-2 { left: 16.666666666666664%; } .col-lg-push-3 { left: 25%; } .col-lg-push-4 { left: 33.33333333333333%; } .col-lg-push-5 { left: 41.66666666666667%; } .col-lg-push-6 { left: 50%; } .col-lg-push-7 { left: 58.333333333333336%; } .col-lg-push-8 { left: 66.66666666666666%; } .col-lg-push-9 { left: 75%; } .col-lg-push-10 { left: 83.33333333333334%; } .col-lg-push-11 { left: 91.66666666666666%; } .col-lg-pull-0 { right: auto; } .col-lg-pull-1 { right: 8.333333333333332%; } .col-lg-pull-2 { right: 16.666666666666664%; } .col-lg-pull-3 { right: 25%; } .col-lg-pull-4 { right: 33.33333333333333%; } .col-lg-pull-5 { right: 41.66666666666667%; } .col-lg-pull-6 { right: 50%; } .col-lg-pull-7 { right: 58.333333333333336%; } .col-lg-pull-8 { right: 66.66666666666666%; } .col-lg-pull-9 { right: 75%; } .col-lg-pull-10 { right: 83.33333333333334%; } .col-lg-pull-11 { right: 91.66666666666666%; } .col-lg-offset-0 { margin-left: 0; } .col-lg-offset-1 { margin-left: 8.333333333333332%; } .col-lg-offset-2 { margin-left: 16.666666666666664%; } .col-lg-offset-3 { margin-left: 25%; } .col-lg-offset-4 { margin-left: 33.33333333333333%; } .col-lg-offset-5 { margin-left: 41.66666666666667%; } .col-lg-offset-6 { margin-left: 50%; } .col-lg-offset-7 { margin-left: 58.333333333333336%; } .col-lg-offset-8 { margin-left: 66.66666666666666%; } .col-lg-offset-9 { margin-left: 75%; } .col-lg-offset-10 { margin-left: 83.33333333333334%; } .col-lg-offset-11 { margin-left: 91.66666666666666%; } } table { max-width: 100%; background-color: transparent; } th { text-align: left; } .table { width: 100%; margin-bottom: 20px; } .table thead > tr > th, .table tbody > tr > th, .table tfoot > tr > th, .table thead > tr > td, .table tbody > tr > td, .table tfoot > tr > td { padding: 8px; line-height: 1.428571429; vertical-align: top; border-top: 1px solid #dddddd; } .table thead > tr > th { vertical-align: bottom; border-bottom: 2px solid #dddddd; } .table caption + thead tr:first-child th, .table colgroup + thead tr:first-child th, .table thead:first-child tr:first-child th, .table caption + thead tr:first-child td, .table colgroup + thead tr:first-child td, .table thead:first-child tr:first-child td { border-top: 0; } .table tbody + tbody { border-top: 2px solid #dddddd; } .table .table { background-color: #ffffff; } .table-condensed thead > tr > th, .table-condensed tbody > tr > th, .table-condensed tfoot > tr > th, .table-condensed thead > tr > td, .table-condensed tbody > tr > td, .table-condensed tfoot > tr > td { padding: 5px; } .table-bordered { border: 1px solid #dddddd; } .table-bordered > thead > tr > th, .table-bordered > tbody > tr > th, .table-bordered > tfoot > tr > th, .table-bordered > thead > tr > td, .table-bordered > tbody > tr > td, .table-bordered > tfoot > tr > td { border: 1px solid #dddddd; } .table-bordered > thead > tr > th, .table-bordered > thead > tr > td { border-bottom-width: 2px; } .table-striped > tbody > tr:nth-child(odd) > td, .table-striped > tbody > tr:nth-child(odd) > th { background-color: #f9f9f9; } .table-hover > tbody > tr:hover > td, .table-hover > tbody > tr:hover > th { background-color: #f5f5f5; } table col[class*="col-"] { display: table-column; float: none; } table td[class*="col-"], table th[class*="col-"] { display: table-cell; float: none; } .table > thead > tr > td.active, .table > tbody > tr > td.active, .table > tfoot > tr > td.active, .table > thead > tr > th.active, .table > tbody > tr > th.active, .table > tfoot > tr > th.active, .table > thead > tr.active > td, .table > tbody > tr.active > td, .table > tfoot > tr.active > td, .table > thead > tr.active > th, .table > tbody > tr.active > th, .table > tfoot > tr.active > th { background-color: #f5f5f5; } .table > thead > tr > td.success, .table > tbody > tr > td.success, .table > tfoot > tr > td.success, .table > thead > tr > th.success, .table > tbody > tr > th.success, .table > tfoot > tr > th.success, .table > thead > tr.success > td, .table > tbody > tr.success > td, .table > tfoot > tr.success > td, .table > thead > tr.success > th, .table > tbody > tr.success > th, .table > tfoot > tr.success > th { background-color: #dff0d8; border-color: #d6e9c6; } .table-hover > tbody > tr > td.success:hover, .table-hover > tbody > tr > th.success:hover, .table-hover > tbody > tr.success:hover > td { background-color: #d0e9c6; border-color: #c9e2b3; } .table > thead > tr > td.danger, .table > tbody > tr > td.danger, .table > tfoot > tr > td.danger, .table > thead > tr > th.danger, .table > tbody > tr > th.danger, .table > tfoot > tr > th.danger, .table > thead > tr.danger > td, .table > tbody > tr.danger > td, .table > tfoot > tr.danger > td, .table > thead > tr.danger > th, .table > tbody > tr.danger > th, .table > tfoot > tr.danger > th { background-color: #f2dede; border-color: #eed3d7; } .table-hover > tbody > tr > td.danger:hover, .table-hover > tbody > tr > th.danger:hover, .table-hover > tbody > tr.danger:hover > td { background-color: #ebcccc; border-color: #e6c1c7; } .table > thead > tr > td.warning, .table > tbody > tr > td.warning, .table > tfoot > tr > td.warning, .table > thead > tr > th.warning, .table > tbody > tr > th.warning, .table > tfoot > tr > th.warning, .table > thead > tr.warning > td, .table > tbody > tr.warning > td, .table > tfoot > tr.warning > td, .table > thead > tr.warning > th, .table > tbody > tr.warning > th, .table > tfoot > tr.warning > th { background-color: #fcf8e3; border-color: #fbeed5; } .table-hover > tbody > tr > td.warning:hover, .table-hover > tbody > tr > th.warning:hover, .table-hover > tbody > tr.warning:hover > td { background-color: #faf2cc; border-color: #f8e5be; } @media (max-width: 768px) { .table-responsive { width: 100%; margin-bottom: 15px; overflow-x: scroll; overflow-y: hidden; border: 1px solid #dddddd; } .table-responsive > .table { margin-bottom: 0; background-color: #fff; } .table-responsive > .table > thead > tr > th, .table-responsive > .table > tbody > tr > th, .table-responsive > .table > tfoot > tr > th, .table-responsive > .table > thead > tr > td, .table-responsive > .table > tbody > tr > td, .table-responsive > .table > tfoot > tr > td { white-space: nowrap; } .table-responsive > .table-bordered { border: 0; } .table-responsive > .table-bordered > thead > tr > th:first-child, .table-responsive > .table-bordered > tbody > tr > th:first-child, .table-responsive > .table-bordered > tfoot > tr > th:first-child, .table-responsive > .table-bordered > thead > tr > td:first-child, .table-responsive > .table-bordered > tbody > tr > td:first-child, .table-responsive > .table-bordered > tfoot > tr > td:first-child { border-left: 0; } .table-responsive > .table-bordered > thead > tr > th:last-child, .table-responsive > .table-bordered > tbody > tr > th:last-child, .table-responsive > .table-bordered > tfoot > tr > th:last-child, .table-responsive > .table-bordered > thead > tr > td:last-child, .table-responsive > .table-bordered > tbody > tr > td:last-child, .table-responsive > .table-bordered > tfoot > tr > td:last-child { border-right: 0; } .table-responsive > .table-bordered > thead > tr:last-child > th, .table-responsive > .table-bordered > tbody > tr:last-child > th, .table-responsive > .table-bordered > tfoot > tr:last-child > th, .table-responsive > .table-bordered > thead > tr:last-child > td, .table-responsive > .table-bordered > tbody > tr:last-child > td, .table-responsive > .table-bordered > tfoot > tr:last-child > td { border-bottom: 0; } } fieldset { padding: 0; margin: 0; border: 0; } legend { display: block; width: 100%; padding: 0; margin-bottom: 20px; font-size: 21px; line-height: inherit; color: #333333; border: 0; border-bottom: 1px solid #e5e5e5; } label { display: inline-block; margin-bottom: 5px; font-weight: bold; } input[type="search"] { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } input[type="radio"], input[type="checkbox"] { margin: 4px 0 0; margin-top: 1px \9; /* IE8-9 */ line-height: normal; } input[type="file"] { display: block; } select[multiple], select[size] { height: auto; } select optgroup { font-family: inherit; font-size: inherit; font-style: inherit; } input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { outline: thin dotted #333; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } input[type="number"]::-webkit-outer-spin-button, input[type="number"]::-webkit-inner-spin-button { height: auto; } .form-control:-moz-placeholder { color: #999999; } .form-control::-moz-placeholder { color: #999999; } .form-control:-ms-input-placeholder { color: #999999; } .form-control::-webkit-input-placeholder { color: #999999; } .form-control { display: block; width: 100%; height: 34px; padding: 6px 12px; font-size: 14px; line-height: 1.428571429; color: #555555; vertical-align: middle; background-color: #ffffff; border: 1px solid #cccccc; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; } .form-control:focus { border-color: #66afe9; outline: 0; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); } .form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { cursor: not-allowed; background-color: #eeeeee; } textarea.form-control { height: auto; } .form-group { margin-bottom: 15px; } .radio, .checkbox { display: block; min-height: 20px; padding-left: 20px; margin-top: 10px; margin-bottom: 10px; vertical-align: middle; } .radio label, .checkbox label { display: inline; margin-bottom: 0; font-weight: normal; cursor: pointer; } .radio input[type="radio"], .radio-inline input[type="radio"], .checkbox input[type="checkbox"], .checkbox-inline input[type="checkbox"] { float: left; margin-left: -20px; } .radio + .radio, .checkbox + .checkbox { margin-top: -5px; } .radio-inline, .checkbox-inline { display: inline-block; padding-left: 20px; margin-bottom: 0; font-weight: normal; vertical-align: middle; cursor: pointer; } .radio-inline + .radio-inline, .checkbox-inline + .checkbox-inline { margin-top: 0; margin-left: 10px; } input[type="radio"][disabled], input[type="checkbox"][disabled], .radio[disabled], .radio-inline[disabled], .checkbox[disabled], .checkbox-inline[disabled], fieldset[disabled] input[type="radio"], fieldset[disabled] input[type="checkbox"], fieldset[disabled] .radio, fieldset[disabled] .radio-inline, fieldset[disabled] .checkbox, fieldset[disabled] .checkbox-inline { cursor: not-allowed; } .input-sm { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } select.input-sm { height: 30px; line-height: 30px; } textarea.input-sm { height: auto; } .input-lg { height: 45px; padding: 10px 16px; font-size: 18px; line-height: 1.33; border-radius: 6px; } select.input-lg { height: 45px; line-height: 45px; } textarea.input-lg { height: auto; } .has-warning .help-block, .has-warning .control-label { color: #c09853; } .has-warning .form-control { border-color: #c09853; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-warning .form-control:focus { border-color: #a47e3c; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; } .has-warning .input-group-addon { color: #c09853; background-color: #fcf8e3; border-color: #c09853; } .has-error .help-block, .has-error .control-label { color: #b94a48; } .has-error .form-control { border-color: #b94a48; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-error .form-control:focus { border-color: #953b39; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; } .has-error .input-group-addon { color: #b94a48; background-color: #f2dede; border-color: #b94a48; } .has-success .help-block, .has-success .control-label { color: #468847; } .has-success .form-control { border-color: #468847; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-success .form-control:focus { border-color: #356635; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; } .has-success .input-group-addon { color: #468847; background-color: #dff0d8; border-color: #468847; } .form-control-static { padding-top: 7px; margin-bottom: 0; } .help-block { display: block; margin-top: 5px; margin-bottom: 10px; color: #737373; } @media (min-width: 768px) { .form-inline .form-group { display: inline-block; margin-bottom: 0; vertical-align: middle; } .form-inline .form-control { display: inline-block; } .form-inline .radio, .form-inline .checkbox { display: inline-block; padding-left: 0; margin-top: 0; margin-bottom: 0; } .form-inline .radio input[type="radio"], .form-inline .checkbox input[type="checkbox"] { float: none; margin-left: 0; } } .form-horizontal .control-label, .form-horizontal .radio, .form-horizontal .checkbox, .form-horizontal .radio-inline, .form-horizontal .checkbox-inline { padding-top: 7px; margin-top: 0; margin-bottom: 0; } .form-horizontal .form-group { margin-right: -15px; margin-left: -15px; } .form-horizontal .form-group:before, .form-horizontal .form-group:after { display: table; content: " "; } .form-horizontal .form-group:after { clear: both; } .form-horizontal .form-group:before, .form-horizontal .form-group:after { display: table; content: " "; } .form-horizontal .form-group:after { clear: both; } @media (min-width: 768px) { .form-horizontal .control-label { text-align: right; } } .btn { display: inline-block; padding: 6px 12px; margin-bottom: 0; font-size: 14px; font-weight: normal; line-height: 1.428571429; text-align: center; white-space: nowrap; vertical-align: middle; cursor: pointer; border: 1px solid transparent; border-radius: 4px; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; -o-user-select: none; user-select: none; } .btn:focus { outline: thin dotted #333; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } .btn:hover, .btn:focus { color: #333333; text-decoration: none; } .btn:active, .btn.active { background-image: none; outline: 0; -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); } .btn.disabled, .btn[disabled], fieldset[disabled] .btn { pointer-events: none; cursor: not-allowed; opacity: 0.65; filter: alpha(opacity=65); -webkit-box-shadow: none; box-shadow: none; } .btn-default { color: #333333; background-color: #ffffff; border-color: #cccccc; } .btn-default:hover, .btn-default:focus, .btn-default:active, .btn-default.active, .open .dropdown-toggle.btn-default { color: #333333; background-color: #ebebeb; border-color: #adadad; } .btn-default:active, .btn-default.active, .open .dropdown-toggle.btn-default { background-image: none; } .btn-default.disabled, .btn-default[disabled], fieldset[disabled] .btn-default, .btn-default.disabled:hover, .btn-default[disabled]:hover, fieldset[disabled] .btn-default:hover, .btn-default.disabled:focus, .btn-default[disabled]:focus, fieldset[disabled] .btn-default:focus, .btn-default.disabled:active, .btn-default[disabled]:active, fieldset[disabled] .btn-default:active, .btn-default.disabled.active, .btn-default[disabled].active, fieldset[disabled] .btn-default.active { background-color: #ffffff; border-color: #cccccc; } .btn-primary { color: #ffffff; background-color: #428bca; border-color: #357ebd; } .btn-primary:hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active, .open .dropdown-toggle.btn-primary { color: #ffffff; background-color: #3276b1; border-color: #285e8e; } .btn-primary:active, .btn-primary.active, .open .dropdown-toggle.btn-primary { background-image: none; } .btn-primary.disabled, .btn-primary[disabled], fieldset[disabled] .btn-primary, .btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, .btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active, .btn-primary.disabled.active, .btn-primary[disabled].active, fieldset[disabled] .btn-primary.active { background-color: #428bca; border-color: #357ebd; } .btn-warning { color: #ffffff; background-color: #f0ad4e; border-color: #eea236; } .btn-warning:hover, .btn-warning:focus, .btn-warning:active, .btn-warning.active, .open .dropdown-toggle.btn-warning { color: #ffffff; background-color: #ed9c28; border-color: #d58512; } .btn-warning:active, .btn-warning.active, .open .dropdown-toggle.btn-warning { background-image: none; } .btn-warning.disabled, .btn-warning[disabled], fieldset[disabled] .btn-warning, .btn-warning.disabled:hover, .btn-warning[disabled]:hover, fieldset[disabled] .btn-warning:hover, .btn-warning.disabled:focus, .btn-warning[disabled]:focus, fieldset[disabled] .btn-warning:focus, .btn-warning.disabled:active, .btn-warning[disabled]:active, fieldset[disabled] .btn-warning:active, .btn-warning.disabled.active, .btn-warning[disabled].active, fieldset[disabled] .btn-warning.active { background-color: #f0ad4e; border-color: #eea236; } .btn-danger { color: #ffffff; background-color: #d9534f; border-color: #d43f3a; } .btn-danger:hover, .btn-danger:focus, .btn-danger:active, .btn-danger.active, .open .dropdown-toggle.btn-danger { color: #ffffff; background-color: #d2322d; border-color: #ac2925; } .btn-danger:active, .btn-danger.active, .open .dropdown-toggle.btn-danger { background-image: none; } .btn-danger.disabled, .btn-danger[disabled], fieldset[disabled] .btn-danger, .btn-danger.disabled:hover, .btn-danger[disabled]:hover, fieldset[disabled] .btn-danger:hover, .btn-danger.disabled:focus, .btn-danger[disabled]:focus, fieldset[disabled] .btn-danger:focus, .btn-danger.disabled:active, .btn-danger[disabled]:active, fieldset[disabled] .btn-danger:active, .btn-danger.disabled.active, .btn-danger[disabled].active, fieldset[disabled] .btn-danger.active { background-color: #d9534f; border-color: #d43f3a; } .btn-success { color: #ffffff; background-color: #5cb85c; border-color: #4cae4c; } .btn-success:hover, .btn-success:focus, .btn-success:active, .btn-success.active, .open .dropdown-toggle.btn-success { color: #ffffff; background-color: #47a447; border-color: #398439; } .btn-success:active, .btn-success.active, .open .dropdown-toggle.btn-success { background-image: none; } .btn-success.disabled, .btn-success[disabled], fieldset[disabled] .btn-success, .btn-success.disabled:hover, .btn-success[disabled]:hover, fieldset[disabled] .btn-success:hover, .btn-success.disabled:focus, .btn-success[disabled]:focus, fieldset[disabled] .btn-success:focus, .btn-success.disabled:active, .btn-success[disabled]:active, fieldset[disabled] .btn-success:active, .btn-success.disabled.active, .btn-success[disabled].active, fieldset[disabled] .btn-success.active { background-color: #5cb85c; border-color: #4cae4c; } .btn-info { color: #ffffff; background-color: #5bc0de; border-color: #46b8da; } .btn-info:hover, .btn-info:focus, .btn-info:active, .btn-info.active, .open .dropdown-toggle.btn-info { color: #ffffff; background-color: #39b3d7; border-color: #269abc; } .btn-info:active, .btn-info.active, .open .dropdown-toggle.btn-info { background-image: none; } .btn-info.disabled, .btn-info[disabled], fieldset[disabled] .btn-info, .btn-info.disabled:hover, .btn-info[disabled]:hover, fieldset[disabled] .btn-info:hover, .btn-info.disabled:focus, .btn-info[disabled]:focus, fieldset[disabled] .btn-info:focus, .btn-info.disabled:active, .btn-info[disabled]:active, fieldset[disabled] .btn-info:active, .btn-info.disabled.active, .btn-info[disabled].active, fieldset[disabled] .btn-info.active { background-color: #5bc0de; border-color: #46b8da; } .btn-link { font-weight: normal; color: #428bca; cursor: pointer; border-radius: 0; } .btn-link, .btn-link:active, .btn-link[disabled], fieldset[disabled] .btn-link { background-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .btn-link, .btn-link:hover, .btn-link:focus, .btn-link:active { border-color: transparent; } .btn-link:hover, .btn-link:focus { color: #2a6496; text-decoration: underline; background-color: transparent; } .btn-link[disabled]:hover, fieldset[disabled] .btn-link:hover, .btn-link[disabled]:focus, fieldset[disabled] .btn-link:focus { color: #999999; text-decoration: none; } .btn-lg { padding: 10px 16px; font-size: 18px; line-height: 1.33; border-radius: 6px; } .btn-sm, .btn-xs { padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .btn-xs { padding: 1px 5px; } .btn-block { display: block; width: 100%; padding-right: 0; padding-left: 0; } .btn-block + .btn-block { margin-top: 5px; } input[type="submit"].btn-block, input[type="reset"].btn-block, input[type="button"].btn-block { width: 100%; } .fade { opacity: 0; -webkit-transition: opacity 0.15s linear; transition: opacity 0.15s linear; } .fade.in { opacity: 1; } .collapse { display: none; } .collapse.in { display: block; } .collapsing { position: relative; height: 0; overflow: hidden; -webkit-transition: height 0.35s ease; transition: height 0.35s ease; } @font-face { font-family: 'Glyphicons Halflings'; src: url('../fonts/glyphicons-halflings-regular.eot'); src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg'); } .glyphicon { position: relative; top: 1px; display: inline-block; font-family: 'Glyphicons Halflings'; -webkit-font-smoothing: antialiased; font-style: normal; font-weight: normal; line-height: 1; } .glyphicon-asterisk:before { content: "\2a"; } .glyphicon-plus:before { content: "\2b"; } .glyphicon-euro:before { content: "\20ac"; } .glyphicon-minus:before { content: "\2212"; } .glyphicon-cloud:before { content: "\2601"; } .glyphicon-envelope:before { content: "\2709"; } .glyphicon-pencil:before { content: "\270f"; } .glyphicon-glass:before { content: "\e001"; } .glyphicon-music:before { content: "\e002"; } .glyphicon-search:before { content: "\e003"; } .glyphicon-heart:before { content: "\e005"; } .glyphicon-star:before { content: "\e006"; } .glyphicon-star-empty:before { content: "\e007"; } .glyphicon-user:before { content: "\e008"; } .glyphicon-film:before { content: "\e009"; } .glyphicon-th-large:before { content: "\e010"; } .glyphicon-th:before { content: "\e011"; } .glyphicon-th-list:before { content: "\e012"; } .glyphicon-ok:before { content: "\e013"; } .glyphicon-remove:before { content: "\e014"; } .glyphicon-zoom-in:before { content: "\e015"; } .glyphicon-zoom-out:before { content: "\e016"; } .glyphicon-off:before { content: "\e017"; } .glyphicon-signal:before { content: "\e018"; } .glyphicon-cog:before { content: "\e019"; } .glyphicon-trash:before { content: "\e020"; } .glyphicon-home:before { content: "\e021"; } .glyphicon-file:before { content: "\e022"; } .glyphicon-time:before { content: "\e023"; } .glyphicon-road:before { content: "\e024"; } .glyphicon-download-alt:before { content: "\e025"; } .glyphicon-download:before { content: "\e026"; } .glyphicon-upload:before { content: "\e027"; } .glyphicon-inbox:before { content: "\e028"; } .glyphicon-play-circle:before { content: "\e029"; } .glyphicon-repeat:before { content: "\e030"; } .glyphicon-refresh:before { content: "\e031"; } .glyphicon-list-alt:before { content: "\e032"; } .glyphicon-flag:before { content: "\e034"; } .glyphicon-headphones:before { content: "\e035"; } .glyphicon-volume-off:before { content: "\e036"; } .glyphicon-volume-down:before { content: "\e037"; } .glyphicon-volume-up:before { content: "\e038"; } .glyphicon-qrcode:before { content: "\e039"; } .glyphicon-barcode:before { content: "\e040"; } .glyphicon-tag:before { content: "\e041"; } .glyphicon-tags:before { content: "\e042"; } .glyphicon-book:before { content: "\e043"; } .glyphicon-print:before { content: "\e045"; } .glyphicon-font:before { content: "\e047"; } .glyphicon-bold:before { content: "\e048"; } .glyphicon-italic:before { content: "\e049"; } .glyphicon-text-height:before { content: "\e050"; } .glyphicon-text-width:before { content: "\e051"; } .glyphicon-align-left:before { content: "\e052"; } .glyphicon-align-center:before { content: "\e053"; } .glyphicon-align-right:before { content: "\e054"; } .glyphicon-align-justify:before { content: "\e055"; } .glyphicon-list:before { content: "\e056"; } .glyphicon-indent-left:before { content: "\e057"; } .glyphicon-indent-right:before { content: "\e058"; } .glyphicon-facetime-video:before { content: "\e059"; } .glyphicon-picture:before { content: "\e060"; } .glyphicon-map-marker:before { content: "\e062"; } .glyphicon-adjust:before { content: "\e063"; } .glyphicon-tint:before { content: "\e064"; } .glyphicon-edit:before { content: "\e065"; } .glyphicon-share:before { content: "\e066"; } .glyphicon-check:before { content: "\e067"; } .glyphicon-move:before { content: "\e068"; } .glyphicon-step-backward:before { content: "\e069"; } .glyphicon-fast-backward:before { content: "\e070"; } .glyphicon-backward:before { content: "\e071"; } .glyphicon-play:before { content: "\e072"; } .glyphicon-pause:before { content: "\e073"; } .glyphicon-stop:before { content: "\e074"; } .glyphicon-forward:before { content: "\e075"; } .glyphicon-fast-forward:before { content: "\e076"; } .glyphicon-step-forward:before { content: "\e077"; } .glyphicon-eject:before { content: "\e078"; } .glyphicon-chevron-left:before { content: "\e079"; } .glyphicon-chevron-right:before { content: "\e080"; } .glyphicon-plus-sign:before { content: "\e081"; } .glyphicon-minus-sign:before { content: "\e082"; } .glyphicon-remove-sign:before { content: "\e083"; } .glyphicon-ok-sign:before { content: "\e084"; } .glyphicon-question-sign:before { content: "\e085"; } .glyphicon-info-sign:before { content: "\e086"; } .glyphicon-screenshot:before { content: "\e087"; } .glyphicon-remove-circle:before { content: "\e088"; } .glyphicon-ok-circle:before { content: "\e089"; } .glyphicon-ban-circle:before { content: "\e090"; } .glyphicon-arrow-left:before { content: "\e091"; } .glyphicon-arrow-right:before { content: "\e092"; } .glyphicon-arrow-up:before { content: "\e093"; } .glyphicon-arrow-down:before { content: "\e094"; } .glyphicon-share-alt:before { content: "\e095"; } .glyphicon-resize-full:before { content: "\e096"; } .glyphicon-resize-small:before { content: "\e097"; } .glyphicon-exclamation-sign:before { content: "\e101"; } .glyphicon-gift:before { content: "\e102"; } .glyphicon-leaf:before { content: "\e103"; } .glyphicon-eye-open:before { content: "\e105"; } .glyphicon-eye-close:before { content: "\e106"; } .glyphicon-warning-sign:before { content: "\e107"; } .glyphicon-plane:before { content: "\e108"; } .glyphicon-random:before { content: "\e110"; } .glyphicon-comment:before { content: "\e111"; } .glyphicon-magnet:before { content: "\e112"; } .glyphicon-chevron-up:before { content: "\e113"; } .glyphicon-chevron-down:before { content: "\e114"; } .glyphicon-retweet:before { content: "\e115"; } .glyphicon-shopping-cart:before { content: "\e116"; } .glyphicon-folder-close:before { content: "\e117"; } .glyphicon-folder-open:before { content: "\e118"; } .glyphicon-resize-vertical:before { content: "\e119"; } .glyphicon-resize-horizontal:before { content: "\e120"; } .glyphicon-hdd:before { content: "\e121"; } .glyphicon-bullhorn:before { content: "\e122"; } .glyphicon-certificate:before { content: "\e124"; } .glyphicon-thumbs-up:before { content: "\e125"; } .glyphicon-thumbs-down:before { content: "\e126"; } .glyphicon-hand-right:before { content: "\e127"; } .glyphicon-hand-left:before { content: "\e128"; } .glyphicon-hand-up:before { content: "\e129"; } .glyphicon-hand-down:before { content: "\e130"; } .glyphicon-circle-arrow-right:before { content: "\e131"; } .glyphicon-circle-arrow-left:before { content: "\e132"; } .glyphicon-circle-arrow-up:before { content: "\e133"; } .glyphicon-circle-arrow-down:before { content: "\e134"; } .glyphicon-globe:before { content: "\e135"; } .glyphicon-tasks:before { content: "\e137"; } .glyphicon-filter:before { content: "\e138"; } .glyphicon-fullscreen:before { content: "\e140"; } .glyphicon-dashboard:before { content: "\e141"; } .glyphicon-heart-empty:before { content: "\e143"; } .glyphicon-link:before { content: "\e144"; } .glyphicon-phone:before { content: "\e145"; } .glyphicon-usd:before { content: "\e148"; } .glyphicon-gbp:before { content: "\e149"; } .glyphicon-sort:before { content: "\e150"; } .glyphicon-sort-by-alphabet:before { content: "\e151"; } .glyphicon-sort-by-alphabet-alt:before { content: "\e152"; } .glyphicon-sort-by-order:before { content: "\e153"; } .glyphicon-sort-by-order-alt:before { content: "\e154"; } .glyphicon-sort-by-attributes:before { content: "\e155"; } .glyphicon-sort-by-attributes-alt:before { content: "\e156"; } .glyphicon-unchecked:before { content: "\e157"; } .glyphicon-expand:before { content: "\e158"; } .glyphicon-collapse-down:before { content: "\e159"; } .glyphicon-collapse-up:before { content: "\e160"; } .glyphicon-log-in:before { content: "\e161"; } .glyphicon-flash:before { content: "\e162"; } .glyphicon-log-out:before { content: "\e163"; } .glyphicon-new-window:before { content: "\e164"; } .glyphicon-record:before { content: "\e165"; } .glyphicon-save:before { content: "\e166"; } .glyphicon-open:before { content: "\e167"; } .glyphicon-saved:before { content: "\e168"; } .glyphicon-import:before { content: "\e169"; } .glyphicon-export:before { content: "\e170"; } .glyphicon-send:before { content: "\e171"; } .glyphicon-floppy-disk:before { content: "\e172"; } .glyphicon-floppy-saved:before { content: "\e173"; } .glyphicon-floppy-remove:before { content: "\e174"; } .glyphicon-floppy-save:before { content: "\e175"; } .glyphicon-floppy-open:before { content: "\e176"; } .glyphicon-credit-card:before { content: "\e177"; } .glyphicon-transfer:before { content: "\e178"; } .glyphicon-cutlery:before { content: "\e179"; } .glyphicon-header:before { content: "\e180"; } .glyphicon-compressed:before { content: "\e181"; } .glyphicon-earphone:before { content: "\e182"; } .glyphicon-phone-alt:before { content: "\e183"; } .glyphicon-tower:before { content: "\e184"; } .glyphicon-stats:before { content: "\e185"; } .glyphicon-sd-video:before { content: "\e186"; } .glyphicon-hd-video:before { content: "\e187"; } .glyphicon-subtitles:before { content: "\e188"; } .glyphicon-sound-stereo:before { content: "\e189"; } .glyphicon-sound-dolby:before { content: "\e190"; } .glyphicon-sound-5-1:before { content: "\e191"; } .glyphicon-sound-6-1:before { content: "\e192"; } .glyphicon-sound-7-1:before { content: "\e193"; } .glyphicon-copyright-mark:before { content: "\e194"; } .glyphicon-registration-mark:before { content: "\e195"; } .glyphicon-cloud-download:before { content: "\e197"; } .glyphicon-cloud-upload:before { content: "\e198"; } .glyphicon-tree-conifer:before { content: "\e199"; } .glyphicon-tree-deciduous:before { content: "\e200"; } .glyphicon-briefcase:before { content: "\1f4bc"; } .glyphicon-calendar:before { content: "\1f4c5"; } .glyphicon-pushpin:before { content: "\1f4cc"; } .glyphicon-paperclip:before { content: "\1f4ce"; } .glyphicon-camera:before { content: "\1f4f7"; } .glyphicon-lock:before { content: "\1f512"; } .glyphicon-bell:before { content: "\1f514"; } .glyphicon-bookmark:before { content: "\1f516"; } .glyphicon-fire:before { content: "\1f525"; } .glyphicon-wrench:before { content: "\1f527"; } .caret { display: inline-block; width: 0; height: 0; margin-left: 2px; vertical-align: middle; border-top: 4px solid #000000; border-right: 4px solid transparent; border-bottom: 0 dotted; border-left: 4px solid transparent; content: ""; } .dropdown { position: relative; } .dropdown-toggle:focus { outline: 0; } .dropdown-menu { position: absolute; top: 100%; left: 0; z-index: 1000; display: none; float: left; min-width: 160px; padding: 5px 0; margin: 2px 0 0; font-size: 14px; list-style: none; background-color: #ffffff; border: 1px solid #cccccc; border: 1px solid rgba(0, 0, 0, 0.15); border-radius: 4px; -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); background-clip: padding-box; } .dropdown-menu.pull-right { right: 0; left: auto; } .dropdown-menu .divider { height: 1px; margin: 9px 0; overflow: hidden; background-color: #e5e5e5; } .dropdown-menu > li > a { display: block; padding: 3px 20px; clear: both; font-weight: normal; line-height: 1.428571429; color: #333333; white-space: nowrap; } .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { color: #ffffff; text-decoration: none; background-color: #428bca; } .dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus { color: #ffffff; text-decoration: none; background-color: #428bca; outline: 0; } .dropdown-menu > .disabled > a, .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { color: #999999; } .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { text-decoration: none; cursor: not-allowed; background-color: transparent; background-image: none; filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); } .open > .dropdown-menu { display: block; } .open > a { outline: 0; } .dropdown-header { display: block; padding: 3px 20px; font-size: 12px; line-height: 1.428571429; color: #999999; } .dropdown-backdrop { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 990; } .pull-right > .dropdown-menu { right: 0; left: auto; } .dropup .caret, .navbar-fixed-bottom .dropdown .caret { border-top: 0 dotted; border-bottom: 4px solid #000000; content: ""; } .dropup .dropdown-menu, .navbar-fixed-bottom .dropdown .dropdown-menu { top: auto; bottom: 100%; margin-bottom: 1px; } @media (min-width: 768px) { .navbar-right .dropdown-menu { right: 0; left: auto; } } .btn-default .caret { border-top-color: #333333; } .btn-primary .caret, .btn-success .caret, .btn-warning .caret, .btn-danger .caret, .btn-info .caret { border-top-color: #fff; } .dropup .btn-default .caret { border-bottom-color: #333333; } .dropup .btn-primary .caret, .dropup .btn-success .caret, .dropup .btn-warning .caret, .dropup .btn-danger .caret, .dropup .btn-info .caret { border-bottom-color: #fff; } .btn-group, .btn-group-vertical { position: relative; display: inline-block; vertical-align: middle; } .btn-group > .btn, .btn-group-vertical > .btn { position: relative; float: left; } .btn-group > .btn:hover, .btn-group-vertical > .btn:hover, .btn-group > .btn:focus, .btn-group-vertical > .btn:focus, .btn-group > .btn:active, .btn-group-vertical > .btn:active, .btn-group > .btn.active, .btn-group-vertical > .btn.active { z-index: 2; } .btn-group > .btn:focus, .btn-group-vertical > .btn:focus { outline: none; } .btn-group .btn + .btn, .btn-group .btn + .btn-group, .btn-group .btn-group + .btn, .btn-group .btn-group + .btn-group { margin-left: -1px; } .btn-toolbar:before, .btn-toolbar:after { display: table; content: " "; } .btn-toolbar:after { clear: both; } .btn-toolbar:before, .btn-toolbar:after { display: table; content: " "; } .btn-toolbar:after { clear: both; } .btn-toolbar .btn-group { float: left; } .btn-toolbar > .btn + .btn, .btn-toolbar > .btn-group + .btn, .btn-toolbar > .btn + .btn-group, .btn-toolbar > .btn-group + .btn-group { margin-left: 5px; } .btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { border-radius: 0; } .btn-group > .btn:first-child { margin-left: 0; } .btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .btn-group > .btn:last-child:not(:first-child), .btn-group > .dropdown-toggle:not(:first-child) { border-bottom-left-radius: 0; border-top-left-radius: 0; } .btn-group > .btn-group { float: left; } .btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group > .btn-group:first-child > .btn:last-child, .btn-group > .btn-group:first-child > .dropdown-toggle { border-top-right-radius: 0; border-bottom-right-radius: 0; } .btn-group > .btn-group:last-child > .btn:first-child { border-bottom-left-radius: 0; border-top-left-radius: 0; } .btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle { outline: 0; } .btn-group-xs > .btn { padding: 5px 10px; padding: 1px 5px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .btn-group-sm > .btn { padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .btn-group-lg > .btn { padding: 10px 16px; font-size: 18px; line-height: 1.33; border-radius: 6px; } .btn-group > .btn + .dropdown-toggle { padding-right: 8px; padding-left: 8px; } .btn-group > .btn-lg + .dropdown-toggle { padding-right: 12px; padding-left: 12px; } .btn-group.open .dropdown-toggle { -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); } .btn .caret { margin-left: 0; } .btn-lg .caret { border-width: 5px 5px 0; border-bottom-width: 0; } .dropup .btn-lg .caret { border-width: 0 5px 5px; } .btn-group-vertical > .btn, .btn-group-vertical > .btn-group { display: block; float: none; width: 100%; max-width: 100%; } .btn-group-vertical > .btn-group:before, .btn-group-vertical > .btn-group:after { display: table; content: " "; } .btn-group-vertical > .btn-group:after { clear: both; } .btn-group-vertical > .btn-group:before, .btn-group-vertical > .btn-group:after { display: table; content: " "; } .btn-group-vertical > .btn-group:after { clear: both; } .btn-group-vertical > .btn-group > .btn { float: none; } .btn-group-vertical > .btn + .btn, .btn-group-vertical > .btn + .btn-group, .btn-group-vertical > .btn-group + .btn, .btn-group-vertical > .btn-group + .btn-group { margin-top: -1px; margin-left: 0; } .btn-group-vertical > .btn:not(:first-child):not(:last-child) { border-radius: 0; } .btn-group-vertical > .btn:first-child:not(:last-child) { border-top-right-radius: 4px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn:last-child:not(:first-child) { border-top-right-radius: 0; border-bottom-left-radius: 4px; border-top-left-radius: 0; } .btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group-vertical > .btn-group:first-child > .btn:last-child, .btn-group-vertical > .btn-group:first-child > .dropdown-toggle { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn-group:last-child > .btn:first-child { border-top-right-radius: 0; border-top-left-radius: 0; } .btn-group-justified { display: table; width: 100%; border-collapse: separate; table-layout: fixed; } .btn-group-justified .btn { display: table-cell; float: none; width: 1%; } [data-toggle="buttons"] > .btn > input[type="radio"], [data-toggle="buttons"] > .btn > input[type="checkbox"] { display: none; } .input-group { position: relative; display: table; border-collapse: separate; } .input-group.col { float: none; padding-right: 0; padding-left: 0; } .input-group .form-control { width: 100%; margin-bottom: 0; } .input-group-lg > .form-control, .input-group-lg > .input-group-addon, .input-group-lg > .input-group-btn > .btn { height: 45px; padding: 10px 16px; font-size: 18px; line-height: 1.33; border-radius: 6px; } select.input-group-lg > .form-control, select.input-group-lg > .input-group-addon, select.input-group-lg > .input-group-btn > .btn { height: 45px; line-height: 45px; } textarea.input-group-lg > .form-control, textarea.input-group-lg > .input-group-addon, textarea.input-group-lg > .input-group-btn > .btn { height: auto; } .input-group-sm > .form-control, .input-group-sm > .input-group-addon, .input-group-sm > .input-group-btn > .btn { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } select.input-group-sm > .form-control, select.input-group-sm > .input-group-addon, select.input-group-sm > .input-group-btn > .btn { height: 30px; line-height: 30px; } textarea.input-group-sm > .form-control, textarea.input-group-sm > .input-group-addon, textarea.input-group-sm > .input-group-btn > .btn { height: auto; } .input-group-addon, .input-group-btn, .input-group .form-control { display: table-cell; } .input-group-addon:not(:first-child):not(:last-child), .input-group-btn:not(:first-child):not(:last-child), .input-group .form-control:not(:first-child):not(:last-child) { border-radius: 0; } .input-group-addon, .input-group-btn { width: 1%; white-space: nowrap; vertical-align: middle; } .input-group-addon { padding: 6px 12px; font-size: 14px; font-weight: normal; line-height: 1; text-align: center; background-color: #eeeeee; border: 1px solid #cccccc; border-radius: 4px; } .input-group-addon.input-sm { padding: 5px 10px; font-size: 12px; border-radius: 3px; } .input-group-addon.input-lg { padding: 10px 16px; font-size: 18px; border-radius: 6px; } .input-group-addon input[type="radio"], .input-group-addon input[type="checkbox"] { margin-top: 0; } .input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child > .btn, .input-group-btn:first-child > .dropdown-toggle, .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .input-group-addon:first-child { border-right: 0; } .input-group .form-control:last-child, .input-group-addon:last-child, .input-group-btn:last-child > .btn, .input-group-btn:last-child > .dropdown-toggle, .input-group-btn:first-child > .btn:not(:first-child) { border-bottom-left-radius: 0; border-top-left-radius: 0; } .input-group-addon:last-child { border-left: 0; } .input-group-btn { position: relative; white-space: nowrap; } .input-group-btn > .btn { position: relative; } .input-group-btn > .btn + .btn { margin-left: -4px; } .input-group-btn > .btn:hover, .input-group-btn > .btn:active { z-index: 2; } .nav { padding-left: 0; margin-bottom: 0; list-style: none; } .nav:before, .nav:after { display: table; content: " "; } .nav:after { clear: both; } .nav:before, .nav:after { display: table; content: " "; } .nav:after { clear: both; } .nav > li { position: relative; display: block; } .nav > li > a { position: relative; display: block; padding: 10px 15px; } .nav > li > a:hover, .nav > li > a:focus { text-decoration: none; background-color: #eeeeee; } .nav > li.disabled > a { color: #999999; } .nav > li.disabled > a:hover, .nav > li.disabled > a:focus { color: #999999; text-decoration: none; cursor: not-allowed; background-color: transparent; } .nav .open > a, .nav .open > a:hover, .nav .open > a:focus { background-color: #eeeeee; border-color: #428bca; } .nav .nav-divider { height: 1px; margin: 9px 0; overflow: hidden; background-color: #e5e5e5; } .nav > li > a > img { max-width: none; } .nav-tabs { border-bottom: 1px solid #dddddd; } .nav-tabs > li { float: left; margin-bottom: -1px; } .nav-tabs > li > a { margin-right: 2px; line-height: 1.428571429; border: 1px solid transparent; border-radius: 4px 4px 0 0; } .nav-tabs > li > a:hover { border-color: #eeeeee #eeeeee #dddddd; } .nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus { color: #555555; cursor: default; background-color: #ffffff; border: 1px solid #dddddd; border-bottom-color: transparent; } .nav-tabs.nav-justified { width: 100%; border-bottom: 0; } .nav-tabs.nav-justified > li { float: none; } .nav-tabs.nav-justified > li > a { text-align: center; } @media (min-width: 768px) { .nav-tabs.nav-justified > li { display: table-cell; width: 1%; } } .nav-tabs.nav-justified > li > a { margin-right: 0; border-bottom: 1px solid #dddddd; } .nav-tabs.nav-justified > .active > a { border-bottom-color: #ffffff; } .nav-pills > li { float: left; } .nav-pills > li > a { border-radius: 5px; } .nav-pills > li + li { margin-left: 2px; } .nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus { color: #ffffff; background-color: #428bca; } .nav-stacked > li { float: none; } .nav-stacked > li + li { margin-top: 2px; margin-left: 0; } .nav-justified { width: 100%; } .nav-justified > li { float: none; } .nav-justified > li > a { text-align: center; } @media (min-width: 768px) { .nav-justified > li { display: table-cell; width: 1%; } } .nav-tabs-justified { border-bottom: 0; } .nav-tabs-justified > li > a { margin-right: 0; border-bottom: 1px solid #dddddd; } .nav-tabs-justified > .active > a { border-bottom-color: #ffffff; } .tabbable:before, .tabbable:after { display: table; content: " "; } .tabbable:after { clear: both; } .tabbable:before, .tabbable:after { display: table; content: " "; } .tabbable:after { clear: both; } .tab-content > .tab-pane, .pill-content > .pill-pane { display: none; } .tab-content > .active, .pill-content > .active { display: block; } .nav .caret { border-top-color: #428bca; border-bottom-color: #428bca; } .nav a:hover .caret { border-top-color: #2a6496; border-bottom-color: #2a6496; } .nav-tabs .dropdown-menu { margin-top: -1px; border-top-right-radius: 0; border-top-left-radius: 0; } .navbar { position: relative; z-index: 1000; min-height: 50px; margin-bottom: 20px; border: 1px solid transparent; } .navbar:before, .navbar:after { display: table; content: " "; } .navbar:after { clear: both; } .navbar:before, .navbar:after { display: table; content: " "; } .navbar:after { clear: both; } @media (min-width: 768px) { .navbar { border-radius: 4px; } } .navbar-header:before, .navbar-header:after { display: table; content: " "; } .navbar-header:after { clear: both; } .navbar-header:before, .navbar-header:after { display: table; content: " "; } .navbar-header:after { clear: both; } @media (min-width: 768px) { .navbar-header { float: left; } } .navbar-collapse { max-height: 340px; padding-right: 15px; padding-left: 15px; overflow-x: visible; border-top: 1px solid transparent; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); -webkit-overflow-scrolling: touch; } .navbar-collapse:before, .navbar-collapse:after { display: table; content: " "; } .navbar-collapse:after { clear: both; } .navbar-collapse:before, .navbar-collapse:after { display: table; content: " "; } .navbar-collapse:after { clear: both; } .navbar-collapse.in { overflow-y: auto; } @media (min-width: 768px) { .navbar-collapse { width: auto; border-top: 0; box-shadow: none; } .navbar-collapse.collapse { display: block !important; height: auto !important; padding-bottom: 0; overflow: visible !important; } .navbar-collapse.in { overflow-y: visible; } .navbar-collapse .navbar-nav.navbar-left:first-child { margin-left: -15px; } .navbar-collapse .navbar-nav.navbar-right:last-child { margin-right: -15px; } .navbar-collapse .navbar-text:last-child { margin-right: 0; } } .container > .navbar-header, .container > .navbar-collapse { margin-right: -15px; margin-left: -15px; } @media (min-width: 768px) { .container > .navbar-header, .container > .navbar-collapse { margin-right: 0; margin-left: 0; } } .navbar-static-top { border-width: 0 0 1px; } @media (min-width: 768px) { .navbar-static-top { border-radius: 0; } } .navbar-fixed-top, .navbar-fixed-bottom { position: fixed; right: 0; left: 0; border-width: 0 0 1px; } @media (min-width: 768px) { .navbar-fixed-top, .navbar-fixed-bottom { border-radius: 0; } } .navbar-fixed-top { top: 0; z-index: 1030; } .navbar-fixed-bottom { bottom: 0; margin-bottom: 0; } .navbar-brand { float: left; padding: 15px 15px; font-size: 18px; line-height: 20px; } .navbar-brand:hover, .navbar-brand:focus { text-decoration: none; } @media (min-width: 768px) { .navbar > .container .navbar-brand { margin-left: -15px; } } .navbar-toggle { position: relative; float: right; padding: 9px 10px; margin-top: 8px; margin-right: 15px; margin-bottom: 8px; background-color: transparent; border: 1px solid transparent; border-radius: 4px; } .navbar-toggle .icon-bar { display: block; width: 22px; height: 2px; border-radius: 1px; } .navbar-toggle .icon-bar + .icon-bar { margin-top: 4px; } @media (min-width: 768px) { .navbar-toggle { display: none; } } .navbar-nav { margin: 7.5px -15px; } .navbar-nav > li > a { padding-top: 10px; padding-bottom: 10px; line-height: 20px; } @media (max-width: 767px) { .navbar-nav .open .dropdown-menu { position: static; float: none; width: auto; margin-top: 0; background-color: transparent; border: 0; box-shadow: none; } .navbar-nav .open .dropdown-menu > li > a, .navbar-nav .open .dropdown-menu .dropdown-header { padding: 5px 15px 5px 25px; } .navbar-nav .open .dropdown-menu > li > a { line-height: 20px; } .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-nav .open .dropdown-menu > li > a:focus { background-image: none; } } @media (min-width: 768px) { .navbar-nav { float: left; margin: 0; } .navbar-nav > li { float: left; } .navbar-nav > li > a { padding-top: 15px; padding-bottom: 15px; } } @media (min-width: 768px) { .navbar-left { float: left !important; } .navbar-right { float: right !important; } } .navbar-form { padding: 10px 15px; margin-top: 8px; margin-right: -15px; margin-bottom: 8px; margin-left: -15px; border-top: 1px solid transparent; border-bottom: 1px solid transparent; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); } @media (min-width: 768px) { .navbar-form .form-group { display: inline-block; margin-bottom: 0; vertical-align: middle; } .navbar-form .form-control { display: inline-block; } .navbar-form .radio, .navbar-form .checkbox { display: inline-block; padding-left: 0; margin-top: 0; margin-bottom: 0; } .navbar-form .radio input[type="radio"], .navbar-form .checkbox input[type="checkbox"] { float: none; margin-left: 0; } } @media (max-width: 767px) { .navbar-form .form-group { margin-bottom: 5px; } } @media (min-width: 768px) { .navbar-form { width: auto; padding-top: 0; padding-bottom: 0; margin-right: 0; margin-left: 0; border: 0; -webkit-box-shadow: none; box-shadow: none; } } .navbar-nav > li > .dropdown-menu { margin-top: 0; border-top-right-radius: 0; border-top-left-radius: 0; } .navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .navbar-nav.pull-right > li > .dropdown-menu, .navbar-nav > li > .dropdown-menu.pull-right { right: 0; left: auto; } .navbar-btn { margin-top: 8px; margin-bottom: 8px; } .navbar-text { float: left; margin-top: 15px; margin-bottom: 15px; } @media (min-width: 768px) { .navbar-text { margin-right: 15px; margin-left: 15px; } } .navbar-default { background-color: #f8f8f8; border-color: #e7e7e7; } .navbar-default .navbar-brand { color: #777777; } .navbar-default .navbar-brand:hover, .navbar-default .navbar-brand:focus { color: #5e5e5e; background-color: transparent; } .navbar-default .navbar-text { color: #777777; } .navbar-default .navbar-nav > li > a { color: #777777; } .navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus { color: #333333; background-color: transparent; } .navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:hover, .navbar-default .navbar-nav > .active > a:focus { color: #555555; background-color: #e7e7e7; } .navbar-default .navbar-nav > .disabled > a, .navbar-default .navbar-nav > .disabled > a:hover, .navbar-default .navbar-nav > .disabled > a:focus { color: #cccccc; background-color: transparent; } .navbar-default .navbar-toggle { border-color: #dddddd; } .navbar-default .navbar-toggle:hover, .navbar-default .navbar-toggle:focus { background-color: #dddddd; } .navbar-default .navbar-toggle .icon-bar { background-color: #cccccc; } .navbar-default .navbar-collapse, .navbar-default .navbar-form { border-color: #e6e6e6; } .navbar-default .navbar-nav > .dropdown > a:hover .caret, .navbar-default .navbar-nav > .dropdown > a:focus .caret { border-top-color: #333333; border-bottom-color: #333333; } .navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus { color: #555555; background-color: #e7e7e7; } .navbar-default .navbar-nav > .open > a .caret, .navbar-default .navbar-nav > .open > a:hover .caret, .navbar-default .navbar-nav > .open > a:focus .caret { border-top-color: #555555; border-bottom-color: #555555; } .navbar-default .navbar-nav > .dropdown > a .caret { border-top-color: #777777; border-bottom-color: #777777; } @media (max-width: 767px) { .navbar-default .navbar-nav .open .dropdown-menu > li > a { color: #777777; } .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { color: #333333; background-color: transparent; } .navbar-default .navbar-nav .open .dropdown-menu > .active > a, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { color: #555555; background-color: #e7e7e7; } .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { color: #cccccc; background-color: transparent; } } .navbar-default .navbar-link { color: #777777; } .navbar-default .navbar-link:hover { color: #333333; } .navbar-inverse { background-color: #222222; border-color: #080808; } .navbar-inverse .navbar-brand { color: #999999; } .navbar-inverse .navbar-brand:hover, .navbar-inverse .navbar-brand:focus { color: #ffffff; background-color: transparent; } .navbar-inverse .navbar-text { color: #999999; } .navbar-inverse .navbar-nav > li > a { color: #999999; } .navbar-inverse .navbar-nav > li > a:hover, .navbar-inverse .navbar-nav > li > a:focus { color: #ffffff; background-color: transparent; } .navbar-inverse .navbar-nav > .active > a, .navbar-inverse .navbar-nav > .active > a:hover, .navbar-inverse .navbar-nav > .active > a:focus { color: #ffffff; background-color: #080808; } .navbar-inverse .navbar-nav > .disabled > a, .navbar-inverse .navbar-nav > .disabled > a:hover, .navbar-inverse .navbar-nav > .disabled > a:focus { color: #444444; background-color: transparent; } .navbar-inverse .navbar-toggle { border-color: #333333; } .navbar-inverse .navbar-toggle:hover, .navbar-inverse .navbar-toggle:focus { background-color: #333333; } .navbar-inverse .navbar-toggle .icon-bar { background-color: #ffffff; } .navbar-inverse .navbar-collapse, .navbar-inverse .navbar-form { border-color: #101010; } .navbar-inverse .navbar-nav > .open > a, .navbar-inverse .navbar-nav > .open > a:hover, .navbar-inverse .navbar-nav > .open > a:focus { color: #ffffff; background-color: #080808; } .navbar-inverse .navbar-nav > .dropdown > a:hover .caret { border-top-color: #ffffff; border-bottom-color: #ffffff; } .navbar-inverse .navbar-nav > .dropdown > a .caret { border-top-color: #999999; border-bottom-color: #999999; } .navbar-inverse .navbar-nav > .open > a .caret, .navbar-inverse .navbar-nav > .open > a:hover .caret, .navbar-inverse .navbar-nav > .open > a:focus .caret { border-top-color: #ffffff; border-bottom-color: #ffffff; } @media (max-width: 767px) { .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { border-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { color: #999999; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { color: #ffffff; background-color: transparent; } .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { color: #ffffff; background-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { color: #444444; background-color: transparent; } } .navbar-inverse .navbar-link { color: #999999; } .navbar-inverse .navbar-link:hover { color: #ffffff; } .breadcrumb { padding: 8px 15px; margin-bottom: 20px; list-style: none; background-color: #f5f5f5; border-radius: 4px; } .breadcrumb > li { display: inline-block; } .breadcrumb > li + li:before { padding: 0 5px; color: #cccccc; content: "/\00a0"; } .breadcrumb > .active { color: #999999; } .pagination { display: inline-block; padding-left: 0; margin: 20px 0; border-radius: 4px; } .pagination > li { display: inline; } .pagination > li > a, .pagination > li > span { position: relative; float: left; padding: 6px 12px; margin-left: -1px; line-height: 1.428571429; text-decoration: none; background-color: #ffffff; border: 1px solid #dddddd; } .pagination > li:first-child > a, .pagination > li:first-child > span { margin-left: 0; border-bottom-left-radius: 4px; border-top-left-radius: 4px; } .pagination > li:last-child > a, .pagination > li:last-child > span { border-top-right-radius: 4px; border-bottom-right-radius: 4px; } .pagination > li > a:hover, .pagination > li > span:hover, .pagination > li > a:focus, .pagination > li > span:focus { background-color: #eeeeee; } .pagination > .active > a, .pagination > .active > span, .pagination > .active > a:hover, .pagination > .active > span:hover, .pagination > .active > a:focus, .pagination > .active > span:focus { z-index: 2; color: #ffffff; cursor: default; background-color: #428bca; border-color: #428bca; } .pagination > .disabled > span, .pagination > .disabled > a, .pagination > .disabled > a:hover, .pagination > .disabled > a:focus { color: #999999; cursor: not-allowed; background-color: #ffffff; border-color: #dddddd; } .pagination-lg > li > a, .pagination-lg > li > span { padding: 10px 16px; font-size: 18px; } .pagination-lg > li:first-child > a, .pagination-lg > li:first-child > span { border-bottom-left-radius: 6px; border-top-left-radius: 6px; } .pagination-lg > li:last-child > a, .pagination-lg > li:last-child > span { border-top-right-radius: 6px; border-bottom-right-radius: 6px; } .pagination-sm > li > a, .pagination-sm > li > span { padding: 5px 10px; font-size: 12px; } .pagination-sm > li:first-child > a, .pagination-sm > li:first-child > span { border-bottom-left-radius: 3px; border-top-left-radius: 3px; } .pagination-sm > li:last-child > a, .pagination-sm > li:last-child > span { border-top-right-radius: 3px; border-bottom-right-radius: 3px; } .pager { padding-left: 0; margin: 20px 0; text-align: center; list-style: none; } .pager:before, .pager:after { display: table; content: " "; } .pager:after { clear: both; } .pager:before, .pager:after { display: table; content: " "; } .pager:after { clear: both; } .pager li { display: inline; } .pager li > a, .pager li > span { display: inline-block; padding: 5px 14px; background-color: #ffffff; border: 1px solid #dddddd; border-radius: 15px; } .pager li > a:hover, .pager li > a:focus { text-decoration: none; background-color: #eeeeee; } .pager .next > a, .pager .next > span { float: right; } .pager .previous > a, .pager .previous > span { float: left; } .pager .disabled > a, .pager .disabled > a:hover, .pager .disabled > a:focus, .pager .disabled > span { color: #999999; cursor: not-allowed; background-color: #ffffff; } .label { display: inline; padding: .2em .6em .3em; font-size: 75%; font-weight: bold; line-height: 1; color: #ffffff; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; } .label[href]:hover, .label[href]:focus { color: #ffffff; text-decoration: none; cursor: pointer; } .label:empty { display: none; } .label-default { background-color: #999999; } .label-default[href]:hover, .label-default[href]:focus { background-color: #808080; } .label-primary { background-color: #428bca; } .label-primary[href]:hover, .label-primary[href]:focus { background-color: #3071a9; } .label-success { background-color: #5cb85c; } .label-success[href]:hover, .label-success[href]:focus { background-color: #449d44; } .label-info { background-color: #5bc0de; } .label-info[href]:hover, .label-info[href]:focus { background-color: #31b0d5; } .label-warning { background-color: #f0ad4e; } .label-warning[href]:hover, .label-warning[href]:focus { background-color: #ec971f; } .label-danger { background-color: #d9534f; } .label-danger[href]:hover, .label-danger[href]:focus { background-color: #c9302c; } .badge { display: inline-block; min-width: 10px; padding: 3px 7px; font-size: 12px; font-weight: bold; line-height: 1; color: #ffffff; text-align: center; white-space: nowrap; vertical-align: baseline; background-color: #999999; border-radius: 10px; } .badge:empty { display: none; } a.badge:hover, a.badge:focus { color: #ffffff; text-decoration: none; cursor: pointer; } .btn .badge { position: relative; top: -1px; } a.list-group-item.active > .badge, .nav-pills > .active > a > .badge { color: #428bca; background-color: #ffffff; } .nav-pills > li > a > .badge { margin-left: 3px; } .jumbotron { padding: 30px; margin-bottom: 30px; font-size: 21px; font-weight: 200; line-height: 2.1428571435; color: inherit; background-color: #eeeeee; } .jumbotron h1 { line-height: 1; color: inherit; } .jumbotron p { line-height: 1.4; } .container .jumbotron { border-radius: 6px; } @media screen and (min-width: 768px) { .jumbotron { padding-top: 48px; padding-bottom: 48px; } .container .jumbotron { padding-right: 60px; padding-left: 60px; } .jumbotron h1 { font-size: 63px; } } .thumbnail { display: inline-block; display: block; height: auto; max-width: 100%; padding: 4px; line-height: 1.428571429; background-color: #ffffff; border: 1px solid #dddddd; border-radius: 4px; -webkit-transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out; } .thumbnail > img { display: block; height: auto; max-width: 100%; } a.thumbnail:hover, a.thumbnail:focus { border-color: #428bca; } .thumbnail > img { margin-right: auto; margin-left: auto; } .thumbnail .caption { padding: 9px; color: #333333; } .alert { padding: 15px; margin-bottom: 20px; border: 1px solid transparent; border-radius: 4px; } .alert h4 { margin-top: 0; color: inherit; } .alert .alert-link { font-weight: bold; } .alert > p, .alert > ul { margin-bottom: 0; } .alert > p + p { margin-top: 5px; } .alert-dismissable { padding-right: 35px; } .alert-dismissable .close { position: relative; top: -2px; right: -21px; color: inherit; } .alert-success { color: #468847; background-color: #dff0d8; border-color: #d6e9c6; } .alert-success hr { border-top-color: #c9e2b3; } .alert-success .alert-link { color: #356635; } .alert-info { color: #3a87ad; background-color: #d9edf7; border-color: #bce8f1; } .alert-info hr { border-top-color: #a6e1ec; } .alert-info .alert-link { color: #2d6987; } .alert-warning { color: #c09853; background-color: #fcf8e3; border-color: #fbeed5; } .alert-warning hr { border-top-color: #f8e5be; } .alert-warning .alert-link { color: #a47e3c; } .alert-danger { color: #b94a48; background-color: #f2dede; border-color: #eed3d7; } .alert-danger hr { border-top-color: #e6c1c7; } .alert-danger .alert-link { color: #953b39; } @-webkit-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } @-moz-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } @-o-keyframes progress-bar-stripes { from { background-position: 0 0; } to { background-position: 40px 0; } } @keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } .progress { height: 20px; margin-bottom: 20px; overflow: hidden; background-color: #f5f5f5; border-radius: 4px; -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); } .progress-bar { float: left; width: 0; height: 100%; font-size: 12px; color: #ffffff; text-align: center; background-color: #428bca; -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); -webkit-transition: width 0.6s ease; transition: width 0.6s ease; } .progress-striped .progress-bar { background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-size: 40px 40px; } .progress.active .progress-bar { -webkit-animation: progress-bar-stripes 2s linear infinite; -moz-animation: progress-bar-stripes 2s linear infinite; -ms-animation: progress-bar-stripes 2s linear infinite; -o-animation: progress-bar-stripes 2s linear infinite; animation: progress-bar-stripes 2s linear infinite; } .progress-bar-success { background-color: #5cb85c; } .progress-striped .progress-bar-success { background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-bar-info { background-color: #5bc0de; } .progress-striped .progress-bar-info { background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-bar-warning { background-color: #f0ad4e; } .progress-striped .progress-bar-warning { background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-bar-danger { background-color: #d9534f; } .progress-striped .progress-bar-danger { background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .media, .media-body { overflow: hidden; zoom: 1; } .media, .media .media { margin-top: 15px; } .media:first-child { margin-top: 0; } .media-object { display: block; } .media-heading { margin: 0 0 5px; } .media > .pull-left { margin-right: 10px; } .media > .pull-right { margin-left: 10px; } .media-list { padding-left: 0; list-style: none; } .list-group { padding-left: 0; margin-bottom: 20px; } .list-group-item { position: relative; display: block; padding: 10px 15px; margin-bottom: -1px; background-color: #ffffff; border: 1px solid #dddddd; } .list-group-item:first-child { border-top-right-radius: 4px; border-top-left-radius: 4px; } .list-group-item:last-child { margin-bottom: 0; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; } .list-group-item > .badge { float: right; } .list-group-item > .badge + .badge { margin-right: 5px; } a.list-group-item { color: #555555; } a.list-group-item .list-group-item-heading { color: #333333; } a.list-group-item:hover, a.list-group-item:focus { text-decoration: none; background-color: #f5f5f5; } .list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus { z-index: 2; color: #ffffff; background-color: #428bca; border-color: #428bca; } .list-group-item.active .list-group-item-heading, .list-group-item.active:hover .list-group-item-heading, .list-group-item.active:focus .list-group-item-heading { color: inherit; } .list-group-item.active .list-group-item-text, .list-group-item.active:hover .list-group-item-text, .list-group-item.active:focus .list-group-item-text { color: #e1edf7; } .list-group-item-heading { margin-top: 0; margin-bottom: 5px; } .list-group-item-text { margin-bottom: 0; line-height: 1.3; } .panel { margin-bottom: 20px; background-color: #ffffff; border: 1px solid transparent; border-radius: 4px; -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); } .panel-body { padding: 15px; } .panel-body:before, .panel-body:after { display: table; content: " "; } .panel-body:after { clear: both; } .panel-body:before, .panel-body:after { display: table; content: " "; } .panel-body:after { clear: both; } .panel > .list-group { margin-bottom: 0; } .panel > .list-group .list-group-item { border-width: 1px 0; } .panel > .list-group .list-group-item:first-child { border-top-right-radius: 0; border-top-left-radius: 0; } .panel > .list-group .list-group-item:last-child { border-bottom: 0; } .panel-heading + .list-group .list-group-item:first-child { border-top-width: 0; } .panel > .table { margin-bottom: 0; } .panel > .panel-body + .table { border-top: 1px solid #dddddd; } .panel-heading { padding: 10px 15px; border-bottom: 1px solid transparent; border-top-right-radius: 3px; border-top-left-radius: 3px; } .panel-title { margin-top: 0; margin-bottom: 0; font-size: 16px; } .panel-title > a { color: inherit; } .panel-footer { padding: 10px 15px; background-color: #f5f5f5; border-top: 1px solid #dddddd; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } .panel-group .panel { margin-bottom: 0; overflow: hidden; border-radius: 4px; } .panel-group .panel + .panel { margin-top: 5px; } .panel-group .panel-heading { border-bottom: 0; } .panel-group .panel-heading + .panel-collapse .panel-body { border-top: 1px solid #dddddd; } .panel-group .panel-footer { border-top: 0; } .panel-group .panel-footer + .panel-collapse .panel-body { border-bottom: 1px solid #dddddd; } .panel-default { border-color: #dddddd; } .panel-default > .panel-heading { color: #333333; background-color: #f5f5f5; border-color: #dddddd; } .panel-default > .panel-heading + .panel-collapse .panel-body { border-top-color: #dddddd; } .panel-default > .panel-footer + .panel-collapse .panel-body { border-bottom-color: #dddddd; } .panel-primary { border-color: #428bca; } .panel-primary > .panel-heading { color: #ffffff; background-color: #428bca; border-color: #428bca; } .panel-primary > .panel-heading + .panel-collapse .panel-body { border-top-color: #428bca; } .panel-primary > .panel-footer + .panel-collapse .panel-body { border-bottom-color: #428bca; } .panel-success { border-color: #d6e9c6; } .panel-success > .panel-heading { color: #468847; background-color: #dff0d8; border-color: #d6e9c6; } .panel-success > .panel-heading + .panel-collapse .panel-body { border-top-color: #d6e9c6; } .panel-success > .panel-footer + .panel-collapse .panel-body { border-bottom-color: #d6e9c6; } .panel-warning { border-color: #fbeed5; } .panel-warning > .panel-heading { color: #c09853; background-color: #fcf8e3; border-color: #fbeed5; } .panel-warning > .panel-heading + .panel-collapse .panel-body { border-top-color: #fbeed5; } .panel-warning > .panel-footer + .panel-collapse .panel-body { border-bottom-color: #fbeed5; } .panel-danger { border-color: #eed3d7; } .panel-danger > .panel-heading { color: #b94a48; background-color: #f2dede; border-color: #eed3d7; } .panel-danger > .panel-heading + .panel-collapse .panel-body { border-top-color: #eed3d7; } .panel-danger > .panel-footer + .panel-collapse .panel-body { border-bottom-color: #eed3d7; } .panel-info { border-color: #bce8f1; } .panel-info > .panel-heading { color: #3a87ad; background-color: #d9edf7; border-color: #bce8f1; } .panel-info > .panel-heading + .panel-collapse .panel-body { border-top-color: #bce8f1; } .panel-info > .panel-footer + .panel-collapse .panel-body { border-bottom-color: #bce8f1; } .well { min-height: 20px; padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #e3e3e3; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); } .well blockquote { border-color: #ddd; border-color: rgba(0, 0, 0, 0.15); } .well-lg { padding: 24px; border-radius: 6px; } .well-sm { padding: 9px; border-radius: 3px; } .close { float: right; font-size: 21px; font-weight: bold; line-height: 1; color: #000000; text-shadow: 0 1px 0 #ffffff; opacity: 0.2; filter: alpha(opacity=20); } .close:hover, .close:focus { color: #000000; text-decoration: none; cursor: pointer; opacity: 0.5; filter: alpha(opacity=50); } button.close { padding: 0; cursor: pointer; background: transparent; border: 0; -webkit-appearance: none; } .modal-open { overflow: hidden; } body.modal-open, .modal-open .navbar-fixed-top, .modal-open .navbar-fixed-bottom { margin-right: 15px; } .modal { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1040; display: none; overflow: auto; overflow-y: scroll; } .modal.fade .modal-dialog { -webkit-transform: translate(0, -25%); -ms-transform: translate(0, -25%); transform: translate(0, -25%); -webkit-transition: -webkit-transform 0.3s ease-out; -moz-transition: -moz-transform 0.3s ease-out; -o-transition: -o-transform 0.3s ease-out; transition: transform 0.3s ease-out; } .modal.in .modal-dialog { -webkit-transform: translate(0, 0); -ms-transform: translate(0, 0); transform: translate(0, 0); } .modal-dialog { z-index: 1050; width: auto; padding: 10px; margin-right: auto; margin-left: auto; } .modal-content { position: relative; background-color: #ffffff; border: 1px solid #999999; border: 1px solid rgba(0, 0, 0, 0.2); border-radius: 6px; outline: none; -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); background-clip: padding-box; } .modal-backdrop { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1030; background-color: #000000; } .modal-backdrop.fade { opacity: 0; filter: alpha(opacity=0); } .modal-backdrop.in { opacity: 0.5; filter: alpha(opacity=50); } .modal-header { min-height: 16.428571429px; padding: 15px; border-bottom: 1px solid #e5e5e5; } .modal-header .close { margin-top: -2px; } .modal-title { margin: 0; line-height: 1.428571429; } .modal-body { position: relative; padding: 20px; } .modal-footer { padding: 19px 20px 20px; margin-top: 15px; text-align: right; border-top: 1px solid #e5e5e5; } .modal-footer:before, .modal-footer:after { display: table; content: " "; } .modal-footer:after { clear: both; } .modal-footer:before, .modal-footer:after { display: table; content: " "; } .modal-footer:after { clear: both; } .modal-footer .btn + .btn { margin-bottom: 0; margin-left: 5px; } .modal-footer .btn-group .btn + .btn { margin-left: -1px; } .modal-footer .btn-block + .btn-block { margin-left: 0; } @media screen and (min-width: 768px) { .modal-dialog { right: auto; left: 50%; width: 600px; padding-top: 30px; padding-bottom: 30px; } .modal-content { -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); } } .tooltip { position: absolute; z-index: 1030; display: block; font-size: 12px; line-height: 1.4; opacity: 0; filter: alpha(opacity=0); visibility: visible; } .tooltip.in { opacity: 0.9; filter: alpha(opacity=90); } .tooltip.top { padding: 5px 0; margin-top: -3px; } .tooltip.right { padding: 0 5px; margin-left: 3px; } .tooltip.bottom { padding: 5px 0; margin-top: 3px; } .tooltip.left { padding: 0 5px; margin-left: -3px; } .tooltip-inner { max-width: 200px; padding: 3px 8px; color: #ffffff; text-align: center; text-decoration: none; background-color: #000000; border-radius: 4px; } .tooltip-arrow { position: absolute; width: 0; height: 0; border-color: transparent; border-style: solid; } .tooltip.top .tooltip-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top-color: #000000; border-width: 5px 5px 0; } .tooltip.top-left .tooltip-arrow { bottom: 0; left: 5px; border-top-color: #000000; border-width: 5px 5px 0; } .tooltip.top-right .tooltip-arrow { right: 5px; bottom: 0; border-top-color: #000000; border-width: 5px 5px 0; } .tooltip.right .tooltip-arrow { top: 50%; left: 0; margin-top: -5px; border-right-color: #000000; border-width: 5px 5px 5px 0; } .tooltip.left .tooltip-arrow { top: 50%; right: 0; margin-top: -5px; border-left-color: #000000; border-width: 5px 0 5px 5px; } .tooltip.bottom .tooltip-arrow { top: 0; left: 50%; margin-left: -5px; border-bottom-color: #000000; border-width: 0 5px 5px; } .tooltip.bottom-left .tooltip-arrow { top: 0; left: 5px; border-bottom-color: #000000; border-width: 0 5px 5px; } .tooltip.bottom-right .tooltip-arrow { top: 0; right: 5px; border-bottom-color: #000000; border-width: 0 5px 5px; } .popover { position: absolute; top: 0; left: 0; z-index: 1010; display: none; max-width: 276px; padding: 1px; text-align: left; white-space: normal; background-color: #ffffff; border: 1px solid #cccccc; border: 1px solid rgba(0, 0, 0, 0.2); border-radius: 6px; -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); background-clip: padding-box; } .popover.top { margin-top: -10px; } .popover.right { margin-left: 10px; } .popover.bottom { margin-top: 10px; } .popover.left { margin-left: -10px; } .popover-title { padding: 8px 14px; margin: 0; font-size: 14px; font-weight: normal; line-height: 18px; background-color: #f7f7f7; border-bottom: 1px solid #ebebeb; border-radius: 5px 5px 0 0; } .popover-content { padding: 9px 14px; } .popover .arrow, .popover .arrow:after { position: absolute; display: block; width: 0; height: 0; border-color: transparent; border-style: solid; } .popover .arrow { border-width: 11px; } .popover .arrow:after { border-width: 10px; content: ""; } .popover.top .arrow { bottom: -11px; left: 50%; margin-left: -11px; border-top-color: #999999; border-top-color: rgba(0, 0, 0, 0.25); border-bottom-width: 0; } .popover.top .arrow:after { bottom: 1px; margin-left: -10px; border-top-color: #ffffff; border-bottom-width: 0; content: " "; } .popover.right .arrow { top: 50%; left: -11px; margin-top: -11px; border-right-color: #999999; border-right-color: rgba(0, 0, 0, 0.25); border-left-width: 0; } .popover.right .arrow:after { bottom: -10px; left: 1px; border-right-color: #ffffff; border-left-width: 0; content: " "; } .popover.bottom .arrow { top: -11px; left: 50%; margin-left: -11px; border-bottom-color: #999999; border-bottom-color: rgba(0, 0, 0, 0.25); border-top-width: 0; } .popover.bottom .arrow:after { top: 1px; margin-left: -10px; border-bottom-color: #ffffff; border-top-width: 0; content: " "; } .popover.left .arrow { top: 50%; right: -11px; margin-top: -11px; border-left-color: #999999; border-left-color: rgba(0, 0, 0, 0.25); border-right-width: 0; } .popover.left .arrow:after { right: 1px; bottom: -10px; border-left-color: #ffffff; border-right-width: 0; content: " "; } .carousel { position: relative; } .carousel-inner { position: relative; width: 100%; overflow: hidden; } .carousel-inner > .item { position: relative; display: none; -webkit-transition: 0.6s ease-in-out left; transition: 0.6s ease-in-out left; } .carousel-inner > .item > img, .carousel-inner > .item > a > img { display: block; height: auto; max-width: 100%; line-height: 1; } .carousel-inner > .active, .carousel-inner > .next, .carousel-inner > .prev { display: block; } .carousel-inner > .active { left: 0; } .carousel-inner > .next, .carousel-inner > .prev { position: absolute; top: 0; width: 100%; } .carousel-inner > .next { left: 100%; } .carousel-inner > .prev { left: -100%; } .carousel-inner > .next.left, .carousel-inner > .prev.right { left: 0; } .carousel-inner > .active.left { left: -100%; } .carousel-inner > .active.right { left: 100%; } .carousel-control { position: absolute; top: 0; bottom: 0; left: 0; width: 15%; font-size: 20px; color: #ffffff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); opacity: 0.5; filter: alpha(opacity=50); } .carousel-control.left { background-image: -webkit-gradient(linear, 0 top, 100% top, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0.0001))); background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.5) 0), color-stop(rgba(0, 0, 0, 0.0001) 100%)); background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%); background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); } .carousel-control.right { right: 0; left: auto; background-image: -webkit-gradient(linear, 0 top, 100% top, from(rgba(0, 0, 0, 0.0001)), to(rgba(0, 0, 0, 0.5))); background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.0001) 0), color-stop(rgba(0, 0, 0, 0.5) 100%)); background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%); background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); } .carousel-control:hover, .carousel-control:focus { color: #ffffff; text-decoration: none; opacity: 0.9; filter: alpha(opacity=90); } .carousel-control .icon-prev, .carousel-control .icon-next, .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right { position: absolute; top: 50%; left: 50%; z-index: 5; display: inline-block; } .carousel-control .icon-prev, .carousel-control .icon-next { width: 20px; height: 20px; margin-top: -10px; margin-left: -10px; font-family: serif; } .carousel-control .icon-prev:before { content: '\2039'; } .carousel-control .icon-next:before { content: '\203a'; } .carousel-indicators { position: absolute; bottom: 10px; left: 50%; z-index: 15; width: 60%; padding-left: 0; margin-left: -30%; text-align: center; list-style: none; } .carousel-indicators li { display: inline-block; width: 10px; height: 10px; margin: 1px; text-indent: -999px; cursor: pointer; border: 1px solid #ffffff; border-radius: 10px; } .carousel-indicators .active { width: 12px; height: 12px; margin: 0; background-color: #ffffff; } .carousel-caption { position: absolute; right: 15%; bottom: 20px; left: 15%; z-index: 10; padding-top: 20px; padding-bottom: 20px; color: #ffffff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); } .carousel-caption .btn { text-shadow: none; } @media screen and (min-width: 768px) { .carousel-control .icon-prev, .carousel-control .icon-next { width: 30px; height: 30px; margin-top: -15px; margin-left: -15px; font-size: 30px; } .carousel-caption { right: 20%; left: 20%; padding-bottom: 30px; } .carousel-indicators { bottom: 20px; } } .clearfix:before, .clearfix:after { display: table; content: " "; } .clearfix:after { clear: both; } .pull-right { float: right !important; } .pull-left { float: left !important; } .hide { display: none !important; } .show { display: block !important; } .invisible { visibility: hidden; } .text-hide { font: 0/0 a; color: transparent; text-shadow: none; background-color: transparent; border: 0; } .affix { position: fixed; } @-ms-viewport { width: device-width; } @media screen and (max-width: 400px) { @-ms-viewport { width: 320px; } } .hidden { display: none !important; visibility: hidden !important; } .visible-xs { display: none !important; } tr.visible-xs { display: none !important; } th.visible-xs, td.visible-xs { display: none !important; } @media (max-width: 767px) { .visible-xs { display: block !important; } tr.visible-xs { display: table-row !important; } th.visible-xs, td.visible-xs { display: table-cell !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-xs.visible-sm { display: block !important; } tr.visible-xs.visible-sm { display: table-row !important; } th.visible-xs.visible-sm, td.visible-xs.visible-sm { display: table-cell !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-xs.visible-md { display: block !important; } tr.visible-xs.visible-md { display: table-row !important; } th.visible-xs.visible-md, td.visible-xs.visible-md { display: table-cell !important; } } @media (min-width: 1200px) { .visible-xs.visible-lg { display: block !important; } tr.visible-xs.visible-lg { display: table-row !important; } th.visible-xs.visible-lg, td.visible-xs.visible-lg { display: table-cell !important; } } .visible-sm { display: none !important; } tr.visible-sm { display: none !important; } th.visible-sm, td.visible-sm { display: none !important; } @media (max-width: 767px) { .visible-sm.visible-xs { display: block !important; } tr.visible-sm.visible-xs { display: table-row !important; } th.visible-sm.visible-xs, td.visible-sm.visible-xs { display: table-cell !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm { display: block !important; } tr.visible-sm { display: table-row !important; } th.visible-sm, td.visible-sm { display: table-cell !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-sm.visible-md { display: block !important; } tr.visible-sm.visible-md { display: table-row !important; } th.visible-sm.visible-md, td.visible-sm.visible-md { display: table-cell !important; } } @media (min-width: 1200px) { .visible-sm.visible-lg { display: block !important; } tr.visible-sm.visible-lg { display: table-row !important; } th.visible-sm.visible-lg, td.visible-sm.visible-lg { display: table-cell !important; } } .visible-md { display: none !important; } tr.visible-md { display: none !important; } th.visible-md, td.visible-md { display: none !important; } @media (max-width: 767px) { .visible-md.visible-xs { display: block !important; } tr.visible-md.visible-xs { display: table-row !important; } th.visible-md.visible-xs, td.visible-md.visible-xs { display: table-cell !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-md.visible-sm { display: block !important; } tr.visible-md.visible-sm { display: table-row !important; } th.visible-md.visible-sm, td.visible-md.visible-sm { display: table-cell !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md { display: block !important; } tr.visible-md { display: table-row !important; } th.visible-md, td.visible-md { display: table-cell !important; } } @media (min-width: 1200px) { .visible-md.visible-lg { display: block !important; } tr.visible-md.visible-lg { display: table-row !important; } th.visible-md.visible-lg, td.visible-md.visible-lg { display: table-cell !important; } } .visible-lg { display: none !important; } tr.visible-lg { display: none !important; } th.visible-lg, td.visible-lg { display: none !important; } @media (max-width: 767px) { .visible-lg.visible-xs { display: block !important; } tr.visible-lg.visible-xs { display: table-row !important; } th.visible-lg.visible-xs, td.visible-lg.visible-xs { display: table-cell !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-lg.visible-sm { display: block !important; } tr.visible-lg.visible-sm { display: table-row !important; } th.visible-lg.visible-sm, td.visible-lg.visible-sm { display: table-cell !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-lg.visible-md { display: block !important; } tr.visible-lg.visible-md { display: table-row !important; } th.visible-lg.visible-md, td.visible-lg.visible-md { display: table-cell !important; } } @media (min-width: 1200px) { .visible-lg { display: block !important; } tr.visible-lg { display: table-row !important; } th.visible-lg, td.visible-lg { display: table-cell !important; } } .hidden-xs { display: block !important; } tr.hidden-xs { display: table-row !important; } th.hidden-xs, td.hidden-xs { display: table-cell !important; } @media (max-width: 767px) { .hidden-xs { display: none !important; } tr.hidden-xs { display: none !important; } th.hidden-xs, td.hidden-xs { display: none !important; } } @media (min-width: 768px) and (max-width: 991px) { .hidden-xs.hidden-sm { display: none !important; } tr.hidden-xs.hidden-sm { display: none !important; } th.hidden-xs.hidden-sm, td.hidden-xs.hidden-sm { display: none !important; } } @media (min-width: 992px) and (max-width: 1199px) { .hidden-xs.hidden-md { display: none !important; } tr.hidden-xs.hidden-md { display: none !important; } th.hidden-xs.hidden-md, td.hidden-xs.hidden-md { display: none !important; } } @media (min-width: 1200px) { .hidden-xs.hidden-lg { display: none !important; } tr.hidden-xs.hidden-lg { display: none !important; } th.hidden-xs.hidden-lg, td.hidden-xs.hidden-lg { display: none !important; } } .hidden-sm { display: block !important; } tr.hidden-sm { display: table-row !important; } th.hidden-sm, td.hidden-sm { display: table-cell !important; } @media (max-width: 767px) { .hidden-sm.hidden-xs { display: none !important; } tr.hidden-sm.hidden-xs { display: none !important; } th.hidden-sm.hidden-xs, td.hidden-sm.hidden-xs { display: none !important; } } @media (min-width: 768px) and (max-width: 991px) { .hidden-sm { display: none !important; } tr.hidden-sm { display: none !important; } th.hidden-sm, td.hidden-sm { display: none !important; } } @media (min-width: 992px) and (max-width: 1199px) { .hidden-sm.hidden-md { display: none !important; } tr.hidden-sm.hidden-md { display: none !important; } th.hidden-sm.hidden-md, td.hidden-sm.hidden-md { display: none !important; } } @media (min-width: 1200px) { .hidden-sm.hidden-lg { display: none !important; } tr.hidden-sm.hidden-lg { display: none !important; } th.hidden-sm.hidden-lg, td.hidden-sm.hidden-lg { display: none !important; } } .hidden-md { display: block !important; } tr.hidden-md { display: table-row !important; } th.hidden-md, td.hidden-md { display: table-cell !important; } @media (max-width: 767px) { .hidden-md.hidden-xs { display: none !important; } tr.hidden-md.hidden-xs { display: none !important; } th.hidden-md.hidden-xs, td.hidden-md.hidden-xs { display: none !important; } } @media (min-width: 768px) and (max-width: 991px) { .hidden-md.hidden-sm { display: none !important; } tr.hidden-md.hidden-sm { display: none !important; } th.hidden-md.hidden-sm, td.hidden-md.hidden-sm { display: none !important; } } @media (min-width: 992px) and (max-width: 1199px) { .hidden-md { display: none !important; } tr.hidden-md { display: none !important; } th.hidden-md, td.hidden-md { display: none !important; } } @media (min-width: 1200px) { .hidden-md.hidden-lg { display: none !important; } tr.hidden-md.hidden-lg { display: none !important; } th.hidden-md.hidden-lg, td.hidden-md.hidden-lg { display: none !important; } } .hidden-lg { display: block !important; } tr.hidden-lg { display: table-row !important; } th.hidden-lg, td.hidden-lg { display: table-cell !important; } @media (max-width: 767px) { .hidden-lg.hidden-xs { display: none !important; } tr.hidden-lg.hidden-xs { display: none !important; } th.hidden-lg.hidden-xs, td.hidden-lg.hidden-xs { display: none !important; } } @media (min-width: 768px) and (max-width: 991px) { .hidden-lg.hidden-sm { display: none !important; } tr.hidden-lg.hidden-sm { display: none !important; } th.hidden-lg.hidden-sm, td.hidden-lg.hidden-sm { display: none !important; } } @media (min-width: 992px) and (max-width: 1199px) { .hidden-lg.hidden-md { display: none !important; } tr.hidden-lg.hidden-md { display: none !important; } th.hidden-lg.hidden-md, td.hidden-lg.hidden-md { display: none !important; } } @media (min-width: 1200px) { .hidden-lg { display: none !important; } tr.hidden-lg { display: none !important; } th.hidden-lg, td.hidden-lg { display: none !important; } } .visible-print { display: none !important; } tr.visible-print { display: none !important; } th.visible-print, td.visible-print { display: none !important; } @media print { .visible-print { display: block !important; } tr.visible-print { display: table-row !important; } th.visible-print, td.visible-print { display: table-cell !important; } .hidden-print { display: none !important; } tr.hidden-print { display: none !important; } th.hidden-print, td.hidden-print { display: none !important; } } ================================================ FILE: AlexaSkillsKit.Sample/Controllers/AccountController.cs ================================================ using System; using System.Collections.Generic; using System.Net.Http; using System.Security.Claims; using System.Security.Cryptography; using System.Threading.Tasks; using System.Web; using System.Web.Http; using System.Web.Http.ModelBinding; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin.Security; using Microsoft.Owin.Security.Cookies; using Microsoft.Owin.Security.OAuth; using Sample.Models; using Sample.Providers; using Sample.Results; namespace Sample.Controllers { [Authorize] [RoutePrefix("api/Account")] public class AccountController : ApiController { private const string LocalLoginProvider = "Local"; private ApplicationUserManager _userManager; public AccountController() { } public AccountController(ApplicationUserManager userManager, ISecureDataFormat accessTokenFormat) { UserManager = userManager; AccessTokenFormat = accessTokenFormat; } public ApplicationUserManager UserManager { get { return _userManager ?? Request.GetOwinContext().GetUserManager(); } private set { _userManager = value; } } public ISecureDataFormat AccessTokenFormat { get; private set; } // GET api/Account/UserInfo [HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)] [Route("UserInfo")] public UserInfoViewModel GetUserInfo() { ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity); return new UserInfoViewModel { Email = User.Identity.GetUserName(), HasRegistered = externalLogin == null, LoginProvider = externalLogin != null ? externalLogin.LoginProvider : null }; } // POST api/Account/Logout [Route("Logout")] public IHttpActionResult Logout() { Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType); return Ok(); } // GET api/Account/ManageInfo?returnUrl=%2F&generateState=true [Route("ManageInfo")] public async Task GetManageInfo(string returnUrl, bool generateState = false) { IdentityUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); if (user == null) { return null; } List logins = new List(); foreach (IdentityUserLogin linkedAccount in user.Logins) { logins.Add(new UserLoginInfoViewModel { LoginProvider = linkedAccount.LoginProvider, ProviderKey = linkedAccount.ProviderKey }); } if (user.PasswordHash != null) { logins.Add(new UserLoginInfoViewModel { LoginProvider = LocalLoginProvider, ProviderKey = user.UserName, }); } return new ManageInfoViewModel { LocalLoginProvider = LocalLoginProvider, Email = user.UserName, Logins = logins, ExternalLoginProviders = GetExternalLogins(returnUrl, generateState) }; } // POST api/Account/ChangePassword [Route("ChangePassword")] public async Task ChangePassword(ChangePasswordBindingModel model) { if (!ModelState.IsValid) { return BadRequest(ModelState); } IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword); if (!result.Succeeded) { return GetErrorResult(result); } return Ok(); } // POST api/Account/SetPassword [Route("SetPassword")] public async Task SetPassword(SetPasswordBindingModel model) { if (!ModelState.IsValid) { return BadRequest(ModelState); } IdentityResult result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword); if (!result.Succeeded) { return GetErrorResult(result); } return Ok(); } // POST api/Account/AddExternalLogin [Route("AddExternalLogin")] public async Task AddExternalLogin(AddExternalLoginBindingModel model) { if (!ModelState.IsValid) { return BadRequest(ModelState); } Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie); AuthenticationTicket ticket = AccessTokenFormat.Unprotect(model.ExternalAccessToken); if (ticket == null || ticket.Identity == null || (ticket.Properties != null && ticket.Properties.ExpiresUtc.HasValue && ticket.Properties.ExpiresUtc.Value < DateTimeOffset.UtcNow)) { return BadRequest("External login failure."); } ExternalLoginData externalData = ExternalLoginData.FromIdentity(ticket.Identity); if (externalData == null) { return BadRequest("The external login is already associated with an account."); } IdentityResult result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), new UserLoginInfo(externalData.LoginProvider, externalData.ProviderKey)); if (!result.Succeeded) { return GetErrorResult(result); } return Ok(); } // POST api/Account/RemoveLogin [Route("RemoveLogin")] public async Task RemoveLogin(RemoveLoginBindingModel model) { if (!ModelState.IsValid) { return BadRequest(ModelState); } IdentityResult result; if (model.LoginProvider == LocalLoginProvider) { result = await UserManager.RemovePasswordAsync(User.Identity.GetUserId()); } else { result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(), new UserLoginInfo(model.LoginProvider, model.ProviderKey)); } if (!result.Succeeded) { return GetErrorResult(result); } return Ok(); } // GET api/Account/ExternalLogin [OverrideAuthentication] [HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)] [AllowAnonymous] [Route("ExternalLogin", Name = "ExternalLogin")] public async Task GetExternalLogin(string provider, string error = null) { if (error != null) { return Redirect(Url.Content("~/") + "#error=" + Uri.EscapeDataString(error)); } if (!User.Identity.IsAuthenticated) { return new ChallengeResult(provider, this); } ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity); if (externalLogin == null) { return InternalServerError(); } if (externalLogin.LoginProvider != provider) { Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie); return new ChallengeResult(provider, this); } ApplicationUser user = await UserManager.FindAsync(new UserLoginInfo(externalLogin.LoginProvider, externalLogin.ProviderKey)); bool hasRegistered = user != null; if (hasRegistered) { Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie); ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager, OAuthDefaults.AuthenticationType); ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager, CookieAuthenticationDefaults.AuthenticationType); AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName); Authentication.SignIn(properties, oAuthIdentity, cookieIdentity); } else { IEnumerable claims = externalLogin.GetClaims(); ClaimsIdentity identity = new ClaimsIdentity(claims, OAuthDefaults.AuthenticationType); Authentication.SignIn(identity); } return Ok(); } // GET api/Account/ExternalLogins?returnUrl=%2F&generateState=true [AllowAnonymous] [Route("ExternalLogins")] public IEnumerable GetExternalLogins(string returnUrl, bool generateState = false) { IEnumerable descriptions = Authentication.GetExternalAuthenticationTypes(); List logins = new List(); string state; if (generateState) { const int strengthInBits = 256; state = RandomOAuthStateGenerator.Generate(strengthInBits); } else { state = null; } foreach (AuthenticationDescription description in descriptions) { ExternalLoginViewModel login = new ExternalLoginViewModel { Name = description.Caption, Url = Url.Route("ExternalLogin", new { provider = description.AuthenticationType, response_type = "token", client_id = Startup.PublicClientId, redirect_uri = new Uri(Request.RequestUri, returnUrl).AbsoluteUri, state = state }), State = state }; logins.Add(login); } return logins; } // POST api/Account/Register [AllowAnonymous] [Route("Register")] public async Task Register(RegisterBindingModel model) { if (!ModelState.IsValid) { return BadRequest(ModelState); } var user = new ApplicationUser() { UserName = model.Email, Email = model.Email }; IdentityResult result = await UserManager.CreateAsync(user, model.Password); if (!result.Succeeded) { return GetErrorResult(result); } return Ok(); } // POST api/Account/RegisterExternal [OverrideAuthentication] [HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)] [Route("RegisterExternal")] public async Task RegisterExternal(RegisterExternalBindingModel model) { if (!ModelState.IsValid) { return BadRequest(ModelState); } var info = await Authentication.GetExternalLoginInfoAsync(); if (info == null) { return InternalServerError(); } var user = new ApplicationUser() { UserName = model.Email, Email = model.Email }; IdentityResult result = await UserManager.CreateAsync(user); if (!result.Succeeded) { return GetErrorResult(result); } result = await UserManager.AddLoginAsync(user.Id, info.Login); if (!result.Succeeded) { return GetErrorResult(result); } return Ok(); } protected override void Dispose(bool disposing) { if (disposing && _userManager != null) { _userManager.Dispose(); _userManager = null; } base.Dispose(disposing); } #region Helpers private IAuthenticationManager Authentication { get { return Request.GetOwinContext().Authentication; } } private IHttpActionResult GetErrorResult(IdentityResult result) { if (result == null) { return InternalServerError(); } if (!result.Succeeded) { if (result.Errors != null) { foreach (string error in result.Errors) { ModelState.AddModelError("", error); } } if (ModelState.IsValid) { // No ModelState errors are available to send, so just return an empty BadRequest. return BadRequest(); } return BadRequest(ModelState); } return null; } private class ExternalLoginData { public string LoginProvider { get; set; } public string ProviderKey { get; set; } public string UserName { get; set; } public IList GetClaims() { IList claims = new List(); claims.Add(new Claim(ClaimTypes.NameIdentifier, ProviderKey, null, LoginProvider)); if (UserName != null) { claims.Add(new Claim(ClaimTypes.Name, UserName, null, LoginProvider)); } return claims; } public static ExternalLoginData FromIdentity(ClaimsIdentity identity) { if (identity == null) { return null; } Claim providerKeyClaim = identity.FindFirst(ClaimTypes.NameIdentifier); if (providerKeyClaim == null || String.IsNullOrEmpty(providerKeyClaim.Issuer) || String.IsNullOrEmpty(providerKeyClaim.Value)) { return null; } if (providerKeyClaim.Issuer == ClaimsIdentity.DefaultIssuer) { return null; } return new ExternalLoginData { LoginProvider = providerKeyClaim.Issuer, ProviderKey = providerKeyClaim.Value, UserName = identity.FindFirstValue(ClaimTypes.Name) }; } } private static class RandomOAuthStateGenerator { private static RandomNumberGenerator _random = new RNGCryptoServiceProvider(); public static string Generate(int strengthInBits) { const int bitsPerByte = 8; if (strengthInBits % bitsPerByte != 0) { throw new ArgumentException("strengthInBits must be evenly divisible by 8.", "strengthInBits"); } int strengthInBytes = strengthInBits / bitsPerByte; byte[] data = new byte[strengthInBytes]; _random.GetBytes(data); return HttpServerUtility.UrlTokenEncode(data); } } #endregion } } ================================================ FILE: AlexaSkillsKit.Sample/Controllers/HomeController.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace Sample.Controllers { public class HomeController : Controller { public ActionResult Index() { ViewBag.Title = "Home Page"; return View(); } } } ================================================ FILE: AlexaSkillsKit.Sample/Controllers/ValuesController.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; namespace Sample.Controllers { [Authorize] public class ValuesController : ApiController { // GET api/values public IEnumerable Get() { return new string[] { "value1", "value2" }; } // GET api/values/5 public string Get(int id) { return "value"; } // POST api/values public void Post([FromBody]string value) { } // PUT api/values/5 public void Put(int id, [FromBody]string value) { } // DELETE api/values/5 public void Delete(int id) { } } } ================================================ FILE: AlexaSkillsKit.Sample/Global.asax ================================================ <%@ Application Codebehind="Global.asax.cs" Inherits="Sample.WebApiApplication" Language="C#" %> ================================================ FILE: AlexaSkillsKit.Sample/Global.asax.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; namespace Sample { public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); GlobalConfiguration.Configure(WebApiConfig.Register); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); } } } ================================================ FILE: AlexaSkillsKit.Sample/Models/AccountBindingModels.cs ================================================ using System; using System.ComponentModel.DataAnnotations; using Newtonsoft.Json; namespace Sample.Models { // Models used as parameters to AccountController actions. public class AddExternalLoginBindingModel { [Required] [Display(Name = "External access token")] public string ExternalAccessToken { get; set; } } public class ChangePasswordBindingModel { [Required] [DataType(DataType.Password)] [Display(Name = "Current password")] public string OldPassword { get; set; } [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "New password")] public string NewPassword { get; set; } [DataType(DataType.Password)] [Display(Name = "Confirm new password")] [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] public string ConfirmPassword { get; set; } } public class RegisterBindingModel { [Required] [Display(Name = "Email")] public string Email { get; set; } [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "Password")] public string Password { get; set; } [DataType(DataType.Password)] [Display(Name = "Confirm password")] [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] public string ConfirmPassword { get; set; } } public class RegisterExternalBindingModel { [Required] [Display(Name = "Email")] public string Email { get; set; } } public class RemoveLoginBindingModel { [Required] [Display(Name = "Login provider")] public string LoginProvider { get; set; } [Required] [Display(Name = "Provider key")] public string ProviderKey { get; set; } } public class SetPasswordBindingModel { [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "New password")] public string NewPassword { get; set; } [DataType(DataType.Password)] [Display(Name = "Confirm new password")] [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] public string ConfirmPassword { get; set; } } } ================================================ FILE: AlexaSkillsKit.Sample/Models/AccountViewModels.cs ================================================ using System; using System.Collections.Generic; namespace Sample.Models { // Models returned by AccountController actions. public class ExternalLoginViewModel { public string Name { get; set; } public string Url { get; set; } public string State { get; set; } } public class ManageInfoViewModel { public string LocalLoginProvider { get; set; } public string Email { get; set; } public IEnumerable Logins { get; set; } public IEnumerable ExternalLoginProviders { get; set; } } public class UserInfoViewModel { public string Email { get; set; } public bool HasRegistered { get; set; } public string LoginProvider { get; set; } } public class UserLoginInfoViewModel { public string LoginProvider { get; set; } public string ProviderKey { get; set; } } } ================================================ FILE: AlexaSkillsKit.Sample/Models/IdentityModels.cs ================================================ using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.AspNet.Identity.Owin; namespace Sample.Models { // You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more. public class ApplicationUser : IdentityUser { public async Task GenerateUserIdentityAsync(UserManager manager, string authenticationType) { // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType var userIdentity = await manager.CreateIdentityAsync(this, authenticationType); // Add custom user claims here return userIdentity; } } public class ApplicationDbContext : IdentityDbContext { public ApplicationDbContext() : base("DefaultConnection", throwIfV1Schema: false) { } public static ApplicationDbContext Create() { return new ApplicationDbContext(); } } } ================================================ FILE: AlexaSkillsKit.Sample/NLog.config ================================================ ================================================ FILE: AlexaSkillsKit.Sample/NLog.xsd ================================================  Watch config file for changes and reload automatically. Print internal NLog messages to the console. Default value is: false Print internal NLog messages to the console error output. Default value is: false Write internal NLog messages to the specified file. Log level threshold for internal log messages. Default value is: Info. Global log level threshold for application log messages. Messages below this level won't be logged.. Pass NLog internal exceptions to the application. Default value is: false. Make all targets within this section asynchronous (creates additional threads but the calling thread isn't blocked by any target writes). Prefix for targets/layout renderers/filters/conditions loaded from this assembly. Load NLog extensions from the specified file (*.dll) Load NLog extensions from the specified assembly. Assembly name should be fully qualified. Name of the logger. May include '*' character which acts like a wildcard. Allowed forms are: *, Name, *Name, Name* and *Name* Comma separated list of levels that this rule matches. Minimum level that this rule matches. Maximum level that this rule matches. Level that this rule matches. Comma separated list of target names. Ignore further rules if this one matches. Name of the file to be included. The name is relative to the name of the current config file. Ignore any errors in the include file. Variable name. Variable value. Name of the target. Maximum number of log events that the buffer can keep. Number of log events to be buffered. Indicates whether buffer should grow as needed. Name of the target. Layout used to format log messages. Name of the target. Layout used to format log messages. Indicates whether to add <!-- --> comments around all written texts. Name of the target. Number of log events that should be processed in a batch by the lazy writer thread. Action to be taken when the lazy writer thread request queue count exceeds the set limit. Limit on the number of requests in the lazy writer thread request queue. Time in milliseconds to sleep between batches. Name of the target. Name of the target. Number of log events to be buffered. Timeout (in milliseconds) after which the contents of buffer will be flushed if there's no write in the specified period of time. Use -1 to disable timed flushes. Indicates whether to use sliding timeout. Name of the target. Encoding to be used. Instance of that is used to format log messages. Maximum message size in bytes. Indicates whether to append newline at the end of log message. Action that should be taken if the message is larger than maxMessageSize. Size of the connection cache (number of connections which are kept alive). Network address. Indicates whether to keep connection open whenever possible. Maximum queue size. Indicates whether to include NLog-specific extensions to log4j schema. Indicates whether to include source info (file name and line number) in the information sent over the network. NDC item separator. Indicates whether to include call site (class and method name) in the information sent over the network. AppInfo field. By default it's the friendly name of the current AppDomain. Indicates whether to include stack contents. Indicates whether to include dictionary contents. Layout that should be use to calcuate the value for the parameter. Viewer parameter name. Name of the target. Text to be rendered. Header. Footer. Indicates whether to use default row highlighting rules. Indicates whether the error stream (stderr) should be used instead of the output stream (stdout). Condition that must be met in order to set the specified foreground and background color. Background color. Foreground color. Indicates whether to ignore case when comparing texts. Regular expression to be matched. You must specify either text or regex. Text to be matched. You must specify either text or regex. Indicates whether to match whole words only. Background color. Foreground color. Name of the target. Text to be rendered. Header. Footer. Indicates whether to send the log messages to the standard error instead of the standard output. Name of the target. Connection string. When provided, it overrides the values specified in DBHost, DBUserName, DBPassword, DBDatabase. Name of the connection string (as specified in <connectionStrings> configuration section. Database name. If the ConnectionString is not provided this value will be used to construct the "Database=" part of the connection string. Database host name. If the ConnectionString is not provided this value will be used to construct the "Server=" part of the connection string. Database password. If the ConnectionString is not provided this value will be used to construct the "Password=" part of the connection string. Name of the database provider. Database user name. If the ConnectionString is not provided this value will be used to construct the "User ID=" part of the connection string. Indicates whether to keep the database connection open between the log events. Indicates whether to use database transactions. Some data providers require this. Connection string using for installation and uninstallation. If not provided, regular ConnectionString is being used. Text of the SQL command to be run on each log level. Type of the SQL command to be run on each log level. Type of the command. Connection string to run the command against. If not provided, connection string from the target is used. Indicates whether to ignore failures. Command text. Layout that should be use to calcuate the value for the parameter. Database parameter name. Database parameter precision. Database parameter scale. Database parameter size. Name of the target. Text to be rendered. Header. Footer. Name of the target. Layout used to format log messages. Name of the target. Layout used to format log messages. Layout that renders event Category. Layout that renders event ID. Name of the Event Log to write to. This can be System, Application or any user-defined name. Name of the machine on which Event Log service is running. Value to be used as the event Source. Name of the target. Indicates whether to return to the first target after any successful write. Name of the target. Text to be rendered. Header. Footer. File encoding. Line ending mode. Way file archives are numbered. Name of the file to be used for an archive. Indicates whether to automatically archive log files every time the specified time passes. Size in bytes above which log files will be automatically archived. Maximum number of archive files that should be kept. Gets ors set a value indicating whether a managed file stream is forced, instead of used the native implementation. File attributes (Windows only). Indicates whether to replace file contents on each write instead of appending log message at the end. Indicates whether to delete old log file on startup. Name of the file to write to. Value specifying the date format to use when archving files. Indicates whether to archive old log file on startup. Indicates whether to create directories if they don't exist. Indicates whether to enable log file(s) to be deleted. Maximum number of seconds that files are kept open. If this number is negative the files are not automatically closed after a period of inactivity. Indicates whether concurrent writes to the log file by multiple processes on different network hosts. Indicates whether to keep log file open instead of opening and closing it on each logging event. Indicates whether concurrent writes to the log file by multiple processes on the same host. Number of times the write is appended on the file before NLog discards the log message. Delay in milliseconds to wait before attempting to write to the file again. Log file buffer size in bytes. Number of files to be kept open. Setting this to a higher value may improve performance in a situation where a single File target is writing to many files (such as splitting by level or by logger). Indicates whether to automatically flush the file buffers after each log message. Name of the target. Condition expression. Log events who meet this condition will be forwarded to the wrapped target. Name of the target. Layout used to format log messages. Indicates whether log text should be appended to the text of the control instead of overwriting it. Name of control to which NLog will log write log text. Name of the Form on which the control is located. Whether new log entry are added to the start or the end of the control Name of the target. Windows domain name to change context to. Required impersonation level. Type of the logon provider. Logon Type. User account password. Indicates whether to revert to the credentials of the process instead of impersonating another user. Username to change context to. Name of the target. Endpoint address. Name of the endpoint configuration in WCF configuration file. Client ID. Indicates whether to include per-event properties in the payload sent to the server. Indicates whether to use binary message encoding. Layout that should be use to calcuate the value for the parameter. Name of the parameter. Type of the parameter. Name of the target. Text to be rendered. Header. Footer. Indicates whether to send message as HTML instead of plain text. Encoding to be used for sending e-mail. Indicates whether to add new lines between log entries. BCC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). Recipients' email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). CC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). Mail message body (repeated for each log message send in one mail). Mail subject. Sender's email address (e.g. joe@domain.com). Indicates whether NewLine characters in the body should be replaced with tags. Priority used for sending mails. Indicates the SMTP client timeout. SMTP Server to be used for sending. SMTP Authentication mode. Username used to connect to SMTP server (used when SmtpAuthentication is set to "basic"). Password used to authenticate against SMTP server (used when SmtpAuthentication is set to "basic"). Indicates whether SSL (secure sockets layer) should be used when communicating with SMTP server. Port number that SMTP Server is listening on. Indicates whether the default Settings from System.Net.MailSettings should be used. Name of the target. Layout used to format log messages. Name of the target. Layout used to format log messages. Message box title. Name of the target. Layout used to format log messages. Encoding to be used when writing text to the queue. Indicates whether to use the XML format when serializing message. This will also disable creating queues. Indicates whether to check if a queue exists before writing to it. Indicates whether to create the queue if it doesn't exists. Label to associate with each message. Name of the queue to write to. Indicates whether to use recoverable messages (with guaranteed delivery). Name of the target. Class name. Method name. The method must be public and static. Name of the target. Layout used to format log messages. Encoding to be used. Maximum message size in bytes. Indicates whether to append newline at the end of log message. Action that should be taken if the message is larger than maxMessageSize. Network address. Size of the connection cache (number of connections which are kept alive). Indicates whether to keep connection open whenever possible. Maximum queue size. Name of the target. Encoding to be used. Instance of that is used to format log messages. Maximum message size in bytes. Indicates whether to append newline at the end of log message. Action that should be taken if the message is larger than maxMessageSize. Size of the connection cache (number of connections which are kept alive). Network address. Indicates whether to keep connection open whenever possible. Maximum queue size. Indicates whether to include NLog-specific extensions to log4j schema. Indicates whether to include source info (file name and line number) in the information sent over the network. NDC item separator. Indicates whether to include call site (class and method name) in the information sent over the network. AppInfo field. By default it's the friendly name of the current AppDomain. Indicates whether to include stack contents. Indicates whether to include dictionary contents. Name of the target. Layout used to format log messages. Indicates whether to perform layout calculation. Name of the target. Layout used to format log messages. Name of the target. Indicates whether performance counter should be automatically created. Name of the performance counter category. Counter help text. Name of the performance counter. Performance counter type. Performance counter instance name. Name of the target. Default filter to be applied when no specific rule matches. Condition to be tested. Resulting filter to be applied when the condition matches. Name of the target. Name of the target. Number of times to repeat each log message. Name of the target. Number of retries that should be attempted on the wrapped target in case of a failure. Time to wait between retries in milliseconds. Name of the target. Layout used to format log messages. Indicates whether scroll bar will be moved automatically to show most recent log entries. Name of RichTextBox to which Nlog will write. Name of the Form on which the control is located. If there is no open form of a specified name than NLog will create a new one. Initial height of the form with rich text box. Maximum number of lines the rich text box will store (or 0 to disable this feature). Indicates whether the created form will be initially minimized. Indicates whether the created window will be a tool window. Initial width of the form with rich text box. Indicates whether to use default coloring rules. Condition that must be met in order to set the specified font color. Background color. Font color. Font style of matched text. Indicates whether to ignore case when comparing texts. Regular expression to be matched. You must specify either text or regex. Text to be matched. You must specify either text or regex. Indicates whether to match whole words only. Background color. Names are identical with KnownColor enum extended with Empty value which means that background color won't be changed. Font color. Names are identical with KnownColor enum extended with Empty value which means that font color won't be changed. Font style of matched text. Possible values are the same as in FontStyle enum in System.Drawing. Name of the target. Name of the target. Name of the target. Layout used to format log messages. Name of the target. Encoding. Web service method name. Web service namespace. Protocol to be used when calling web service. Web service URL. Footer layout. Header layout. Body layout (can be repeated multiple times). Custom column delimiter value (valid when ColumnDelimiter is set to 'Custom'). Column delimiter. Quote Character. Quoting mode. Indicates whether CVS should include header. Layout of the column. Name of the column. Footer layout. Header layout. Body layout (can be repeated multiple times). Layout text. Action to be taken when filter matches. Condition expression. Action to be taken when filter matches. Indicates whether to ignore case when comparing strings. Layout to be used to filter log messages. Substring to be matched. Action to be taken when filter matches. String to compare the layout to. Indicates whether to ignore case when comparing strings. Layout to be used to filter log messages. Action to be taken when filter matches. Indicates whether to ignore case when comparing strings. Layout to be used to filter log messages. Substring to be matched. Action to be taken when filter matches. String to compare the layout to. Indicates whether to ignore case when comparing strings. Layout to be used to filter log messages. ================================================ FILE: AlexaSkillsKit.Sample/Project_Readme.html ================================================  Your ASP.NET application ================================================ FILE: AlexaSkillsKit.Sample/Properties/AssemblyInfo.cs ================================================ 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("AlexaSkillsKit.App")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("AlexaSkillsKit.App")] [assembly: AssemblyCopyright("Copyright © 2015 Stefan Negritoiu (FreeBusy)")] [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("aceb63c9-7b4c-466c-8dc5-de054f5c9bdf")] // 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")] ================================================ FILE: AlexaSkillsKit.Sample/Providers/ApplicationOAuthProvider.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin.Security; using Microsoft.Owin.Security.Cookies; using Microsoft.Owin.Security.OAuth; using Sample.Models; namespace Sample.Providers { public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider { private readonly string _publicClientId; public ApplicationOAuthProvider(string publicClientId) { if (publicClientId == null) { throw new ArgumentNullException("publicClientId"); } _publicClientId = publicClientId; } public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { var userManager = context.OwinContext.GetUserManager(); ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password); if (user == null) { context.SetError("invalid_grant", "The user name or password is incorrect."); return; } ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, OAuthDefaults.AuthenticationType); ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager, CookieAuthenticationDefaults.AuthenticationType); AuthenticationProperties properties = CreateProperties(user.UserName); AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties); context.Validated(ticket); context.Request.Context.Authentication.SignIn(cookiesIdentity); } public override Task TokenEndpoint(OAuthTokenEndpointContext context) { foreach (KeyValuePair property in context.Properties.Dictionary) { context.AdditionalResponseParameters.Add(property.Key, property.Value); } return Task.FromResult(null); } public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { // Resource owner password credentials does not provide a client ID. if (context.ClientId == null) { context.Validated(); } return Task.FromResult(null); } public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context) { if (context.ClientId == _publicClientId) { Uri expectedRootUri = new Uri(context.Request.Uri, "/"); if (expectedRootUri.AbsoluteUri == context.RedirectUri) { context.Validated(); } } return Task.FromResult(null); } public static AuthenticationProperties CreateProperties(string userName) { IDictionary data = new Dictionary { { "userName", userName } }; return new AuthenticationProperties(data); } } } ================================================ FILE: AlexaSkillsKit.Sample/Results/ChallengeResult.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Web.Http; namespace Sample.Results { public class ChallengeResult : IHttpActionResult { public ChallengeResult(string loginProvider, ApiController controller) { LoginProvider = loginProvider; Request = controller.Request; } public string LoginProvider { get; set; } public HttpRequestMessage Request { get; set; } public Task ExecuteAsync(CancellationToken cancellationToken) { Request.GetOwinContext().Authentication.Challenge(LoginProvider); HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized); response.RequestMessage = Request; return Task.FromResult(response); } } } ================================================ FILE: AlexaSkillsKit.Sample/Scripts/bootstrap.js ================================================ /* NUGET: BEGIN LICENSE TEXT * * Microsoft grants you the right to use these script files for the sole * purpose of either: (i) interacting through your browser with the Microsoft * website or online service, subject to the applicable licensing or use * terms; or (ii) using the files as included with a Microsoft product subject * to that product's license terms. Microsoft reserves all other rights to the * files not expressly granted by Microsoft, whether by implication, estoppel * or otherwise. Insofar as a script file is dual licensed under GPL, * Microsoft neither took the code under GPL nor distributes it thereunder but * under the terms set out in this paragraph. All notices and licenses * below are for informational purposes only. * * NUGET: END LICENSE TEXT */ /** * bootstrap.js v3.0.0 by @fat and @mdo * Copyright 2013 Twitter Inc. * http://www.apache.org/licenses/LICENSE-2.0 */ if (!jQuery) { throw new Error("Bootstrap requires jQuery") } /* ======================================================================== * Bootstrap: transition.js v3.0.0 * http://twbs.github.com/bootstrap/javascript.html#transitions * ======================================================================== * Copyright 2013 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ======================================================================== */ +function ($) { "use strict"; // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) // ============================================================ function transitionEnd() { var el = document.createElement('bootstrap') var transEndEventNames = { 'WebkitTransition' : 'webkitTransitionEnd' , 'MozTransition' : 'transitionend' , 'OTransition' : 'oTransitionEnd otransitionend' , 'transition' : 'transitionend' } for (var name in transEndEventNames) { if (el.style[name] !== undefined) { return { end: transEndEventNames[name] } } } } // http://blog.alexmaccaw.com/css-transitions $.fn.emulateTransitionEnd = function (duration) { var called = false, $el = this $(this).one($.support.transition.end, function () { called = true }) var callback = function () { if (!called) $($el).trigger($.support.transition.end) } setTimeout(callback, duration) return this } $(function () { $.support.transition = transitionEnd() }) }(window.jQuery); /* ======================================================================== * Bootstrap: alert.js v3.0.0 * http://twbs.github.com/bootstrap/javascript.html#alerts * ======================================================================== * Copyright 2013 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ======================================================================== */ +function ($) { "use strict"; // ALERT CLASS DEFINITION // ====================== var dismiss = '[data-dismiss="alert"]' var Alert = function (el) { $(el).on('click', dismiss, this.close) } Alert.prototype.close = function (e) { var $this = $(this) var selector = $this.attr('data-target') if (!selector) { selector = $this.attr('href') selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 } var $parent = $(selector) if (e) e.preventDefault() if (!$parent.length) { $parent = $this.hasClass('alert') ? $this : $this.parent() } $parent.trigger(e = $.Event('close.bs.alert')) if (e.isDefaultPrevented()) return $parent.removeClass('in') function removeElement() { $parent.trigger('closed.bs.alert').remove() } $.support.transition && $parent.hasClass('fade') ? $parent .one($.support.transition.end, removeElement) .emulateTransitionEnd(150) : removeElement() } // ALERT PLUGIN DEFINITION // ======================= var old = $.fn.alert $.fn.alert = function (option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.alert') if (!data) $this.data('bs.alert', (data = new Alert(this))) if (typeof option == 'string') data[option].call($this) }) } $.fn.alert.Constructor = Alert // ALERT NO CONFLICT // ================= $.fn.alert.noConflict = function () { $.fn.alert = old return this } // ALERT DATA-API // ============== $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) }(window.jQuery); /* ======================================================================== * Bootstrap: button.js v3.0.0 * http://twbs.github.com/bootstrap/javascript.html#buttons * ======================================================================== * Copyright 2013 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ======================================================================== */ +function ($) { "use strict"; // BUTTON PUBLIC CLASS DEFINITION // ============================== var Button = function (element, options) { this.$element = $(element) this.options = $.extend({}, Button.DEFAULTS, options) } Button.DEFAULTS = { loadingText: 'loading...' } Button.prototype.setState = function (state) { var d = 'disabled' var $el = this.$element var val = $el.is('input') ? 'val' : 'html' var data = $el.data() state = state + 'Text' if (!data.resetText) $el.data('resetText', $el[val]()) $el[val](data[state] || this.options[state]) // push to event loop to allow forms to submit setTimeout(function () { state == 'loadingText' ? $el.addClass(d).attr(d, d) : $el.removeClass(d).removeAttr(d); }, 0) } Button.prototype.toggle = function () { var $parent = this.$element.closest('[data-toggle="buttons"]') if ($parent.length) { var $input = this.$element.find('input') .prop('checked', !this.$element.hasClass('active')) .trigger('change') if ($input.prop('type') === 'radio') $parent.find('.active').removeClass('active') } this.$element.toggleClass('active') } // BUTTON PLUGIN DEFINITION // ======================== var old = $.fn.button $.fn.button = function (option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.button') var options = typeof option == 'object' && option if (!data) $this.data('bs.button', (data = new Button(this, options))) if (option == 'toggle') data.toggle() else if (option) data.setState(option) }) } $.fn.button.Constructor = Button // BUTTON NO CONFLICT // ================== $.fn.button.noConflict = function () { $.fn.button = old return this } // BUTTON DATA-API // =============== $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) { var $btn = $(e.target) if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') $btn.button('toggle') e.preventDefault() }) }(window.jQuery); /* ======================================================================== * Bootstrap: carousel.js v3.0.0 * http://twbs.github.com/bootstrap/javascript.html#carousel * ======================================================================== * Copyright 2012 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ======================================================================== */ +function ($) { "use strict"; // CAROUSEL CLASS DEFINITION // ========================= var Carousel = function (element, options) { this.$element = $(element) this.$indicators = this.$element.find('.carousel-indicators') this.options = options this.paused = this.sliding = this.interval = this.$active = this.$items = null this.options.pause == 'hover' && this.$element .on('mouseenter', $.proxy(this.pause, this)) .on('mouseleave', $.proxy(this.cycle, this)) } Carousel.DEFAULTS = { interval: 5000 , pause: 'hover' , wrap: true } Carousel.prototype.cycle = function (e) { e || (this.paused = false) this.interval && clearInterval(this.interval) this.options.interval && !this.paused && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) return this } Carousel.prototype.getActiveIndex = function () { this.$active = this.$element.find('.item.active') this.$items = this.$active.parent().children() return this.$items.index(this.$active) } Carousel.prototype.to = function (pos) { var that = this var activeIndex = this.getActiveIndex() if (pos > (this.$items.length - 1) || pos < 0) return if (this.sliding) return this.$element.one('slid', function () { that.to(pos) }) if (activeIndex == pos) return this.pause().cycle() return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) } Carousel.prototype.pause = function (e) { e || (this.paused = true) if (this.$element.find('.next, .prev').length && $.support.transition.end) { this.$element.trigger($.support.transition.end) this.cycle(true) } this.interval = clearInterval(this.interval) return this } Carousel.prototype.next = function () { if (this.sliding) return return this.slide('next') } Carousel.prototype.prev = function () { if (this.sliding) return return this.slide('prev') } Carousel.prototype.slide = function (type, next) { var $active = this.$element.find('.item.active') var $next = next || $active[type]() var isCycling = this.interval var direction = type == 'next' ? 'left' : 'right' var fallback = type == 'next' ? 'first' : 'last' var that = this if (!$next.length) { if (!this.options.wrap) return $next = this.$element.find('.item')[fallback]() } this.sliding = true isCycling && this.pause() var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction }) if ($next.hasClass('active')) return if (this.$indicators.length) { this.$indicators.find('.active').removeClass('active') this.$element.one('slid', function () { var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()]) $nextIndicator && $nextIndicator.addClass('active') }) } if ($.support.transition && this.$element.hasClass('slide')) { this.$element.trigger(e) if (e.isDefaultPrevented()) return $next.addClass(type) $next[0].offsetWidth // force reflow $active.addClass(direction) $next.addClass(direction) $active .one($.support.transition.end, function () { $next.removeClass([type, direction].join(' ')).addClass('active') $active.removeClass(['active', direction].join(' ')) that.sliding = false setTimeout(function () { that.$element.trigger('slid') }, 0) }) .emulateTransitionEnd(600) } else { this.$element.trigger(e) if (e.isDefaultPrevented()) return $active.removeClass('active') $next.addClass('active') this.sliding = false this.$element.trigger('slid') } isCycling && this.cycle() return this } // CAROUSEL PLUGIN DEFINITION // ========================== var old = $.fn.carousel $.fn.carousel = function (option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.carousel') var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) var action = typeof option == 'string' ? option : options.slide if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) if (typeof option == 'number') data.to(option) else if (action) data[action]() else if (options.interval) data.pause().cycle() }) } $.fn.carousel.Constructor = Carousel // CAROUSEL NO CONFLICT // ==================== $.fn.carousel.noConflict = function () { $.fn.carousel = old return this } // CAROUSEL DATA-API // ================= $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { var $this = $(this), href var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 var options = $.extend({}, $target.data(), $this.data()) var slideIndex = $this.attr('data-slide-to') if (slideIndex) options.interval = false $target.carousel(options) if (slideIndex = $this.attr('data-slide-to')) { $target.data('bs.carousel').to(slideIndex) } e.preventDefault() }) $(window).on('load', function () { $('[data-ride="carousel"]').each(function () { var $carousel = $(this) $carousel.carousel($carousel.data()) }) }) }(window.jQuery); /* ======================================================================== * Bootstrap: collapse.js v3.0.0 * http://twbs.github.com/bootstrap/javascript.html#collapse * ======================================================================== * Copyright 2012 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ======================================================================== */ +function ($) { "use strict"; // COLLAPSE PUBLIC CLASS DEFINITION // ================================ var Collapse = function (element, options) { this.$element = $(element) this.options = $.extend({}, Collapse.DEFAULTS, options) this.transitioning = null if (this.options.parent) this.$parent = $(this.options.parent) if (this.options.toggle) this.toggle() } Collapse.DEFAULTS = { toggle: true } Collapse.prototype.dimension = function () { var hasWidth = this.$element.hasClass('width') return hasWidth ? 'width' : 'height' } Collapse.prototype.show = function () { if (this.transitioning || this.$element.hasClass('in')) return var startEvent = $.Event('show.bs.collapse') this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) return var actives = this.$parent && this.$parent.find('> .panel > .in') if (actives && actives.length) { var hasData = actives.data('bs.collapse') if (hasData && hasData.transitioning) return actives.collapse('hide') hasData || actives.data('bs.collapse', null) } var dimension = this.dimension() this.$element .removeClass('collapse') .addClass('collapsing') [dimension](0) this.transitioning = 1 var complete = function () { this.$element .removeClass('collapsing') .addClass('in') [dimension]('auto') this.transitioning = 0 this.$element.trigger('shown.bs.collapse') } if (!$.support.transition) return complete.call(this) var scrollSize = $.camelCase(['scroll', dimension].join('-')) this.$element .one($.support.transition.end, $.proxy(complete, this)) .emulateTransitionEnd(350) [dimension](this.$element[0][scrollSize]) } Collapse.prototype.hide = function () { if (this.transitioning || !this.$element.hasClass('in')) return var startEvent = $.Event('hide.bs.collapse') this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) return var dimension = this.dimension() this.$element [dimension](this.$element[dimension]()) [0].offsetHeight this.$element .addClass('collapsing') .removeClass('collapse') .removeClass('in') this.transitioning = 1 var complete = function () { this.transitioning = 0 this.$element .trigger('hidden.bs.collapse') .removeClass('collapsing') .addClass('collapse') } if (!$.support.transition) return complete.call(this) this.$element [dimension](0) .one($.support.transition.end, $.proxy(complete, this)) .emulateTransitionEnd(350) } Collapse.prototype.toggle = function () { this[this.$element.hasClass('in') ? 'hide' : 'show']() } // COLLAPSE PLUGIN DEFINITION // ========================== var old = $.fn.collapse $.fn.collapse = function (option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.collapse') var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) if (typeof option == 'string') data[option]() }) } $.fn.collapse.Constructor = Collapse // COLLAPSE NO CONFLICT // ==================== $.fn.collapse.noConflict = function () { $.fn.collapse = old return this } // COLLAPSE DATA-API // ================= $(document).on('click.bs.collapse.data-api', '[data-toggle=collapse]', function (e) { var $this = $(this), href var target = $this.attr('data-target') || e.preventDefault() || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 var $target = $(target) var data = $target.data('bs.collapse') var option = data ? 'toggle' : $this.data() var parent = $this.attr('data-parent') var $parent = parent && $(parent) if (!data || !data.transitioning) { if ($parent) $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass('collapsed') $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed') } $target.collapse(option) }) }(window.jQuery); /* ======================================================================== * Bootstrap: dropdown.js v3.0.0 * http://twbs.github.com/bootstrap/javascript.html#dropdowns * ======================================================================== * Copyright 2012 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ======================================================================== */ +function ($) { "use strict"; // DROPDOWN CLASS DEFINITION // ========================= var backdrop = '.dropdown-backdrop' var toggle = '[data-toggle=dropdown]' var Dropdown = function (element) { var $el = $(element).on('click.bs.dropdown', this.toggle) } Dropdown.prototype.toggle = function (e) { var $this = $(this) if ($this.is('.disabled, :disabled')) return var $parent = getParent($this) var isActive = $parent.hasClass('open') clearMenus() if (!isActive) { if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { // if mobile we we use a backdrop because click events don't delegate $('